2 * Copyright (c) 2020 Cisco and/or its affiliates.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
16 #ifndef __CNAT_NODE_H__
17 #define __CNAT_NODE_H__
19 #include <vlibmemory/api.h>
20 #include <cnat/cnat_session.h>
21 #include <cnat/cnat_client.h>
23 typedef uword (*cnat_node_sub_t) (vlib_main_t * vm,
24 vlib_node_runtime_t * node,
26 cnat_node_ctx_t * ctx, int rv,
27 cnat_session_t * session);
30 * Inline translation functions
33 static_always_inline u8
34 has_ip6_address (ip6_address_t * a)
36 return ((0 != a->as_u64[0]) || (0 != a->as_u64[1]));
39 static_always_inline void
40 cnat_ip4_translate_l4 (ip4_header_t * ip4, udp_header_t * udp,
42 ip4_address_t new_addr[VLIB_N_DIR],
43 u16 new_port[VLIB_N_DIR])
45 u16 old_port[VLIB_N_DIR];
46 ip4_address_t old_addr[VLIB_N_DIR];
49 old_port[VLIB_TX] = udp->dst_port;
50 old_port[VLIB_RX] = udp->src_port;
51 old_addr[VLIB_TX] = ip4->dst_address;
52 old_addr[VLIB_RX] = ip4->src_address;
55 if (new_addr[VLIB_TX].as_u32)
57 ip_csum_update (sum, old_addr[VLIB_TX].as_u32, new_addr[VLIB_TX].as_u32,
58 ip4_header_t, dst_address);
59 if (new_port[VLIB_TX])
61 udp->dst_port = new_port[VLIB_TX];
62 sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
63 ip4_header_t /* cheat */ ,
64 length /* changed member */ );
66 if (new_addr[VLIB_RX].as_u32)
68 ip_csum_update (sum, old_addr[VLIB_RX].as_u32, new_addr[VLIB_RX].as_u32,
69 ip4_header_t, src_address);
71 if (new_port[VLIB_RX])
73 udp->src_port = new_port[VLIB_RX];
74 sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
75 ip4_header_t /* cheat */ ,
76 length /* changed member */ );
78 *checksum = ip_csum_fold (sum);
81 static_always_inline void
82 cnat_ip4_translate_l3 (ip4_header_t * ip4, ip4_address_t new_addr[VLIB_N_DIR])
84 ip4_address_t old_addr[VLIB_N_DIR];
87 old_addr[VLIB_TX] = ip4->dst_address;
88 old_addr[VLIB_RX] = ip4->src_address;
91 if (new_addr[VLIB_TX].as_u32)
93 ip4->dst_address = new_addr[VLIB_TX];
95 ip_csum_update (sum, old_addr[VLIB_TX].as_u32,
96 new_addr[VLIB_TX].as_u32, ip4_header_t, dst_address);
98 if (new_addr[VLIB_RX].as_u32)
100 ip4->src_address = new_addr[VLIB_RX];
102 ip_csum_update (sum, old_addr[VLIB_RX].as_u32,
103 new_addr[VLIB_RX].as_u32, ip4_header_t, src_address);
105 ip4->checksum = ip_csum_fold (sum);
108 static_always_inline void
109 cnat_tcp_update_session_lifetime (tcp_header_t * tcp, u32 index)
111 cnat_main_t *cm = &cnat_main;
112 if (PREDICT_FALSE (tcp_fin (tcp)))
114 cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
117 if (PREDICT_FALSE (tcp_rst (tcp)))
119 cnat_timestamp_set_lifetime (index, CNAT_DEFAULT_TCP_RST_TIMEOUT);
122 if (PREDICT_FALSE (tcp_syn (tcp) && tcp_ack (tcp)))
124 cnat_timestamp_set_lifetime (index, cm->tcp_max_age);
128 static_always_inline void
129 cnat_translation_ip4 (const cnat_session_t * session,
130 ip4_header_t * ip4, udp_header_t * udp)
132 tcp_header_t *tcp = (tcp_header_t *) udp;
133 ip4_address_t new_addr[VLIB_N_DIR];
134 u16 new_port[VLIB_N_DIR];
136 new_addr[VLIB_TX] = session->value.cs_ip[VLIB_TX].ip4;
137 new_addr[VLIB_RX] = session->value.cs_ip[VLIB_RX].ip4;
138 new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
139 new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
141 if (ip4->protocol == IP_PROTOCOL_TCP)
143 if (PREDICT_FALSE (tcp->checksum))
144 cnat_ip4_translate_l4 (ip4, udp, &tcp->checksum, new_addr, new_port);
147 udp->dst_port = new_port[VLIB_TX];
148 udp->src_port = new_port[VLIB_RX];
150 cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
152 else if (ip4->protocol == IP_PROTOCOL_UDP)
154 if (PREDICT_FALSE (udp->checksum))
155 cnat_ip4_translate_l4 (ip4, udp, &udp->checksum, new_addr, new_port);
158 udp->dst_port = new_port[VLIB_TX];
159 udp->src_port = new_port[VLIB_RX];
163 cnat_ip4_translate_l3 (ip4, new_addr);
166 static_always_inline void
167 cnat_ip6_translate_l3 (ip6_header_t * ip6, ip6_address_t new_addr[VLIB_N_DIR])
169 if (has_ip6_address (&new_addr[VLIB_TX]))
170 ip6_address_copy (&ip6->dst_address, &new_addr[VLIB_TX]);
171 if (has_ip6_address (&new_addr[VLIB_RX]))
172 ip6_address_copy (&ip6->src_address, &new_addr[VLIB_RX]);
175 static_always_inline void
176 cnat_ip6_translate_l4 (ip6_header_t * ip6, udp_header_t * udp,
178 ip6_address_t new_addr[VLIB_N_DIR],
179 u16 new_port[VLIB_N_DIR])
181 u16 old_port[VLIB_N_DIR];
182 ip6_address_t old_addr[VLIB_N_DIR];
185 old_port[VLIB_TX] = udp->dst_port;
186 old_port[VLIB_RX] = udp->src_port;
187 ip6_address_copy (&old_addr[VLIB_TX], &ip6->dst_address);
188 ip6_address_copy (&old_addr[VLIB_RX], &ip6->src_address);
191 if (has_ip6_address (&new_addr[VLIB_TX]))
193 sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[0]);
194 sum = ip_csum_add_even (sum, new_addr[VLIB_TX].as_u64[1]);
195 sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[0]);
196 sum = ip_csum_sub_even (sum, old_addr[VLIB_TX].as_u64[1]);
199 if (new_port[VLIB_TX])
201 udp->dst_port = new_port[VLIB_TX];
202 sum = ip_csum_update (sum, old_port[VLIB_TX], new_port[VLIB_TX],
203 ip4_header_t /* cheat */ ,
204 length /* changed member */ );
206 if (has_ip6_address (&new_addr[VLIB_RX]))
208 sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[0]);
209 sum = ip_csum_add_even (sum, new_addr[VLIB_RX].as_u64[1]);
210 sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[0]);
211 sum = ip_csum_sub_even (sum, old_addr[VLIB_RX].as_u64[1]);
214 if (new_port[VLIB_RX])
216 udp->src_port = new_port[VLIB_RX];
217 sum = ip_csum_update (sum, old_port[VLIB_RX], new_port[VLIB_RX],
218 ip4_header_t /* cheat */ ,
219 length /* changed member */ );
221 *checksum = ip_csum_fold (sum);
224 static_always_inline void
225 cnat_translation_ip6 (const cnat_session_t * session,
226 ip6_header_t * ip6, udp_header_t * udp)
228 tcp_header_t *tcp = (tcp_header_t *) udp;
229 ip6_address_t new_addr[VLIB_N_DIR];
230 u16 new_port[VLIB_N_DIR];
232 ip6_address_copy (&new_addr[VLIB_TX], &session->value.cs_ip[VLIB_TX].ip6);
233 ip6_address_copy (&new_addr[VLIB_RX], &session->value.cs_ip[VLIB_RX].ip6);
234 new_port[VLIB_TX] = session->value.cs_port[VLIB_TX];
235 new_port[VLIB_RX] = session->value.cs_port[VLIB_RX];
237 if (ip6->protocol == IP_PROTOCOL_TCP)
239 if (PREDICT_FALSE (tcp->checksum))
240 cnat_ip6_translate_l4 (ip6, udp, &tcp->checksum, new_addr, new_port);
243 udp->dst_port = new_port[VLIB_TX];
244 udp->src_port = new_port[VLIB_RX];
246 cnat_tcp_update_session_lifetime (tcp, session->value.cs_ts_index);
248 else if (ip6->protocol == IP_PROTOCOL_UDP)
250 if (PREDICT_FALSE (udp->checksum))
251 cnat_ip6_translate_l4 (ip6, udp, &udp->checksum, new_addr, new_port);
254 udp->dst_port = new_port[VLIB_TX];
255 udp->src_port = new_port[VLIB_RX];
259 cnat_ip6_translate_l3 (ip6, new_addr);
262 static_always_inline void
263 cnat_session_make_key (vlib_buffer_t * b, ip_address_family_t af,
264 clib_bihash_kv_40_48_t * bkey)
267 cnat_session_t *session = (cnat_session_t *) bkey;
271 ip4 = vlib_buffer_get_current (b);
272 udp = (udp_header_t *) (ip4 + 1);
273 session->key.cs_af = AF_IP4;
274 session->key.__cs_pad[0] = 0;
275 session->key.__cs_pad[1] = 0;
277 ip46_address_set_ip4 (&session->key.cs_ip[VLIB_TX], &ip4->dst_address);
278 ip46_address_set_ip4 (&session->key.cs_ip[VLIB_RX], &ip4->src_address);
279 session->key.cs_port[VLIB_RX] = udp->src_port;
280 session->key.cs_port[VLIB_TX] = udp->dst_port;
281 session->key.cs_proto = ip4->protocol;
286 ip6 = vlib_buffer_get_current (b);
287 udp = (udp_header_t *) (ip6 + 1);
288 session->key.cs_af = AF_IP6;
289 session->key.__cs_pad[0] = 0;
290 session->key.__cs_pad[1] = 0;
292 ip46_address_set_ip6 (&session->key.cs_ip[VLIB_TX], &ip6->dst_address);
293 ip46_address_set_ip6 (&session->key.cs_ip[VLIB_RX], &ip6->src_address);
294 session->key.cs_port[VLIB_RX] = udp->src_port;
295 session->key.cs_port[VLIB_TX] = udp->dst_port;
296 session->key.cs_proto = ip6->protocol;
301 * Create NAT sessions
304 static_always_inline void
305 cnat_session_create (cnat_session_t * session, cnat_node_ctx_t * ctx,
309 clib_bihash_kv_40_48_t rkey;
310 cnat_session_t *rsession = (cnat_session_t *) & rkey;
311 clib_bihash_kv_40_48_t *bkey = (clib_bihash_kv_40_48_t *) session;
312 clib_bihash_kv_40_48_t rvalue;
315 /* create the reverse flow key */
316 ip46_address_copy (&rsession->key.cs_ip[VLIB_RX],
317 &session->value.cs_ip[VLIB_TX]);
318 ip46_address_copy (&rsession->key.cs_ip[VLIB_TX],
319 &session->value.cs_ip[VLIB_RX]);
320 rsession->key.cs_proto = session->key.cs_proto;
321 rsession->key.__cs_pad[0] = 0;
322 rsession->key.__cs_pad[1] = 0;
323 rsession->key.cs_af = ctx->af;
324 rsession->key.cs_port[VLIB_RX] = session->value.cs_port[VLIB_TX];
325 rsession->key.cs_port[VLIB_TX] = session->value.cs_port[VLIB_RX];
327 /* First search for existing reverse session */
328 rv = clib_bihash_search_inline_2_40_48 (&cnat_session_db, &rkey, &rvalue);
331 /* Reverse session already exists
332 corresponding client should also exist
333 we only need to refcnt the timestamp */
334 cnat_session_t *found_rsession = (cnat_session_t *) & rvalue;
335 session->value.cs_ts_index = found_rsession->value.cs_ts_index;
336 cnat_timestamp_inc_refcnt (session->value.cs_ts_index);
337 clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 1 /* is_add */ );
338 goto create_rsession;
341 session->value.cs_ts_index = cnat_timestamp_new (ctx->now);
342 clib_bihash_add_del_40_48 (&cnat_session_db, bkey, 1);
344 /* is this the first time we've seen this source address */
345 cc = (AF_IP4 == ctx->af ?
346 cnat_client_ip4_find (&session->value.cs_ip[VLIB_RX].ip4) :
347 cnat_client_ip6_find (&session->value.cs_ip[VLIB_RX].ip6));
352 if (AF_IP4 == ctx->af)
353 r0 = (u64) session->value.cs_ip[VLIB_RX].ip4.as_u32;
356 r0 = r0 * 31 + session->value.cs_ip[VLIB_RX].ip6.as_u64[0];
357 r0 = r0 * 31 + session->value.cs_ip[VLIB_RX].ip6.as_u64[1];
361 if (!throttle_check (&cnat_throttle, ctx->thread_index, r0, ctx->seed))
364 l.addr.version = ctx->af;
365 ip46_address_copy (&l.addr.ip, &session->value.cs_ip[VLIB_RX]);
366 /* fire client create to the main thread */
367 vl_api_rpc_call_main_thread (cnat_client_learn,
368 (u8 *) & l, sizeof (l));
372 /* Will still need to count those for session refcnt */
374 clib_spinlock_lock (&cnat_client_db.throttle_pool_lock
375 [ctx->thread_index]);
376 pool_get (cnat_client_db.throttle_pool[ctx->thread_index], addr);
377 addr->version = ctx->af;
378 ip46_address_copy (&addr->ip, &session->value.cs_ip[VLIB_RX]);
379 clib_spinlock_unlock (&cnat_client_db.throttle_pool_lock
380 [ctx->thread_index]);
385 cnat_client_cnt_session (cc);
389 /* add the reverse flow */
390 ip46_address_copy (&rsession->value.cs_ip[VLIB_RX],
391 &session->key.cs_ip[VLIB_TX]);
392 ip46_address_copy (&rsession->value.cs_ip[VLIB_TX],
393 &session->key.cs_ip[VLIB_RX]);
394 rsession->value.cs_ts_index = session->value.cs_ts_index;
395 rsession->value.cs_lbi = INDEX_INVALID;
396 rsession->value.flags = rsession_flags;
397 rsession->value.cs_port[VLIB_TX] = session->key.cs_port[VLIB_RX];
398 rsession->value.cs_port[VLIB_RX] = session->key.cs_port[VLIB_TX];
400 clib_bihash_add_del_40_48 (&cnat_session_db, &rkey, 1);
404 cnat_node_inline (vlib_main_t * vm,
405 vlib_node_runtime_t * node,
406 vlib_frame_t * frame,
407 cnat_node_sub_t cnat_sub,
408 ip_address_family_t af, u8 do_trace)
410 u32 n_left, *from, thread_index;
411 vlib_buffer_t *bufs[VLIB_FRAME_SIZE];
412 vlib_buffer_t **b = bufs;
413 u16 nexts[VLIB_FRAME_SIZE], *next;
417 thread_index = vm->thread_index;
418 from = vlib_frame_vector_args (frame);
419 n_left = frame->n_vectors;
421 vlib_get_buffers (vm, from, bufs, n_left);
422 now = vlib_time_now (vm);
423 seed = throttle_seed (&cnat_throttle, thread_index, vlib_time_now (vm));
424 cnat_session_t *session[4];
425 clib_bihash_kv_40_48_t bkey[4], bvalue[4];
429 cnat_node_ctx_t ctx = { now, seed, thread_index, af, do_trace };
433 /* Kickstart our state */
434 cnat_session_make_key (b[3], af, &bkey[3]);
435 cnat_session_make_key (b[2], af, &bkey[2]);
436 cnat_session_make_key (b[1], af, &bkey[1]);
437 cnat_session_make_key (b[0], af, &bkey[0]);
439 hash[3] = clib_bihash_hash_40_48 (&bkey[3]);
440 hash[2] = clib_bihash_hash_40_48 (&bkey[2]);
441 hash[1] = clib_bihash_hash_40_48 (&bkey[1]);
442 hash[0] = clib_bihash_hash_40_48 (&bkey[0]);
449 vlib_prefetch_buffer_header (b[11], LOAD);
450 vlib_prefetch_buffer_header (b[10], LOAD);
451 vlib_prefetch_buffer_header (b[9], LOAD);
452 vlib_prefetch_buffer_header (b[8], LOAD);
456 clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
459 session[3] = (cnat_session_t *) (rv[3] ? &bkey[3] : &bvalue[3]);
460 next[3] = cnat_sub (vm, node, b[3], &ctx, rv[3], session[3]);
463 clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
466 session[2] = (cnat_session_t *) (rv[2] ? &bkey[2] : &bvalue[2]);
467 next[2] = cnat_sub (vm, node, b[2], &ctx, rv[2], session[2]);
470 clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
473 session[1] = (cnat_session_t *) (rv[1] ? &bkey[1] : &bvalue[1]);
474 next[1] = cnat_sub (vm, node, b[1], &ctx, rv[1], session[1]);
477 clib_bihash_search_inline_2_with_hash_40_48 (&cnat_session_db,
480 session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
481 next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
483 cnat_session_make_key (b[7], af, &bkey[3]);
484 cnat_session_make_key (b[6], af, &bkey[2]);
485 cnat_session_make_key (b[5], af, &bkey[1]);
486 cnat_session_make_key (b[4], af, &bkey[0]);
488 hash[3] = clib_bihash_hash_40_48 (&bkey[3]);
489 hash[2] = clib_bihash_hash_40_48 (&bkey[2]);
490 hash[1] = clib_bihash_hash_40_48 (&bkey[1]);
491 hash[0] = clib_bihash_hash_40_48 (&bkey[0]);
493 clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[3]);
494 clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[2]);
495 clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[1]);
496 clib_bihash_prefetch_bucket_40_48 (&cnat_session_db, hash[0]);
498 clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[3]);
499 clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[2]);
500 clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[1]);
501 clib_bihash_prefetch_data_40_48 (&cnat_session_db, hash[0]);
510 cnat_session_make_key (b[0], af, &bkey[0]);
511 rv[0] = clib_bihash_search_inline_2_40_48 (&cnat_session_db,
512 &bkey[0], &bvalue[0]);
514 session[0] = (cnat_session_t *) (rv[0] ? &bkey[0] : &bvalue[0]);
515 next[0] = cnat_sub (vm, node, b[0], &ctx, rv[0], session[0]);
522 vlib_buffer_enqueue_to_next (vm, node, from, nexts, frame->n_vectors);
524 return frame->n_vectors;
528 * fd.io coding-style-patch-verification: ON
531 * eval: (c-set-style "gnu")