2 * Copyright (c) 2016 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 #include <vlib/vlib.h>
17 #include <vnet/vnet.h>
18 #include <vnet/pg/pg.h>
19 #include <vnet/handoff.h>
21 #include <vnet/ip/ip.h>
22 #include <vnet/ethernet/ethernet.h>
23 #include <vnet/fib/ip4_fib.h>
24 #include <snat/snat.h>
25 #include <snat/snat_ipfix_logging.h>
26 #include <snat/snat_det.h>
28 #include <vppinfra/hash.h>
29 #include <vppinfra/error.h>
30 #include <vppinfra/elog.h>
37 } snat_in2out_trace_t;
40 u32 next_worker_index;
42 } snat_in2out_worker_handoff_trace_t;
44 /* packet trace format function */
45 static u8 * format_snat_in2out_trace (u8 * s, va_list * args)
47 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
48 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
49 snat_in2out_trace_t * t = va_arg (*args, snat_in2out_trace_t *);
52 tag = t->is_slow_path ? "SNAT_IN2OUT_SLOW_PATH" : "SNAT_IN2OUT_FAST_PATH";
54 s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag,
55 t->sw_if_index, t->next_index, t->session_index);
60 static u8 * format_snat_in2out_fast_trace (u8 * s, va_list * args)
62 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
63 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
64 snat_in2out_trace_t * t = va_arg (*args, snat_in2out_trace_t *);
66 s = format (s, "SANT_IN2OUT_FAST: sw_if_index %d, next index %d",
67 t->sw_if_index, t->next_index);
72 static u8 * format_snat_in2out_worker_handoff_trace (u8 * s, va_list * args)
74 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
75 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
76 snat_in2out_worker_handoff_trace_t * t =
77 va_arg (*args, snat_in2out_worker_handoff_trace_t *);
80 m = t->do_handoff ? "next worker" : "same worker";
81 s = format (s, "SNAT_IN2OUT_WORKER_HANDOFF: %s %d", m, t->next_worker_index);
86 vlib_node_registration_t snat_in2out_node;
87 vlib_node_registration_t snat_in2out_slowpath_node;
88 vlib_node_registration_t snat_in2out_fast_node;
89 vlib_node_registration_t snat_in2out_worker_handoff_node;
90 vlib_node_registration_t snat_det_in2out_node;
92 #define foreach_snat_in2out_error \
93 _(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \
94 _(IN2OUT_PACKETS, "Good in2out packets processed") \
95 _(OUT_OF_PORTS, "Out of ports") \
96 _(BAD_OUTSIDE_FIB, "Outside VRF ID not found") \
97 _(BAD_ICMP_TYPE, "icmp type not echo-request") \
98 _(NO_TRANSLATION, "No translation")
101 #define _(sym,str) SNAT_IN2OUT_ERROR_##sym,
102 foreach_snat_in2out_error
105 } snat_in2out_error_t;
107 static char * snat_in2out_error_strings[] = {
108 #define _(sym,string) string,
109 foreach_snat_in2out_error
114 SNAT_IN2OUT_NEXT_LOOKUP,
115 SNAT_IN2OUT_NEXT_DROP,
116 SNAT_IN2OUT_NEXT_SLOW_PATH,
117 SNAT_IN2OUT_NEXT_ICMP_ERROR,
119 } snat_in2out_next_t;
122 * @brief Check if packet should be translated
124 * Packets aimed at outside interface and external addresss with active session
125 * should be translated.
127 * @param sm SNAT main
128 * @param rt SNAT runtime data
129 * @param sw_if_index0 index of the inside interface
130 * @param ip0 IPv4 header
131 * @param proto0 SNAT protocol
132 * @param rx_fib_index0 RX FIB index
134 * @returns 0 if packet should be translated otherwise 1
137 snat_not_translate (snat_main_t * sm, snat_runtime_t * rt, u32 sw_if_index0,
138 ip4_header_t * ip0, u32 proto0, u32 rx_fib_index0)
140 ip4_address_t * first_int_addr;
141 udp_header_t * udp0 = ip4_next_header (ip0);
142 snat_session_key_t key0, sm0;
143 clib_bihash_kv_8_8_t kv0, value0;
144 fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
146 .fp_proto = FIB_PROTOCOL_IP4,
149 .ip4.as_u32 = ip0->dst_address.as_u32,
153 if (PREDICT_FALSE(rt->cached_sw_if_index != sw_if_index0))
156 ip4_interface_first_address (sm->ip4_main, sw_if_index0,
157 0 /* just want the address */);
158 rt->cached_sw_if_index = sw_if_index0;
160 rt->cached_ip4_address = first_int_addr->as_u32;
162 rt->cached_ip4_address = 0;
165 /* Don't NAT packet aimed at the intfc address */
166 if (PREDICT_FALSE(ip0->dst_address.as_u32 == rt->cached_ip4_address))
169 key0.addr = ip0->dst_address;
170 key0.port = udp0->dst_port;
171 key0.protocol = proto0;
172 key0.fib_index = sm->outside_fib_index;
173 kv0.key = key0.as_u64;
175 /* NAT packet aimed at external address if */
176 /* has active sessions */
177 if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
179 /* or is static mappings */
180 if (!snat_static_mapping_match(sm, key0, &sm0, 1))
186 fei = fib_table_lookup (rx_fib_index0, &pfx);
187 if (FIB_NODE_INDEX_INVALID != fei)
189 u32 sw_if_index = fib_entry_get_resolving_interface (fei);
190 if (sw_if_index == ~0)
192 fei = fib_table_lookup (sm->outside_fib_index, &pfx);
193 if (FIB_NODE_INDEX_INVALID != fei)
194 sw_if_index = fib_entry_get_resolving_interface (fei);
197 pool_foreach (i, sm->interfaces,
199 /* NAT packet aimed at outside interface */
200 if ((i->is_inside == 0) && (sw_if_index == i->sw_if_index))
208 static u32 slow_path (snat_main_t *sm, vlib_buffer_t *b0,
211 snat_session_key_t * key0,
212 snat_session_t ** sessionp,
213 vlib_node_runtime_t * node,
218 snat_user_key_t user_key;
220 clib_bihash_kv_8_8_t kv0, value0;
221 u32 oldest_per_user_translation_list_index;
222 dlist_elt_t * oldest_per_user_translation_list_elt;
223 dlist_elt_t * per_user_translation_list_elt;
224 dlist_elt_t * per_user_list_head_elt;
226 snat_session_key_t key1;
227 u32 address_index = ~0;
228 u32 outside_fib_index;
230 snat_worker_key_t worker_by_out_key;
232 p = hash_get (sm->ip4_main->fib_index_by_table_id, sm->outside_vrf_id);
235 b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_OUTSIDE_FIB];
236 return SNAT_IN2OUT_NEXT_DROP;
238 outside_fib_index = p[0];
240 key1.protocol = key0->protocol;
241 user_key.addr = ip0->src_address;
242 user_key.fib_index = rx_fib_index0;
243 kv0.key = user_key.as_u64;
245 /* Ever heard of the "user" = src ip4 address before? */
246 if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0))
248 /* no, make a new one */
249 pool_get (sm->per_thread_data[cpu_index].users, u);
250 memset (u, 0, sizeof (*u));
251 u->addr = ip0->src_address;
252 u->fib_index = rx_fib_index0;
254 pool_get (sm->per_thread_data[cpu_index].list_pool, per_user_list_head_elt);
256 u->sessions_per_user_list_head_index = per_user_list_head_elt -
257 sm->per_thread_data[cpu_index].list_pool;
259 clib_dlist_init (sm->per_thread_data[cpu_index].list_pool,
260 u->sessions_per_user_list_head_index);
262 kv0.value = u - sm->per_thread_data[cpu_index].users;
265 clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */);
269 u = pool_elt_at_index (sm->per_thread_data[cpu_index].users,
273 /* Over quota? Recycle the least recently used dynamic translation */
274 if (u->nsessions >= sm->max_translations_per_user)
276 /* Remove the oldest dynamic translation */
278 oldest_per_user_translation_list_index =
279 clib_dlist_remove_head (sm->per_thread_data[cpu_index].list_pool,
280 u->sessions_per_user_list_head_index);
282 ASSERT (oldest_per_user_translation_list_index != ~0);
284 /* add it back to the end of the LRU list */
285 clib_dlist_addtail (sm->per_thread_data[cpu_index].list_pool,
286 u->sessions_per_user_list_head_index,
287 oldest_per_user_translation_list_index);
288 /* Get the list element */
289 oldest_per_user_translation_list_elt =
290 pool_elt_at_index (sm->per_thread_data[cpu_index].list_pool,
291 oldest_per_user_translation_list_index);
293 /* Get the session index from the list element */
294 session_index = oldest_per_user_translation_list_elt->value;
296 /* Get the session */
297 s = pool_elt_at_index (sm->per_thread_data[cpu_index].sessions,
299 } while (snat_is_session_static (s));
301 /* Remove in2out, out2in keys */
302 kv0.key = s->in2out.as_u64;
303 if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 0 /* is_add */))
304 clib_warning ("in2out key delete failed");
305 kv0.key = s->out2in.as_u64;
306 if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 0 /* is_add */))
307 clib_warning ("out2in key delete failed");
310 snat_ipfix_logging_nat44_ses_delete(s->in2out.addr.as_u32,
311 s->out2in.addr.as_u32,
315 s->in2out.fib_index);
317 snat_free_outside_address_and_port
318 (sm, &s->out2in, s->outside_address_index);
319 s->outside_address_index = ~0;
321 if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, &key1,
326 b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
327 return SNAT_IN2OUT_NEXT_DROP;
329 s->outside_address_index = address_index;
333 u8 static_mapping = 1;
335 /* First try to match static mapping by local address and port */
336 if (snat_static_mapping_match (sm, *key0, &key1, 0))
339 /* Try to create dynamic translation */
340 if (snat_alloc_outside_address_and_port (sm, rx_fib_index0, &key1,
343 b0->error = node->errors[SNAT_IN2OUT_ERROR_OUT_OF_PORTS];
344 return SNAT_IN2OUT_NEXT_DROP;
348 /* Create a new session */
349 pool_get (sm->per_thread_data[cpu_index].sessions, s);
350 memset (s, 0, sizeof (*s));
352 s->outside_address_index = address_index;
356 u->nstaticsessions++;
357 s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
364 /* Create list elts */
365 pool_get (sm->per_thread_data[cpu_index].list_pool,
366 per_user_translation_list_elt);
367 clib_dlist_init (sm->per_thread_data[cpu_index].list_pool,
368 per_user_translation_list_elt -
369 sm->per_thread_data[cpu_index].list_pool);
371 per_user_translation_list_elt->value =
372 s - sm->per_thread_data[cpu_index].sessions;
373 s->per_user_index = per_user_translation_list_elt -
374 sm->per_thread_data[cpu_index].list_pool;
375 s->per_user_list_head_index = u->sessions_per_user_list_head_index;
377 clib_dlist_addtail (sm->per_thread_data[cpu_index].list_pool,
378 s->per_user_list_head_index,
379 per_user_translation_list_elt -
380 sm->per_thread_data[cpu_index].list_pool);
385 s->out2in.protocol = key0->protocol;
386 s->out2in.fib_index = outside_fib_index;
389 /* Add to translation hashes */
390 kv0.key = s->in2out.as_u64;
391 kv0.value = s - sm->per_thread_data[cpu_index].sessions;
392 if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */))
393 clib_warning ("in2out key add failed");
395 kv0.key = s->out2in.as_u64;
396 kv0.value = s - sm->per_thread_data[cpu_index].sessions;
398 if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */))
399 clib_warning ("out2in key add failed");
401 /* Add to translated packets worker lookup */
402 worker_by_out_key.addr = s->out2in.addr;
403 worker_by_out_key.port = s->out2in.port;
404 worker_by_out_key.fib_index = s->out2in.fib_index;
405 kv0.key = worker_by_out_key.as_u64;
406 kv0.value = cpu_index;
407 clib_bihash_add_del_8_8 (&sm->worker_by_out, &kv0, 1);
410 snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32,
411 s->out2in.addr.as_u32,
415 s->in2out.fib_index);
420 u16 src_port, dst_port;
423 static inline u32 icmp_in2out_slow_path (snat_main_t *sm,
426 icmp46_header_t * icmp0,
429 vlib_node_runtime_t * node,
433 snat_session_t ** p_s0)
435 snat_session_key_t key0;
436 icmp_echo_header_t *echo0, *inner_echo0 = 0;
437 ip4_header_t *inner_ip0 = 0;
439 icmp46_header_t *inner_icmp0;
440 clib_bihash_kv_8_8_t kv0, value0;
441 snat_session_t * s0 = 0;
442 u32 new_addr0, old_addr0;
443 u16 old_id0, new_id0;
446 snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
447 u8 is_error_message = 0;
449 echo0 = (icmp_echo_header_t *)(icmp0+1);
451 key0.addr = ip0->src_address;
452 key0.fib_index = rx_fib_index0;
456 case ICMP4_destination_unreachable:
457 case ICMP4_time_exceeded:
458 case ICMP4_parameter_problem:
459 case ICMP4_source_quench:
461 case ICMP4_alternate_host_address:
462 is_error_message = 1;
465 if (!is_error_message)
467 if (PREDICT_FALSE(icmp0->type != ICMP4_echo_request))
469 b0->error = node->errors[SNAT_IN2OUT_ERROR_BAD_ICMP_TYPE];
470 next0 = SNAT_IN2OUT_NEXT_DROP;
473 key0.protocol = SNAT_PROTOCOL_ICMP;
474 key0.port = echo0->identifier;
478 inner_ip0 = (ip4_header_t *)(echo0+1);
479 l4_header = ip4_next_header (inner_ip0);
480 key0.protocol = ip_proto_to_snat_proto (inner_ip0->protocol);
481 switch (key0.protocol)
483 case SNAT_PROTOCOL_ICMP:
484 inner_icmp0 = (icmp46_header_t*)l4_header;
485 inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1);
486 key0.port = inner_echo0->identifier;
488 case SNAT_PROTOCOL_UDP:
489 case SNAT_PROTOCOL_TCP:
490 key0.port = ((tcp_udp_header_t*)l4_header)->dst_port;
493 b0->error = node->errors[SNAT_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
494 next0 = SNAT_IN2OUT_NEXT_DROP;
499 kv0.key = key0.as_u64;
501 if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0))
503 if (PREDICT_FALSE(snat_not_translate(sm, rt, sw_if_index0, ip0,
504 IP_PROTOCOL_ICMP, rx_fib_index0)))
507 if (is_error_message)
509 next0 = SNAT_IN2OUT_NEXT_DROP;
513 next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
514 &s0, node, next0, cpu_index);
516 if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
520 s0 = pool_elt_at_index (sm->per_thread_data[cpu_index].sessions,
523 sum0 = ip_incremental_checksum (0, icmp0,
524 ntohs(ip0->length) - ip4_header_bytes (ip0));
525 checksum0 = ~ip_csum_fold (sum0);
526 if (PREDICT_FALSE(checksum0 != 0 && checksum0 != 0xffff))
528 next0 = SNAT_IN2OUT_NEXT_DROP;
532 old_addr0 = ip0->src_address.as_u32;
533 ip0->src_address = s0->out2in.addr;
534 new_addr0 = ip0->src_address.as_u32;
535 vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
537 sum0 = ip0->checksum;
538 sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
539 src_address /* changed member */);
540 ip0->checksum = ip_csum_fold (sum0);
542 if (!is_error_message)
544 old_id0 = echo0->identifier;
545 new_id0 = s0->out2in.port;
546 echo0->identifier = new_id0;
548 sum0 = icmp0->checksum;
549 sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
551 icmp0->checksum = ip_csum_fold (sum0);
555 if (!ip4_header_checksum_is_valid (inner_ip0))
557 next0 = SNAT_IN2OUT_NEXT_DROP;
561 old_addr0 = inner_ip0->dst_address.as_u32;
562 inner_ip0->dst_address = s0->out2in.addr;
563 new_addr0 = inner_ip0->src_address.as_u32;
565 sum0 = icmp0->checksum;
566 sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
567 dst_address /* changed member */);
568 icmp0->checksum = ip_csum_fold (sum0);
570 switch (key0.protocol)
572 case SNAT_PROTOCOL_ICMP:
573 old_id0 = inner_echo0->identifier;
574 new_id0 = s0->out2in.port;
575 inner_echo0->identifier = new_id0;
577 sum0 = icmp0->checksum;
578 sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
580 icmp0->checksum = ip_csum_fold (sum0);
582 case SNAT_PROTOCOL_UDP:
583 case SNAT_PROTOCOL_TCP:
584 old_id0 = ((tcp_udp_header_t*)l4_header)->dst_port;
585 new_id0 = s0->out2in.port;
586 ((tcp_udp_header_t*)l4_header)->dst_port = new_id0;
588 sum0 = icmp0->checksum;
589 sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t,
591 icmp0->checksum = ip_csum_fold (sum0);
599 s0->last_heard = now;
601 s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
602 /* Per-user LRU list maintenance for dynamic translations */
603 if (!snat_is_session_static (s0))
605 clib_dlist_remove (sm->per_thread_data[cpu_index].list_pool,
607 clib_dlist_addtail (sm->per_thread_data[cpu_index].list_pool,
608 s0->per_user_list_head_index,
620 * Hairpinning allows two endpoints on the internal side of the NAT to
621 * communicate even if they only use each other's external IP addresses
624 * @param sm SNAT main.
625 * @param b0 Vlib buffer.
626 * @param ip0 IP header.
627 * @param udp0 UDP header.
628 * @param tcp0 TCP header.
629 * @param proto0 SNAT protocol.
632 snat_hairpinning (snat_main_t *sm,
639 snat_session_key_t key0, sm0;
640 snat_worker_key_t k0;
642 clib_bihash_kv_8_8_t kv0, value0;
644 u32 new_dst_addr0 = 0, old_dst_addr0, ti = 0, si;
645 u16 new_dst_port0, old_dst_port0;
647 key0.addr = ip0->dst_address;
648 key0.port = udp0->dst_port;
649 key0.protocol = proto0;
650 key0.fib_index = sm->outside_fib_index;
651 kv0.key = key0.as_u64;
653 /* Check if destination is in active sessions */
654 if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
656 /* or static mappings */
657 if (!snat_static_mapping_match(sm, key0, &sm0, 1))
659 new_dst_addr0 = sm0.addr.as_u32;
660 new_dst_port0 = sm0.port;
661 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
667 if (sm->num_workers > 1)
669 k0.addr = ip0->dst_address;
670 k0.port = udp0->dst_port;
671 k0.fib_index = sm->outside_fib_index;
673 if (clib_bihash_search_8_8 (&sm->worker_by_out, &kv0, &value0))
679 ti = sm->num_workers;
681 s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
682 new_dst_addr0 = s0->in2out.addr.as_u32;
683 new_dst_port0 = s0->in2out.port;
684 vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
687 /* Destination is behind the same NAT, use internal address and port */
690 old_dst_addr0 = ip0->dst_address.as_u32;
691 ip0->dst_address.as_u32 = new_dst_addr0;
692 sum0 = ip0->checksum;
693 sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
694 ip4_header_t, dst_address);
695 ip0->checksum = ip_csum_fold (sum0);
697 old_dst_port0 = tcp0->dst;
698 if (PREDICT_TRUE(new_dst_port0 != old_dst_port0))
700 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
702 tcp0->dst = new_dst_port0;
703 sum0 = tcp0->checksum;
704 sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
705 ip4_header_t, dst_address);
706 sum0 = ip_csum_update (sum0, old_dst_port0, new_dst_port0,
707 ip4_header_t /* cheat */, length);
708 tcp0->checksum = ip_csum_fold(sum0);
712 udp0->dst_port = new_dst_port0;
720 snat_in2out_node_fn_inline (vlib_main_t * vm,
721 vlib_node_runtime_t * node,
722 vlib_frame_t * frame, int is_slow_path)
724 u32 n_left_from, * from, * to_next;
725 snat_in2out_next_t next_index;
726 u32 pkts_processed = 0;
727 snat_main_t * sm = &snat_main;
728 snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
729 f64 now = vlib_time_now (vm);
730 u32 stats_node_index;
731 u32 cpu_index = os_get_cpu_number ();
733 stats_node_index = is_slow_path ? snat_in2out_slowpath_node.index :
734 snat_in2out_node.index;
736 from = vlib_frame_vector_args (frame);
737 n_left_from = frame->n_vectors;
738 next_index = node->cached_next_index;
740 while (n_left_from > 0)
744 vlib_get_next_frame (vm, node, next_index,
745 to_next, n_left_to_next);
747 while (n_left_from >= 4 && n_left_to_next >= 2)
750 vlib_buffer_t * b0, * b1;
752 u32 sw_if_index0, sw_if_index1;
753 ip4_header_t * ip0, * ip1;
754 ip_csum_t sum0, sum1;
755 u32 new_addr0, old_addr0, new_addr1, old_addr1;
756 u16 old_port0, new_port0, old_port1, new_port1;
757 udp_header_t * udp0, * udp1;
758 tcp_header_t * tcp0, * tcp1;
759 icmp46_header_t * icmp0, * icmp1;
760 snat_session_key_t key0, key1;
761 u32 rx_fib_index0, rx_fib_index1;
763 snat_session_t * s0 = 0, * s1 = 0;
764 clib_bihash_kv_8_8_t kv0, value0, kv1, value1;
766 /* Prefetch next iteration. */
768 vlib_buffer_t * p2, * p3;
770 p2 = vlib_get_buffer (vm, from[2]);
771 p3 = vlib_get_buffer (vm, from[3]);
773 vlib_prefetch_buffer_header (p2, LOAD);
774 vlib_prefetch_buffer_header (p3, LOAD);
776 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
777 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
780 /* speculatively enqueue b0 and b1 to the current next frame */
781 to_next[0] = bi0 = from[0];
782 to_next[1] = bi1 = from[1];
788 b0 = vlib_get_buffer (vm, bi0);
789 b1 = vlib_get_buffer (vm, bi1);
791 ip0 = vlib_buffer_get_current (b0);
792 udp0 = ip4_next_header (ip0);
793 tcp0 = (tcp_header_t *) udp0;
794 icmp0 = (icmp46_header_t *) udp0;
796 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
797 rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index,
800 next0 = next1 = SNAT_IN2OUT_NEXT_LOOKUP;
802 proto0 = ip_proto_to_snat_proto (ip0->protocol);
804 if (PREDICT_FALSE(ip0->ttl == 1))
806 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
807 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
808 ICMP4_time_exceeded_ttl_exceeded_in_transit,
810 next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR;
814 /* Next configured feature, probably ip4-lookup */
817 if (PREDICT_FALSE (proto0 == ~0))
820 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
822 next0 = icmp_in2out_slow_path
823 (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0,
824 node, next0, now, cpu_index, &s0);
830 if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP))
832 next0 = SNAT_IN2OUT_NEXT_SLOW_PATH;
837 key0.addr = ip0->src_address;
838 key0.port = udp0->src_port;
839 key0.protocol = proto0;
840 key0.fib_index = rx_fib_index0;
842 kv0.key = key0.as_u64;
844 if (PREDICT_FALSE (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0) != 0))
848 if (PREDICT_FALSE(snat_not_translate(sm, rt, sw_if_index0, ip0,
849 proto0, rx_fib_index0)))
852 next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
853 &s0, node, next0, cpu_index);
854 if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
859 next0 = SNAT_IN2OUT_NEXT_SLOW_PATH;
864 s0 = pool_elt_at_index (sm->per_thread_data[cpu_index].sessions,
867 old_addr0 = ip0->src_address.as_u32;
868 ip0->src_address = s0->out2in.addr;
869 new_addr0 = ip0->src_address.as_u32;
870 vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
872 sum0 = ip0->checksum;
873 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
875 src_address /* changed member */);
876 ip0->checksum = ip_csum_fold (sum0);
878 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
880 old_port0 = tcp0->src_port;
881 tcp0->src_port = s0->out2in.port;
882 new_port0 = tcp0->src_port;
884 sum0 = tcp0->checksum;
885 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
887 dst_address /* changed member */);
888 sum0 = ip_csum_update (sum0, old_port0, new_port0,
889 ip4_header_t /* cheat */,
890 length /* changed member */);
891 tcp0->checksum = ip_csum_fold(sum0);
895 old_port0 = udp0->src_port;
896 udp0->src_port = s0->out2in.port;
901 snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
904 s0->last_heard = now;
906 s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
907 /* Per-user LRU list maintenance for dynamic translation */
908 if (!snat_is_session_static (s0))
910 clib_dlist_remove (sm->per_thread_data[cpu_index].list_pool,
912 clib_dlist_addtail (sm->per_thread_data[cpu_index].list_pool,
913 s0->per_user_list_head_index,
918 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
919 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
921 snat_in2out_trace_t *t =
922 vlib_add_trace (vm, node, b0, sizeof (*t));
923 t->is_slow_path = is_slow_path;
924 t->sw_if_index = sw_if_index0;
925 t->next_index = next0;
926 t->session_index = ~0;
928 t->session_index = s0 - sm->per_thread_data[cpu_index].sessions;
931 pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
933 ip1 = vlib_buffer_get_current (b1);
934 udp1 = ip4_next_header (ip1);
935 tcp1 = (tcp_header_t *) udp1;
936 icmp1 = (icmp46_header_t *) udp1;
938 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
939 rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index,
942 proto1 = ip_proto_to_snat_proto (ip1->protocol);
944 if (PREDICT_FALSE(ip0->ttl == 1))
946 vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
947 icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
948 ICMP4_time_exceeded_ttl_exceeded_in_transit,
950 next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR;
954 /* Next configured feature, probably ip4-lookup */
957 if (PREDICT_FALSE (proto1 == ~0))
960 if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
962 next1 = icmp_in2out_slow_path
963 (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node,
964 next1, now, cpu_index, &s1);
970 if (PREDICT_FALSE (proto1 == ~0 || proto1 == SNAT_PROTOCOL_ICMP))
972 next1 = SNAT_IN2OUT_NEXT_SLOW_PATH;
977 key1.addr = ip1->src_address;
978 key1.port = udp1->src_port;
979 key1.protocol = proto1;
980 key1.fib_index = rx_fib_index1;
982 kv1.key = key1.as_u64;
984 if (PREDICT_FALSE(clib_bihash_search_8_8 (&sm->in2out, &kv1, &value1) != 0))
988 if (PREDICT_FALSE(snat_not_translate(sm, rt, sw_if_index1, ip1,
989 proto1, rx_fib_index1)))
992 next1 = slow_path (sm, b1, ip1, rx_fib_index1, &key1,
993 &s1, node, next1, cpu_index);
994 if (PREDICT_FALSE (next1 == SNAT_IN2OUT_NEXT_DROP))
999 next1 = SNAT_IN2OUT_NEXT_SLOW_PATH;
1004 s1 = pool_elt_at_index (sm->per_thread_data[cpu_index].sessions,
1007 old_addr1 = ip1->src_address.as_u32;
1008 ip1->src_address = s1->out2in.addr;
1009 new_addr1 = ip1->src_address.as_u32;
1010 vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->out2in.fib_index;
1012 sum1 = ip1->checksum;
1013 sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
1015 src_address /* changed member */);
1016 ip1->checksum = ip_csum_fold (sum1);
1018 if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
1020 old_port1 = tcp1->src_port;
1021 tcp1->src_port = s1->out2in.port;
1022 new_port1 = tcp1->src_port;
1024 sum1 = tcp1->checksum;
1025 sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
1027 dst_address /* changed member */);
1028 sum1 = ip_csum_update (sum1, old_port1, new_port1,
1029 ip4_header_t /* cheat */,
1030 length /* changed member */);
1031 tcp1->checksum = ip_csum_fold(sum1);
1035 old_port1 = udp1->src_port;
1036 udp1->src_port = s1->out2in.port;
1041 snat_hairpinning (sm, b1, ip1, udp1, tcp1, proto1);
1044 s1->last_heard = now;
1046 s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
1047 /* Per-user LRU list maintenance for dynamic translation */
1048 if (!snat_is_session_static (s1))
1050 clib_dlist_remove (sm->per_thread_data[cpu_index].list_pool,
1051 s1->per_user_index);
1052 clib_dlist_addtail (sm->per_thread_data[cpu_index].list_pool,
1053 s1->per_user_list_head_index,
1054 s1->per_user_index);
1058 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1059 && (b1->flags & VLIB_BUFFER_IS_TRACED)))
1061 snat_in2out_trace_t *t =
1062 vlib_add_trace (vm, node, b1, sizeof (*t));
1063 t->sw_if_index = sw_if_index1;
1064 t->next_index = next1;
1065 t->session_index = ~0;
1067 t->session_index = s1 - sm->per_thread_data[cpu_index].sessions;
1070 pkts_processed += next1 != SNAT_IN2OUT_NEXT_DROP;
1072 /* verify speculative enqueues, maybe switch current next frame */
1073 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1074 to_next, n_left_to_next,
1075 bi0, bi1, next0, next1);
1078 while (n_left_from > 0 && n_left_to_next > 0)
1086 u32 new_addr0, old_addr0;
1087 u16 old_port0, new_port0;
1088 udp_header_t * udp0;
1089 tcp_header_t * tcp0;
1090 icmp46_header_t * icmp0;
1091 snat_session_key_t key0;
1094 snat_session_t * s0 = 0;
1095 clib_bihash_kv_8_8_t kv0, value0;
1097 /* speculatively enqueue b0 to the current next frame */
1103 n_left_to_next -= 1;
1105 b0 = vlib_get_buffer (vm, bi0);
1106 next0 = SNAT_IN2OUT_NEXT_LOOKUP;
1108 ip0 = vlib_buffer_get_current (b0);
1109 udp0 = ip4_next_header (ip0);
1110 tcp0 = (tcp_header_t *) udp0;
1111 icmp0 = (icmp46_header_t *) udp0;
1113 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1114 rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index,
1117 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1119 if (PREDICT_FALSE(ip0->ttl == 1))
1121 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1122 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1123 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1125 next0 = SNAT_IN2OUT_NEXT_ICMP_ERROR;
1129 /* Next configured feature, probably ip4-lookup */
1132 if (PREDICT_FALSE (proto0 == ~0))
1135 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
1137 next0 = icmp_in2out_slow_path
1138 (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
1139 next0, now, cpu_index, &s0);
1145 if (PREDICT_FALSE (proto0 == ~0 || proto0 == SNAT_PROTOCOL_ICMP))
1147 next0 = SNAT_IN2OUT_NEXT_SLOW_PATH;
1152 key0.addr = ip0->src_address;
1153 key0.port = udp0->src_port;
1154 key0.protocol = proto0;
1155 key0.fib_index = rx_fib_index0;
1157 kv0.key = key0.as_u64;
1159 if (clib_bihash_search_8_8 (&sm->in2out, &kv0, &value0))
1163 if (PREDICT_FALSE(snat_not_translate(sm, rt, sw_if_index0, ip0,
1164 proto0, rx_fib_index0)))
1167 next0 = slow_path (sm, b0, ip0, rx_fib_index0, &key0,
1168 &s0, node, next0, cpu_index);
1170 if (PREDICT_FALSE (next0 == SNAT_IN2OUT_NEXT_DROP))
1175 next0 = SNAT_IN2OUT_NEXT_SLOW_PATH;
1180 s0 = pool_elt_at_index (sm->per_thread_data[cpu_index].sessions,
1183 old_addr0 = ip0->src_address.as_u32;
1184 ip0->src_address = s0->out2in.addr;
1185 new_addr0 = ip0->src_address.as_u32;
1186 vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
1188 sum0 = ip0->checksum;
1189 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1191 src_address /* changed member */);
1192 ip0->checksum = ip_csum_fold (sum0);
1194 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1196 old_port0 = tcp0->src_port;
1197 tcp0->src_port = s0->out2in.port;
1198 new_port0 = tcp0->src_port;
1200 sum0 = tcp0->checksum;
1201 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1203 dst_address /* changed member */);
1204 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1205 ip4_header_t /* cheat */,
1206 length /* changed member */);
1207 tcp0->checksum = ip_csum_fold(sum0);
1211 old_port0 = udp0->src_port;
1212 udp0->src_port = s0->out2in.port;
1217 snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
1220 s0->last_heard = now;
1222 s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
1223 /* Per-user LRU list maintenance for dynamic translation */
1224 if (!snat_is_session_static (s0))
1226 clib_dlist_remove (sm->per_thread_data[cpu_index].list_pool,
1227 s0->per_user_index);
1228 clib_dlist_addtail (sm->per_thread_data[cpu_index].list_pool,
1229 s0->per_user_list_head_index,
1230 s0->per_user_index);
1234 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1235 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1237 snat_in2out_trace_t *t =
1238 vlib_add_trace (vm, node, b0, sizeof (*t));
1239 t->is_slow_path = is_slow_path;
1240 t->sw_if_index = sw_if_index0;
1241 t->next_index = next0;
1242 t->session_index = ~0;
1244 t->session_index = s0 - sm->per_thread_data[cpu_index].sessions;
1247 pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
1249 /* verify speculative enqueue, maybe switch current next frame */
1250 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1251 to_next, n_left_to_next,
1255 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1258 vlib_node_increment_counter (vm, stats_node_index,
1259 SNAT_IN2OUT_ERROR_IN2OUT_PACKETS,
1261 return frame->n_vectors;
1265 snat_in2out_fast_path_fn (vlib_main_t * vm,
1266 vlib_node_runtime_t * node,
1267 vlib_frame_t * frame)
1269 return snat_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */);
1272 VLIB_REGISTER_NODE (snat_in2out_node) = {
1273 .function = snat_in2out_fast_path_fn,
1274 .name = "snat-in2out",
1275 .vector_size = sizeof (u32),
1276 .format_trace = format_snat_in2out_trace,
1277 .type = VLIB_NODE_TYPE_INTERNAL,
1279 .n_errors = ARRAY_LEN(snat_in2out_error_strings),
1280 .error_strings = snat_in2out_error_strings,
1282 .runtime_data_bytes = sizeof (snat_runtime_t),
1284 .n_next_nodes = SNAT_IN2OUT_N_NEXT,
1286 /* edit / add dispositions here */
1288 [SNAT_IN2OUT_NEXT_DROP] = "error-drop",
1289 [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1290 [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath",
1291 [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1295 VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_node, snat_in2out_fast_path_fn);
1298 snat_in2out_slow_path_fn (vlib_main_t * vm,
1299 vlib_node_runtime_t * node,
1300 vlib_frame_t * frame)
1302 return snat_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */);
1305 VLIB_REGISTER_NODE (snat_in2out_slowpath_node) = {
1306 .function = snat_in2out_slow_path_fn,
1307 .name = "snat-in2out-slowpath",
1308 .vector_size = sizeof (u32),
1309 .format_trace = format_snat_in2out_trace,
1310 .type = VLIB_NODE_TYPE_INTERNAL,
1312 .n_errors = ARRAY_LEN(snat_in2out_error_strings),
1313 .error_strings = snat_in2out_error_strings,
1315 .runtime_data_bytes = sizeof (snat_runtime_t),
1317 .n_next_nodes = SNAT_IN2OUT_N_NEXT,
1319 /* edit / add dispositions here */
1321 [SNAT_IN2OUT_NEXT_DROP] = "error-drop",
1322 [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1323 [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath",
1324 [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1328 VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_slowpath_node, snat_in2out_slow_path_fn);
1330 /**************************/
1331 /*** deterministic mode ***/
1332 /**************************/
1334 snat_det_in2out_node_fn (vlib_main_t * vm,
1335 vlib_node_runtime_t * node,
1336 vlib_frame_t * frame)
1338 u32 n_left_from, * from, * to_next;
1339 snat_in2out_next_t next_index;
1340 u32 pkts_processed = 0;
1341 snat_main_t * sm = &snat_main;
1342 u32 now = (u32) vlib_time_now (vm);
1344 from = vlib_frame_vector_args (frame);
1345 n_left_from = frame->n_vectors;
1346 next_index = node->cached_next_index;
1348 while (n_left_from > 0)
1352 vlib_get_next_frame (vm, node, next_index,
1353 to_next, n_left_to_next);
1355 while (n_left_from >= 4 && n_left_to_next >= 2)
1358 vlib_buffer_t * b0, * b1;
1360 u32 sw_if_index0, sw_if_index1;
1361 ip4_header_t * ip0, * ip1;
1362 ip_csum_t sum0, sum1;
1363 ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1;
1364 u16 old_port0, new_port0, lo_port0, i;
1365 u16 old_port1, new_port1, lo_port1;
1366 udp_header_t * udp0, * udp1;
1367 tcp_header_t * tcp0, * tcp1;
1369 snat_det_out_key_t key0, key1;
1370 snat_det_map_t * dm0, * dm1;
1371 snat_det_session_t * ses0 = 0, * ses1 = 0;
1373 /* Prefetch next iteration. */
1375 vlib_buffer_t * p2, * p3;
1377 p2 = vlib_get_buffer (vm, from[2]);
1378 p3 = vlib_get_buffer (vm, from[3]);
1380 vlib_prefetch_buffer_header (p2, LOAD);
1381 vlib_prefetch_buffer_header (p3, LOAD);
1383 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
1384 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
1387 /* speculatively enqueue b0 and b1 to the current next frame */
1388 to_next[0] = bi0 = from[0];
1389 to_next[1] = bi1 = from[1];
1393 n_left_to_next -= 2;
1395 b0 = vlib_get_buffer (vm, bi0);
1396 b1 = vlib_get_buffer (vm, bi1);
1398 next0 = SNAT_IN2OUT_NEXT_LOOKUP;
1399 next1 = SNAT_IN2OUT_NEXT_LOOKUP;
1401 ip0 = vlib_buffer_get_current (b0);
1402 udp0 = ip4_next_header (ip0);
1403 tcp0 = (tcp_header_t *) udp0;
1405 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1407 dm0 = snat_det_map_by_user(sm, &ip0->src_address);
1408 if (PREDICT_FALSE(!dm0))
1410 clib_warning("no match for internal host %U",
1411 format_ip4_address, &ip0->src_address);
1415 snat_det_forward(dm0, &ip0->src_address, &new_addr0, &lo_port0);
1417 ses0 = snat_det_find_ses_by_in(dm0, &ip0->src_address, tcp0->src);
1418 if (PREDICT_FALSE(!ses0))
1420 key0.ext_host_addr = ip0->dst_address;
1421 key0.ext_host_port = tcp0->dst;
1422 for (i = 0; i < dm0->ports_per_host; i++)
1424 key0.out_port = clib_host_to_net_u16 (lo_port0 + i +
1425 (clib_net_to_host_u16 (tcp0->src) % dm0->ports_per_host));
1427 if (snat_det_get_ses_by_out (dm0, &ip0->src_address, key0.as_u64))
1430 ses0 = snat_det_ses_create(dm0, &ip0->src_address, tcp0->src, &key0);
1433 if (PREDICT_FALSE(!ses0))
1435 next0 = SNAT_IN2OUT_NEXT_DROP;
1440 new_port0 = ses0->out.out_port;
1441 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1443 old_addr0.as_u32 = ip0->src_address.as_u32;
1444 ip0->src_address.as_u32 = new_addr0.as_u32;
1445 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->outside_fib_index;
1447 sum0 = ip0->checksum;
1448 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1450 src_address /* changed member */);
1451 ip0->checksum = ip_csum_fold (sum0);
1453 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1455 if (tcp0->flags & TCP_FLAG_SYN)
1456 ses0->state = SNAT_SESSION_TCP_SYN_SENT;
1457 else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_SYN_SENT)
1458 ses0->state = SNAT_SESSION_TCP_ESTABLISHED;
1459 else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED)
1460 ses0->state = SNAT_SESSION_TCP_FIN_WAIT;
1461 else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_FIN_WAIT)
1462 snat_det_ses_close(dm0, ses0);
1463 else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_CLOSE_WAIT)
1464 ses0->state = SNAT_SESSION_TCP_LAST_ACK;
1465 else if (tcp0->flags == 0 && ses0->state == SNAT_SESSION_UNKNOWN)
1466 ses0->state = SNAT_SESSION_TCP_ESTABLISHED;
1468 old_port0 = tcp0->src;
1469 tcp0->src = new_port0;
1471 sum0 = tcp0->checksum;
1472 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1474 dst_address /* changed member */);
1475 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1476 ip4_header_t /* cheat */,
1477 length /* changed member */);
1478 tcp0->checksum = ip_csum_fold(sum0);
1482 ses0->state = SNAT_SESSION_UDP_ACTIVE;
1483 old_port0 = udp0->src_port;
1484 udp0->src_port = new_port0;
1490 case SNAT_SESSION_UDP_ACTIVE:
1491 ses0->expire = now + SNAT_UDP_TIMEOUT;
1493 case SNAT_SESSION_TCP_SYN_SENT:
1494 case SNAT_SESSION_TCP_FIN_WAIT:
1495 case SNAT_SESSION_TCP_CLOSE_WAIT:
1496 case SNAT_SESSION_TCP_LAST_ACK:
1497 ses0->expire = now + SNAT_TCP_TRANSITORY_TIMEOUT;
1499 case SNAT_SESSION_TCP_ESTABLISHED:
1500 ses0->expire = now + SNAT_TCP_ESTABLISHED_TIMEOUT;
1505 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1506 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1508 snat_in2out_trace_t *t =
1509 vlib_add_trace (vm, node, b0, sizeof (*t));
1510 t->is_slow_path = 0;
1511 t->sw_if_index = sw_if_index0;
1512 t->next_index = next0;
1513 t->session_index = ~0;
1515 t->session_index = ses0 - dm0->sessions;
1518 pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
1520 ip1 = vlib_buffer_get_current (b1);
1521 udp1 = ip4_next_header (ip1);
1522 tcp1 = (tcp_header_t *) udp1;
1524 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
1526 dm1 = snat_det_map_by_user(sm, &ip1->src_address);
1527 if (PREDICT_FALSE(!dm1))
1529 clib_warning("no match for internal host %U",
1530 format_ip4_address, &ip0->src_address);
1534 snat_det_forward(dm1, &ip1->src_address, &new_addr1, &lo_port1);
1537 ses1 = snat_det_find_ses_by_in(dm1, &ip1->src_address, tcp1->src);
1538 if (PREDICT_FALSE(!ses1))
1540 key1.ext_host_addr = ip1->dst_address;
1541 key1.ext_host_port = tcp1->dst;
1542 for (i = 0; i < dm1->ports_per_host; i++)
1544 key1.out_port = clib_host_to_net_u16 (lo_port1 + i +
1545 (clib_net_to_host_u16 (tcp1->src) % dm1->ports_per_host));
1547 if (snat_det_get_ses_by_out (dm1, &ip1->src_address, key1.as_u64))
1550 ses1 = snat_det_ses_create(dm1, &ip1->src_address, tcp1->src, &key1);
1553 if (PREDICT_FALSE(!ses1))
1555 next1 = SNAT_IN2OUT_NEXT_DROP;
1560 new_port1 = ses1->out.out_port;
1561 proto1 = ip_proto_to_snat_proto (ip1->protocol);
1563 old_addr1.as_u32 = ip1->src_address.as_u32;
1564 ip1->src_address.as_u32 = new_addr1.as_u32;
1565 vnet_buffer(b1)->sw_if_index[VLIB_TX] = sm->outside_fib_index;
1567 sum1 = ip1->checksum;
1568 sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
1570 src_address /* changed member */);
1571 ip1->checksum = ip_csum_fold (sum1);
1573 if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
1575 if (tcp1->flags & TCP_FLAG_SYN)
1576 ses1->state = SNAT_SESSION_TCP_SYN_SENT;
1577 else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_SYN_SENT)
1578 ses1->state = SNAT_SESSION_TCP_ESTABLISHED;
1579 else if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_ESTABLISHED)
1580 ses1->state = SNAT_SESSION_TCP_FIN_WAIT;
1581 else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_FIN_WAIT)
1582 snat_det_ses_close(dm1, ses1);
1583 else if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_CLOSE_WAIT)
1584 ses1->state = SNAT_SESSION_TCP_LAST_ACK;
1585 else if (tcp1->flags == 0 && ses1->state == SNAT_SESSION_UNKNOWN)
1586 ses1->state = SNAT_SESSION_TCP_ESTABLISHED;
1588 old_port1 = tcp1->src;
1589 tcp1->src = new_port1;
1591 sum1 = tcp1->checksum;
1592 sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
1594 dst_address /* changed member */);
1595 sum1 = ip_csum_update (sum1, old_port1, new_port1,
1596 ip4_header_t /* cheat */,
1597 length /* changed member */);
1598 tcp1->checksum = ip_csum_fold(sum1);
1602 ses1->state = SNAT_SESSION_UDP_ACTIVE;
1603 old_port1 = udp1->src_port;
1604 udp1->src_port = new_port1;
1610 case SNAT_SESSION_UDP_ACTIVE:
1611 ses1->expire = now + SNAT_UDP_TIMEOUT;
1613 case SNAT_SESSION_TCP_SYN_SENT:
1614 case SNAT_SESSION_TCP_FIN_WAIT:
1615 case SNAT_SESSION_TCP_CLOSE_WAIT:
1616 case SNAT_SESSION_TCP_LAST_ACK:
1617 ses1->expire = now + SNAT_TCP_TRANSITORY_TIMEOUT;
1619 case SNAT_SESSION_TCP_ESTABLISHED:
1620 ses1->expire = now + SNAT_TCP_ESTABLISHED_TIMEOUT;
1625 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1626 && (b1->flags & VLIB_BUFFER_IS_TRACED)))
1628 snat_in2out_trace_t *t =
1629 vlib_add_trace (vm, node, b1, sizeof (*t));
1630 t->is_slow_path = 0;
1631 t->sw_if_index = sw_if_index1;
1632 t->next_index = next1;
1633 t->session_index = ~0;
1635 t->session_index = ses1 - dm1->sessions;
1638 pkts_processed += next1 != SNAT_IN2OUT_NEXT_DROP;
1640 /* verify speculative enqueues, maybe switch current next frame */
1641 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1642 to_next, n_left_to_next,
1643 bi0, bi1, next0, next1);
1646 while (n_left_from > 0 && n_left_to_next > 0)
1654 ip4_address_t new_addr0, old_addr0;
1655 u16 old_port0, new_port0, lo_port0, i;
1656 udp_header_t * udp0;
1657 tcp_header_t * tcp0;
1659 snat_det_out_key_t key0;
1660 snat_det_map_t * dm0;
1661 snat_det_session_t * ses0 = 0;
1663 /* speculatively enqueue b0 to the current next frame */
1669 n_left_to_next -= 1;
1671 b0 = vlib_get_buffer (vm, bi0);
1672 next0 = SNAT_IN2OUT_NEXT_LOOKUP;
1674 ip0 = vlib_buffer_get_current (b0);
1675 udp0 = ip4_next_header (ip0);
1676 tcp0 = (tcp_header_t *) udp0;
1678 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1680 dm0 = snat_det_map_by_user(sm, &ip0->src_address);
1681 if (PREDICT_FALSE(!dm0))
1683 clib_warning("no match for internal host %U",
1684 format_ip4_address, &ip0->src_address);
1688 snat_det_forward(dm0, &ip0->src_address, &new_addr0, &lo_port0);
1690 ses0 = snat_det_find_ses_by_in(dm0, &ip0->src_address, tcp0->src);
1691 if (PREDICT_FALSE(!ses0))
1693 key0.ext_host_addr = ip0->dst_address;
1694 key0.ext_host_port = tcp0->dst;
1695 for (i = 0; i < dm0->ports_per_host; i++)
1697 key0.out_port = clib_host_to_net_u16 (lo_port0 + i +
1698 (clib_net_to_host_u16 (tcp0->src) % dm0->ports_per_host));
1700 if (snat_det_get_ses_by_out (dm0, &ip0->src_address, key0.as_u64))
1703 ses0 = snat_det_ses_create(dm0, &ip0->src_address, tcp0->src, &key0);
1706 if (PREDICT_FALSE(!ses0))
1708 next0 = SNAT_IN2OUT_NEXT_DROP;
1713 new_port0 = ses0->out.out_port;
1714 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1716 old_addr0.as_u32 = ip0->src_address.as_u32;
1717 ip0->src_address.as_u32 = new_addr0.as_u32;
1718 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->outside_fib_index;
1720 sum0 = ip0->checksum;
1721 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1723 src_address /* changed member */);
1724 ip0->checksum = ip_csum_fold (sum0);
1726 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1728 if (tcp0->flags & TCP_FLAG_SYN)
1729 ses0->state = SNAT_SESSION_TCP_SYN_SENT;
1730 else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_SYN_SENT)
1731 ses0->state = SNAT_SESSION_TCP_ESTABLISHED;
1732 else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED)
1733 ses0->state = SNAT_SESSION_TCP_FIN_WAIT;
1734 else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_FIN_WAIT)
1735 snat_det_ses_close(dm0, ses0);
1736 else if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_CLOSE_WAIT)
1737 ses0->state = SNAT_SESSION_TCP_LAST_ACK;
1738 else if (tcp0->flags == 0 && ses0->state == SNAT_SESSION_UNKNOWN)
1739 ses0->state = SNAT_SESSION_TCP_ESTABLISHED;
1741 old_port0 = tcp0->src;
1742 tcp0->src = new_port0;
1744 sum0 = tcp0->checksum;
1745 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1747 dst_address /* changed member */);
1748 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1749 ip4_header_t /* cheat */,
1750 length /* changed member */);
1751 tcp0->checksum = ip_csum_fold(sum0);
1755 ses0->state = SNAT_SESSION_UDP_ACTIVE;
1756 old_port0 = udp0->src_port;
1757 udp0->src_port = new_port0;
1763 case SNAT_SESSION_UDP_ACTIVE:
1764 ses0->expire = now + SNAT_UDP_TIMEOUT;
1766 case SNAT_SESSION_TCP_SYN_SENT:
1767 case SNAT_SESSION_TCP_FIN_WAIT:
1768 case SNAT_SESSION_TCP_CLOSE_WAIT:
1769 case SNAT_SESSION_TCP_LAST_ACK:
1770 ses0->expire = now + SNAT_TCP_TRANSITORY_TIMEOUT;
1772 case SNAT_SESSION_TCP_ESTABLISHED:
1773 ses0->expire = now + SNAT_TCP_ESTABLISHED_TIMEOUT;
1778 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1779 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1781 snat_in2out_trace_t *t =
1782 vlib_add_trace (vm, node, b0, sizeof (*t));
1783 t->is_slow_path = 0;
1784 t->sw_if_index = sw_if_index0;
1785 t->next_index = next0;
1786 t->session_index = ~0;
1788 t->session_index = ses0 - dm0->sessions;
1791 pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
1793 /* verify speculative enqueue, maybe switch current next frame */
1794 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1795 to_next, n_left_to_next,
1799 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1802 vlib_node_increment_counter (vm, snat_det_in2out_node.index,
1803 SNAT_IN2OUT_ERROR_IN2OUT_PACKETS,
1805 return frame->n_vectors;
1808 VLIB_REGISTER_NODE (snat_det_in2out_node) = {
1809 .function = snat_det_in2out_node_fn,
1810 .name = "snat-det-in2out",
1811 .vector_size = sizeof (u32),
1812 .format_trace = format_snat_in2out_trace,
1813 .type = VLIB_NODE_TYPE_INTERNAL,
1815 .n_errors = ARRAY_LEN(snat_in2out_error_strings),
1816 .error_strings = snat_in2out_error_strings,
1818 .runtime_data_bytes = sizeof (snat_runtime_t),
1822 /* edit / add dispositions here */
1824 [SNAT_IN2OUT_NEXT_DROP] = "error-drop",
1825 [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1829 VLIB_NODE_FUNCTION_MULTIARCH (snat_det_in2out_node, snat_det_in2out_node_fn);
1831 /**********************/
1832 /*** worker handoff ***/
1833 /**********************/
1835 snat_in2out_worker_handoff_fn (vlib_main_t * vm,
1836 vlib_node_runtime_t * node,
1837 vlib_frame_t * frame)
1839 snat_main_t *sm = &snat_main;
1840 vlib_thread_main_t *tm = vlib_get_thread_main ();
1841 u32 n_left_from, *from, *to_next = 0;
1842 static __thread vlib_frame_queue_elt_t **handoff_queue_elt_by_worker_index;
1843 static __thread vlib_frame_queue_t **congested_handoff_queue_by_worker_index
1845 vlib_frame_queue_elt_t *hf = 0;
1846 vlib_frame_t *f = 0;
1848 u32 n_left_to_next_worker = 0, *to_next_worker = 0;
1849 u32 next_worker_index = 0;
1850 u32 current_worker_index = ~0;
1851 u32 cpu_index = os_get_cpu_number ();
1853 ASSERT (vec_len (sm->workers));
1855 if (PREDICT_FALSE (handoff_queue_elt_by_worker_index == 0))
1857 vec_validate (handoff_queue_elt_by_worker_index, tm->n_vlib_mains - 1);
1859 vec_validate_init_empty (congested_handoff_queue_by_worker_index,
1860 sm->first_worker_index + sm->num_workers - 1,
1861 (vlib_frame_queue_t *) (~0));
1864 from = vlib_frame_vector_args (frame);
1865 n_left_from = frame->n_vectors;
1867 while (n_left_from > 0)
1880 b0 = vlib_get_buffer (vm, bi0);
1882 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1883 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
1885 ip0 = vlib_buffer_get_current (b0);
1887 next_worker_index = sm->worker_in2out_cb(ip0, rx_fib_index0);
1889 if (PREDICT_FALSE (next_worker_index != cpu_index))
1893 if (next_worker_index != current_worker_index)
1896 hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker;
1898 hf = vlib_get_worker_handoff_queue_elt (sm->fq_in2out_index,
1900 handoff_queue_elt_by_worker_index);
1902 n_left_to_next_worker = VLIB_FRAME_SIZE - hf->n_vectors;
1903 to_next_worker = &hf->buffer_index[hf->n_vectors];
1904 current_worker_index = next_worker_index;
1907 /* enqueue to correct worker thread */
1908 to_next_worker[0] = bi0;
1910 n_left_to_next_worker--;
1912 if (n_left_to_next_worker == 0)
1914 hf->n_vectors = VLIB_FRAME_SIZE;
1915 vlib_put_frame_queue_elt (hf);
1916 current_worker_index = ~0;
1917 handoff_queue_elt_by_worker_index[next_worker_index] = 0;
1924 /* if this is 1st frame */
1927 f = vlib_get_frame_to_node (vm, sm->in2out_node_index);
1928 to_next = vlib_frame_vector_args (f);
1936 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1937 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1939 snat_in2out_worker_handoff_trace_t *t =
1940 vlib_add_trace (vm, node, b0, sizeof (*t));
1941 t->next_worker_index = next_worker_index;
1942 t->do_handoff = do_handoff;
1947 vlib_put_frame_to_node (vm, sm->in2out_node_index, f);
1950 hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker;
1952 /* Ship frames to the worker nodes */
1953 for (i = 0; i < vec_len (handoff_queue_elt_by_worker_index); i++)
1955 if (handoff_queue_elt_by_worker_index[i])
1957 hf = handoff_queue_elt_by_worker_index[i];
1959 * It works better to let the handoff node
1960 * rate-adapt, always ship the handoff queue element.
1962 if (1 || hf->n_vectors == hf->last_n_vectors)
1964 vlib_put_frame_queue_elt (hf);
1965 handoff_queue_elt_by_worker_index[i] = 0;
1968 hf->last_n_vectors = hf->n_vectors;
1970 congested_handoff_queue_by_worker_index[i] =
1971 (vlib_frame_queue_t *) (~0);
1974 current_worker_index = ~0;
1975 return frame->n_vectors;
1978 VLIB_REGISTER_NODE (snat_in2out_worker_handoff_node) = {
1979 .function = snat_in2out_worker_handoff_fn,
1980 .name = "snat-in2out-worker-handoff",
1981 .vector_size = sizeof (u32),
1982 .format_trace = format_snat_in2out_worker_handoff_trace,
1983 .type = VLIB_NODE_TYPE_INTERNAL,
1992 VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_worker_handoff_node, snat_in2out_worker_handoff_fn);
1994 /********************************/
1995 /*** static mapping only mode ***/
1996 /********************************/
1997 static inline u32 icmp_in2out_static_map (snat_main_t *sm,
2000 icmp46_header_t * icmp0,
2002 vlib_node_runtime_t * node,
2006 snat_session_key_t key0, sm0;
2007 icmp_echo_header_t *echo0;
2008 u32 new_addr0, old_addr0;
2009 u16 old_id0, new_id0;
2011 snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
2013 echo0 = (icmp_echo_header_t *)(icmp0+1);
2015 key0.addr = ip0->src_address;
2016 key0.port = echo0->identifier;
2017 key0.fib_index = rx_fib_index0;
2019 if (snat_static_mapping_match(sm, key0, &sm0, 0))
2021 if (PREDICT_FALSE(snat_not_translate(sm, rt, sw_if_index0, ip0,
2022 IP_PROTOCOL_ICMP, rx_fib_index0)))
2025 b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION];
2026 return SNAT_IN2OUT_NEXT_DROP;
2029 new_addr0 = sm0.addr.as_u32;
2031 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
2032 old_addr0 = ip0->src_address.as_u32;
2033 ip0->src_address.as_u32 = new_addr0;
2035 sum0 = ip0->checksum;
2036 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
2038 src_address /* changed member */);
2039 ip0->checksum = ip_csum_fold (sum0);
2041 if (PREDICT_FALSE(new_id0 != echo0->identifier))
2043 old_id0 = echo0->identifier;
2044 echo0->identifier = new_id0;
2046 sum0 = icmp0->checksum;
2047 sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
2049 icmp0->checksum = ip_csum_fold (sum0);
2056 snat_in2out_fast_static_map_fn (vlib_main_t * vm,
2057 vlib_node_runtime_t * node,
2058 vlib_frame_t * frame)
2060 u32 n_left_from, * from, * to_next;
2061 snat_in2out_next_t next_index;
2062 u32 pkts_processed = 0;
2063 snat_main_t * sm = &snat_main;
2064 snat_runtime_t * rt = (snat_runtime_t *)node->runtime_data;
2065 u32 stats_node_index;
2067 stats_node_index = snat_in2out_fast_node.index;
2069 from = vlib_frame_vector_args (frame);
2070 n_left_from = frame->n_vectors;
2071 next_index = node->cached_next_index;
2073 while (n_left_from > 0)
2077 vlib_get_next_frame (vm, node, next_index,
2078 to_next, n_left_to_next);
2080 while (n_left_from > 0 && n_left_to_next > 0)
2088 u32 new_addr0, old_addr0;
2089 u16 old_port0, new_port0;
2090 udp_header_t * udp0;
2091 tcp_header_t * tcp0;
2092 icmp46_header_t * icmp0;
2093 snat_session_key_t key0, sm0;
2097 /* speculatively enqueue b0 to the current next frame */
2103 n_left_to_next -= 1;
2105 b0 = vlib_get_buffer (vm, bi0);
2106 next0 = SNAT_IN2OUT_NEXT_LOOKUP;
2108 ip0 = vlib_buffer_get_current (b0);
2109 udp0 = ip4_next_header (ip0);
2110 tcp0 = (tcp_header_t *) udp0;
2111 icmp0 = (icmp46_header_t *) udp0;
2113 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
2114 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
2116 proto0 = ip_proto_to_snat_proto (ip0->protocol);
2118 if (PREDICT_FALSE (proto0 == ~0))
2121 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
2123 if (PREDICT_FALSE(snat_not_translate(sm, rt, sw_if_index0, ip0,
2124 proto0, rx_fib_index0)))
2127 next0 = icmp_in2out_static_map
2128 (sm, b0, ip0, icmp0, sw_if_index0, node, next0, rx_fib_index0);
2132 key0.addr = ip0->src_address;
2133 key0.port = udp0->src_port;
2134 key0.fib_index = rx_fib_index0;
2136 if (snat_static_mapping_match(sm, key0, &sm0, 0))
2138 b0->error = node->errors[SNAT_IN2OUT_ERROR_NO_TRANSLATION];
2139 next0= SNAT_IN2OUT_NEXT_DROP;
2143 new_addr0 = sm0.addr.as_u32;
2144 new_port0 = sm0.port;
2145 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
2146 old_addr0 = ip0->src_address.as_u32;
2147 ip0->src_address.as_u32 = new_addr0;
2149 sum0 = ip0->checksum;
2150 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
2152 src_address /* changed member */);
2153 ip0->checksum = ip_csum_fold (sum0);
2155 if (PREDICT_FALSE(new_port0 != udp0->dst_port))
2157 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
2159 old_port0 = tcp0->src_port;
2160 tcp0->src_port = new_port0;
2162 sum0 = tcp0->checksum;
2163 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
2165 dst_address /* changed member */);
2166 sum0 = ip_csum_update (sum0, old_port0, new_port0,
2167 ip4_header_t /* cheat */,
2168 length /* changed member */);
2169 tcp0->checksum = ip_csum_fold(sum0);
2173 old_port0 = udp0->src_port;
2174 udp0->src_port = new_port0;
2180 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
2182 sum0 = tcp0->checksum;
2183 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
2185 dst_address /* changed member */);
2186 tcp0->checksum = ip_csum_fold(sum0);
2191 snat_hairpinning (sm, b0, ip0, udp0, tcp0, proto0);
2194 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
2195 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
2197 snat_in2out_trace_t *t =
2198 vlib_add_trace (vm, node, b0, sizeof (*t));
2199 t->sw_if_index = sw_if_index0;
2200 t->next_index = next0;
2203 pkts_processed += next0 != SNAT_IN2OUT_NEXT_DROP;
2205 /* verify speculative enqueue, maybe switch current next frame */
2206 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2207 to_next, n_left_to_next,
2211 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2214 vlib_node_increment_counter (vm, stats_node_index,
2215 SNAT_IN2OUT_ERROR_IN2OUT_PACKETS,
2217 return frame->n_vectors;
2221 VLIB_REGISTER_NODE (snat_in2out_fast_node) = {
2222 .function = snat_in2out_fast_static_map_fn,
2223 .name = "snat-in2out-fast",
2224 .vector_size = sizeof (u32),
2225 .format_trace = format_snat_in2out_fast_trace,
2226 .type = VLIB_NODE_TYPE_INTERNAL,
2228 .n_errors = ARRAY_LEN(snat_in2out_error_strings),
2229 .error_strings = snat_in2out_error_strings,
2231 .runtime_data_bytes = sizeof (snat_runtime_t),
2233 .n_next_nodes = SNAT_IN2OUT_N_NEXT,
2235 /* edit / add dispositions here */
2237 [SNAT_IN2OUT_NEXT_DROP] = "error-drop",
2238 [SNAT_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
2239 [SNAT_IN2OUT_NEXT_SLOW_PATH] = "snat-in2out-slowpath",
2240 [SNAT_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
2244 VLIB_NODE_FUNCTION_MULTIARCH (snat_in2out_fast_node, snat_in2out_fast_static_map_fn);