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/udp/udp.h>
23 #include <vnet/ethernet/ethernet.h>
24 #include <vnet/fib/ip4_fib.h>
25 #include <snat/snat.h>
26 #include <snat/snat_ipfix_logging.h>
27 #include <snat/snat_det.h>
29 #include <vppinfra/hash.h>
30 #include <vppinfra/error.h>
31 #include <vppinfra/elog.h>
37 } snat_out2in_trace_t;
40 u32 next_worker_index;
42 } snat_out2in_worker_handoff_trace_t;
44 /* packet trace format function */
45 static u8 * format_snat_out2in_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_out2in_trace_t * t = va_arg (*args, snat_out2in_trace_t *);
51 s = format (s, "SNAT_OUT2IN: sw_if_index %d, next index %d, session index %d",
52 t->sw_if_index, t->next_index, t->session_index);
56 static u8 * format_snat_out2in_fast_trace (u8 * s, va_list * args)
58 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
59 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
60 snat_out2in_trace_t * t = va_arg (*args, snat_out2in_trace_t *);
62 s = format (s, "SNAT_OUT2IN_FAST: sw_if_index %d, next index %d",
63 t->sw_if_index, t->next_index);
67 static u8 * format_snat_out2in_worker_handoff_trace (u8 * s, va_list * args)
69 CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
70 CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
71 snat_out2in_worker_handoff_trace_t * t =
72 va_arg (*args, snat_out2in_worker_handoff_trace_t *);
75 m = t->do_handoff ? "next worker" : "same worker";
76 s = format (s, "SNAT_OUT2IN_WORKER_HANDOFF: %s %d", m, t->next_worker_index);
81 vlib_node_registration_t snat_out2in_node;
82 vlib_node_registration_t snat_out2in_fast_node;
83 vlib_node_registration_t snat_out2in_worker_handoff_node;
84 vlib_node_registration_t snat_det_out2in_node;
86 #define foreach_snat_out2in_error \
87 _(UNSUPPORTED_PROTOCOL, "Unsupported protocol") \
88 _(OUT2IN_PACKETS, "Good out2in packets processed") \
89 _(BAD_ICMP_TYPE, "icmp type not echo-reply") \
90 _(NO_TRANSLATION, "No translation")
93 #define _(sym,str) SNAT_OUT2IN_ERROR_##sym,
94 foreach_snat_out2in_error
97 } snat_out2in_error_t;
99 static char * snat_out2in_error_strings[] = {
100 #define _(sym,string) string,
101 foreach_snat_out2in_error
106 SNAT_OUT2IN_NEXT_DROP,
107 SNAT_OUT2IN_NEXT_LOOKUP,
108 SNAT_OUT2IN_NEXT_ICMP_ERROR,
110 } snat_out2in_next_t;
113 * @brief Create session for static mapping.
115 * Create NAT session initiated by host from external network with static
118 * @param sm SNAT main.
119 * @param b0 Vlib buffer.
120 * @param in2out In2out SNAT session key.
121 * @param out2in Out2in SNAT session key.
122 * @param node Vlib node.
124 * @returns SNAT session if successfully created otherwise 0.
126 static inline snat_session_t *
127 create_session_for_static_mapping (snat_main_t *sm,
129 snat_session_key_t in2out,
130 snat_session_key_t out2in,
131 vlib_node_runtime_t * node,
135 snat_user_key_t user_key;
137 clib_bihash_kv_8_8_t kv0, value0;
138 dlist_elt_t * per_user_translation_list_elt;
139 dlist_elt_t * per_user_list_head_elt;
141 user_key.addr = in2out.addr;
142 user_key.fib_index = in2out.fib_index;
143 kv0.key = user_key.as_u64;
145 /* Ever heard of the "user" = inside ip4 address before? */
146 if (clib_bihash_search_8_8 (&sm->user_hash, &kv0, &value0))
148 /* no, make a new one */
149 pool_get (sm->per_thread_data[thread_index].users, u);
150 memset (u, 0, sizeof (*u));
151 u->addr = in2out.addr;
152 u->fib_index = in2out.fib_index;
154 pool_get (sm->per_thread_data[thread_index].list_pool,
155 per_user_list_head_elt);
157 u->sessions_per_user_list_head_index = per_user_list_head_elt -
158 sm->per_thread_data[thread_index].list_pool;
160 clib_dlist_init (sm->per_thread_data[thread_index].list_pool,
161 u->sessions_per_user_list_head_index);
163 kv0.value = u - sm->per_thread_data[thread_index].users;
166 clib_bihash_add_del_8_8 (&sm->user_hash, &kv0, 1 /* is_add */);
168 /* add non-traslated packets worker lookup */
169 kv0.value = thread_index;
170 clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv0, 1);
174 u = pool_elt_at_index (sm->per_thread_data[thread_index].users,
178 pool_get (sm->per_thread_data[thread_index].sessions, s);
179 memset (s, 0, sizeof (*s));
181 s->outside_address_index = ~0;
182 s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
183 u->nstaticsessions++;
185 /* Create list elts */
186 pool_get (sm->per_thread_data[thread_index].list_pool,
187 per_user_translation_list_elt);
188 clib_dlist_init (sm->per_thread_data[thread_index].list_pool,
189 per_user_translation_list_elt -
190 sm->per_thread_data[thread_index].list_pool);
192 per_user_translation_list_elt->value =
193 s - sm->per_thread_data[thread_index].sessions;
195 per_user_translation_list_elt - sm->per_thread_data[thread_index].list_pool;
196 s->per_user_list_head_index = u->sessions_per_user_list_head_index;
198 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
199 s->per_user_list_head_index,
200 per_user_translation_list_elt -
201 sm->per_thread_data[thread_index].list_pool);
205 s->in2out.protocol = out2in.protocol;
207 /* Add to translation hashes */
208 kv0.key = s->in2out.as_u64;
209 kv0.value = s - sm->per_thread_data[thread_index].sessions;
210 if (clib_bihash_add_del_8_8 (&sm->in2out, &kv0, 1 /* is_add */))
211 clib_warning ("in2out key add failed");
213 kv0.key = s->out2in.as_u64;
214 kv0.value = s - sm->per_thread_data[thread_index].sessions;
216 if (clib_bihash_add_del_8_8 (&sm->out2in, &kv0, 1 /* is_add */))
217 clib_warning ("out2in key add failed");
220 snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32,
221 s->out2in.addr.as_u32,
225 s->in2out.fib_index);
230 snat_out2in_error_t icmp_get_key(ip4_header_t *ip0,
231 snat_session_key_t *p_key0)
233 icmp46_header_t *icmp0;
234 snat_session_key_t key0;
235 icmp_echo_header_t *echo0, *inner_echo0 = 0;
236 ip4_header_t *inner_ip0;
238 icmp46_header_t *inner_icmp0;
240 icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
241 echo0 = (icmp_echo_header_t *)(icmp0+1);
243 if (!icmp_is_error_message (icmp0))
245 key0.protocol = SNAT_PROTOCOL_ICMP;
246 key0.addr = ip0->dst_address;
247 key0.port = echo0->identifier;
251 inner_ip0 = (ip4_header_t *)(echo0+1);
252 l4_header = ip4_next_header (inner_ip0);
253 key0.protocol = ip_proto_to_snat_proto (inner_ip0->protocol);
254 key0.addr = inner_ip0->src_address;
255 switch (key0.protocol)
257 case SNAT_PROTOCOL_ICMP:
258 inner_icmp0 = (icmp46_header_t*)l4_header;
259 inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1);
260 key0.port = inner_echo0->identifier;
262 case SNAT_PROTOCOL_UDP:
263 case SNAT_PROTOCOL_TCP:
264 key0.port = ((tcp_udp_header_t*)l4_header)->src_port;
267 return SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL;
271 return -1; /* success */
275 * Get address and port values to be used for packet SNAT translation
276 * and create session if needed
278 * @param[in,out] sm SNAT main
279 * @param[in,out] node SNAT node runtime
280 * @param[in] thread_index thread index
281 * @param[in,out] b0 buffer containing packet to be translated
282 * @param[out] p_proto protocol used for matching
283 * @param[out] p_value address and port after NAT translation
284 * @param[out] p_dont_translate if packet should not be translated
285 * @param d optional parameter
287 u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node,
288 u32 thread_index, vlib_buffer_t *b0, u8 *p_proto,
289 snat_session_key_t *p_value,
290 u8 *p_dont_translate, void *d)
293 icmp46_header_t *icmp0;
296 snat_session_key_t key0;
297 snat_session_key_t sm0;
298 snat_session_t *s0 = 0;
299 u8 dont_translate = 0;
300 clib_bihash_kv_8_8_t kv0, value0;
304 ip0 = vlib_buffer_get_current (b0);
305 icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
306 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
307 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
309 err = icmp_get_key (ip0, &key0);
312 b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
313 next0 = SNAT_OUT2IN_NEXT_DROP;
316 key0.fib_index = rx_fib_index0;
318 kv0.key = key0.as_u64;
320 if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
322 /* Try to match static mapping by external address and port,
323 destination address and port in packet */
324 if (snat_static_mapping_match(sm, key0, &sm0, 1))
326 /* Don't NAT packet aimed at the intfc address */
327 if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0,
328 ip0->dst_address.as_u32)))
333 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
334 next0 = SNAT_OUT2IN_NEXT_DROP;
338 if (icmp_is_error_message (icmp0))
340 next0 = SNAT_OUT2IN_NEXT_DROP;
344 /* Create session initiated by host from external network */
345 s0 = create_session_for_static_mapping(sm, b0, sm0, key0,
350 next0 = SNAT_OUT2IN_NEXT_DROP;
355 s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions,
358 if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply &&
359 !icmp_is_error_message (icmp0)))
361 b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE];
362 next0 = SNAT_OUT2IN_NEXT_DROP;
367 *p_proto = key0.protocol;
369 *p_value = s0->in2out;
370 *p_dont_translate = dont_translate;
372 *(snat_session_t**)d = s0;
377 * Get address and port values to be used for packet SNAT translation
379 * @param[in] sm SNAT main
380 * @param[in,out] node SNAT node runtime
381 * @param[in] thread_index thread index
382 * @param[in,out] b0 buffer containing packet to be translated
383 * @param[out] p_proto protocol used for matching
384 * @param[out] p_value address and port after NAT translation
385 * @param[out] p_dont_translate if packet should not be translated
386 * @param d optional parameter
388 u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node,
389 u32 thread_index, vlib_buffer_t *b0, u8 *p_proto,
390 snat_session_key_t *p_value,
391 u8 *p_dont_translate, void *d)
394 icmp46_header_t *icmp0;
397 snat_session_key_t key0;
398 snat_session_key_t sm0;
399 u8 dont_translate = 0;
403 ip0 = vlib_buffer_get_current (b0);
404 icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
405 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
406 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
408 err = icmp_get_key (ip0, &key0);
411 b0->error = node->errors[err];
412 next0 = SNAT_OUT2IN_NEXT_DROP;
415 key0.fib_index = rx_fib_index0;
417 if (snat_static_mapping_match(sm, key0, &sm0, 1))
419 /* Don't NAT packet aimed at the intfc address */
420 if (is_interface_addr(sm, node, sw_if_index0, ip0->dst_address.as_u32))
425 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
426 next0 = SNAT_OUT2IN_NEXT_DROP;
430 if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply &&
431 !icmp_is_error_message (icmp0)))
433 if (icmp0->type != ICMP4_echo_request || key0.port != sm0.port)
435 b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE];
436 next0 = SNAT_OUT2IN_NEXT_DROP;
444 *p_proto = key0.protocol;
445 *p_dont_translate = dont_translate;
449 static inline u32 icmp_out2in (snat_main_t *sm,
452 icmp46_header_t * icmp0,
455 vlib_node_runtime_t * node,
460 snat_session_key_t sm0;
462 icmp_echo_header_t *echo0, *inner_echo0 = 0;
463 ip4_header_t *inner_ip0 = 0;
465 icmp46_header_t *inner_icmp0;
467 u32 new_addr0, old_addr0;
468 u16 old_id0, new_id0;
473 echo0 = (icmp_echo_header_t *)(icmp0+1);
475 next0_tmp = sm->icmp_match_out2in_cb(sm, node, thread_index, b0,
476 &protocol, &sm0, &dont_translate, d);
479 if (next0 == SNAT_OUT2IN_NEXT_DROP || dont_translate)
482 sum0 = ip_incremental_checksum (0, icmp0,
483 ntohs(ip0->length) - ip4_header_bytes (ip0));
484 checksum0 = ~ip_csum_fold (sum0);
485 if (checksum0 != 0 && checksum0 != 0xffff)
487 next0 = SNAT_OUT2IN_NEXT_DROP;
491 old_addr0 = ip0->dst_address.as_u32;
492 new_addr0 = ip0->dst_address.as_u32 = sm0.addr.as_u32;
493 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
495 sum0 = ip0->checksum;
496 sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
497 dst_address /* changed member */);
498 ip0->checksum = ip_csum_fold (sum0);
500 if (!icmp_is_error_message (icmp0))
503 if (PREDICT_FALSE(new_id0 != echo0->identifier))
505 old_id0 = echo0->identifier;
507 echo0->identifier = new_id0;
509 sum0 = icmp0->checksum;
510 sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
511 identifier /* changed member */);
512 icmp0->checksum = ip_csum_fold (sum0);
517 inner_ip0 = (ip4_header_t *)(echo0+1);
518 l4_header = ip4_next_header (inner_ip0);
520 if (!ip4_header_checksum_is_valid (inner_ip0))
522 next0 = SNAT_OUT2IN_NEXT_DROP;
526 old_addr0 = inner_ip0->src_address.as_u32;
527 inner_ip0->src_address = sm0.addr;
528 new_addr0 = inner_ip0->src_address.as_u32;
530 sum0 = icmp0->checksum;
531 sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
532 src_address /* changed member */);
533 icmp0->checksum = ip_csum_fold (sum0);
537 case SNAT_PROTOCOL_ICMP:
538 inner_icmp0 = (icmp46_header_t*)l4_header;
539 inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1);
541 old_id0 = inner_echo0->identifier;
543 inner_echo0->identifier = new_id0;
545 sum0 = icmp0->checksum;
546 sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
548 icmp0->checksum = ip_csum_fold (sum0);
550 case SNAT_PROTOCOL_UDP:
551 case SNAT_PROTOCOL_TCP:
552 old_id0 = ((tcp_udp_header_t*)l4_header)->src_port;
554 ((tcp_udp_header_t*)l4_header)->src_port = new_id0;
556 sum0 = icmp0->checksum;
557 sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t,
559 icmp0->checksum = ip_csum_fold (sum0);
571 static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
574 icmp46_header_t * icmp0,
577 vlib_node_runtime_t * node,
580 snat_session_t ** p_s0)
582 next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
583 next0, thread_index, p_s0);
584 snat_session_t * s0 = *p_s0;
585 if (PREDICT_TRUE(next0 != SNAT_OUT2IN_NEXT_DROP && s0))
588 s0->last_heard = now;
590 s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
591 /* Per-user LRU list maintenance for dynamic translation */
592 if (!snat_is_session_static (s0))
594 clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
596 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
597 s0->per_user_list_head_index,
605 snat_out2in_node_fn (vlib_main_t * vm,
606 vlib_node_runtime_t * node,
607 vlib_frame_t * frame)
609 u32 n_left_from, * from, * to_next;
610 snat_out2in_next_t next_index;
611 u32 pkts_processed = 0;
612 snat_main_t * sm = &snat_main;
613 f64 now = vlib_time_now (vm);
614 u32 thread_index = vlib_get_thread_index ();
616 from = vlib_frame_vector_args (frame);
617 n_left_from = frame->n_vectors;
618 next_index = node->cached_next_index;
620 while (n_left_from > 0)
624 vlib_get_next_frame (vm, node, next_index,
625 to_next, n_left_to_next);
627 while (n_left_from >= 4 && n_left_to_next >= 2)
630 vlib_buffer_t * b0, * b1;
631 u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
632 u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP;
633 u32 sw_if_index0, sw_if_index1;
634 ip4_header_t * ip0, *ip1;
635 ip_csum_t sum0, sum1;
636 u32 new_addr0, old_addr0;
637 u16 new_port0, old_port0;
638 u32 new_addr1, old_addr1;
639 u16 new_port1, old_port1;
640 udp_header_t * udp0, * udp1;
641 tcp_header_t * tcp0, * tcp1;
642 icmp46_header_t * icmp0, * icmp1;
643 snat_session_key_t key0, key1, sm0, sm1;
644 u32 rx_fib_index0, rx_fib_index1;
646 snat_session_t * s0 = 0, * s1 = 0;
647 clib_bihash_kv_8_8_t kv0, kv1, value0, value1;
649 /* Prefetch next iteration. */
651 vlib_buffer_t * p2, * p3;
653 p2 = vlib_get_buffer (vm, from[2]);
654 p3 = vlib_get_buffer (vm, from[3]);
656 vlib_prefetch_buffer_header (p2, LOAD);
657 vlib_prefetch_buffer_header (p3, LOAD);
659 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
660 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
663 /* speculatively enqueue b0 and b1 to the current next frame */
664 to_next[0] = bi0 = from[0];
665 to_next[1] = bi1 = from[1];
671 b0 = vlib_get_buffer (vm, bi0);
672 b1 = vlib_get_buffer (vm, bi1);
674 ip0 = vlib_buffer_get_current (b0);
675 udp0 = ip4_next_header (ip0);
676 tcp0 = (tcp_header_t *) udp0;
677 icmp0 = (icmp46_header_t *) udp0;
679 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
680 rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index,
683 if (PREDICT_FALSE(ip0->ttl == 1))
685 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
686 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
687 ICMP4_time_exceeded_ttl_exceeded_in_transit,
689 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
693 proto0 = ip_proto_to_snat_proto (ip0->protocol);
695 if (PREDICT_FALSE (proto0 == ~0))
698 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
700 next0 = icmp_out2in_slow_path
701 (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
702 next0, now, thread_index, &s0);
706 key0.addr = ip0->dst_address;
707 key0.port = udp0->dst_port;
708 key0.protocol = proto0;
709 key0.fib_index = rx_fib_index0;
711 kv0.key = key0.as_u64;
713 if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
715 /* Try to match static mapping by external address and port,
716 destination address and port in packet */
717 if (snat_static_mapping_match(sm, key0, &sm0, 1))
719 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
721 * Send DHCP packets to the ipv4 stack, or we won't
722 * be able to use dhcp client on the outside interface
724 if (proto0 != SNAT_PROTOCOL_UDP
726 != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
727 next0 = SNAT_OUT2IN_NEXT_DROP;
731 /* Create session initiated by host from external network */
732 s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node,
736 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
737 next0 = SNAT_OUT2IN_NEXT_DROP;
742 s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions,
745 old_addr0 = ip0->dst_address.as_u32;
746 ip0->dst_address = s0->in2out.addr;
747 new_addr0 = ip0->dst_address.as_u32;
748 vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
750 sum0 = ip0->checksum;
751 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
753 dst_address /* changed member */);
754 ip0->checksum = ip_csum_fold (sum0);
756 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
758 old_port0 = tcp0->dst_port;
759 tcp0->dst_port = s0->in2out.port;
760 new_port0 = tcp0->dst_port;
762 sum0 = tcp0->checksum;
763 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
765 dst_address /* changed member */);
767 sum0 = ip_csum_update (sum0, old_port0, new_port0,
768 ip4_header_t /* cheat */,
769 length /* changed member */);
770 tcp0->checksum = ip_csum_fold(sum0);
774 old_port0 = udp0->dst_port;
775 udp0->dst_port = s0->in2out.port;
780 s0->last_heard = now;
782 s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
783 /* Per-user LRU list maintenance for dynamic translation */
784 if (!snat_is_session_static (s0))
786 clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
788 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
789 s0->per_user_list_head_index,
794 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
795 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
797 snat_out2in_trace_t *t =
798 vlib_add_trace (vm, node, b0, sizeof (*t));
799 t->sw_if_index = sw_if_index0;
800 t->next_index = next0;
801 t->session_index = ~0;
803 t->session_index = s0 - sm->per_thread_data[thread_index].sessions;
806 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
809 ip1 = vlib_buffer_get_current (b1);
810 udp1 = ip4_next_header (ip1);
811 tcp1 = (tcp_header_t *) udp1;
812 icmp1 = (icmp46_header_t *) udp1;
814 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
815 rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index,
818 if (PREDICT_FALSE(ip1->ttl == 1))
820 vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
821 icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
822 ICMP4_time_exceeded_ttl_exceeded_in_transit,
824 next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
828 proto1 = ip_proto_to_snat_proto (ip1->protocol);
830 if (PREDICT_FALSE (proto1 == ~0))
833 if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
835 next1 = icmp_out2in_slow_path
836 (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node,
837 next1, now, thread_index, &s1);
841 key1.addr = ip1->dst_address;
842 key1.port = udp1->dst_port;
843 key1.protocol = proto1;
844 key1.fib_index = rx_fib_index1;
846 kv1.key = key1.as_u64;
848 if (clib_bihash_search_8_8 (&sm->out2in, &kv1, &value1))
850 /* Try to match static mapping by external address and port,
851 destination address and port in packet */
852 if (snat_static_mapping_match(sm, key1, &sm1, 1))
854 b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
856 * Send DHCP packets to the ipv4 stack, or we won't
857 * be able to use dhcp client on the outside interface
859 if (proto1 != SNAT_PROTOCOL_UDP
861 != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
862 next1 = SNAT_OUT2IN_NEXT_DROP;
866 /* Create session initiated by host from external network */
867 s1 = create_session_for_static_mapping(sm, b1, sm1, key1, node,
871 b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
872 next1 = SNAT_OUT2IN_NEXT_DROP;
877 s1 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions,
880 old_addr1 = ip1->dst_address.as_u32;
881 ip1->dst_address = s1->in2out.addr;
882 new_addr1 = ip1->dst_address.as_u32;
883 vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->in2out.fib_index;
885 sum1 = ip1->checksum;
886 sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
888 dst_address /* changed member */);
889 ip1->checksum = ip_csum_fold (sum1);
891 if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
893 old_port1 = tcp1->dst_port;
894 tcp1->dst_port = s1->in2out.port;
895 new_port1 = tcp1->dst_port;
897 sum1 = tcp1->checksum;
898 sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
900 dst_address /* changed member */);
902 sum1 = ip_csum_update (sum1, old_port1, new_port1,
903 ip4_header_t /* cheat */,
904 length /* changed member */);
905 tcp1->checksum = ip_csum_fold(sum1);
909 old_port1 = udp1->dst_port;
910 udp1->dst_port = s1->in2out.port;
915 s1->last_heard = now;
917 s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
918 /* Per-user LRU list maintenance for dynamic translation */
919 if (!snat_is_session_static (s1))
921 clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
923 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
924 s1->per_user_list_head_index,
929 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
930 && (b1->flags & VLIB_BUFFER_IS_TRACED)))
932 snat_out2in_trace_t *t =
933 vlib_add_trace (vm, node, b1, sizeof (*t));
934 t->sw_if_index = sw_if_index1;
935 t->next_index = next1;
936 t->session_index = ~0;
938 t->session_index = s1 - sm->per_thread_data[thread_index].sessions;
941 pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP;
943 /* verify speculative enqueues, maybe switch current next frame */
944 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
945 to_next, n_left_to_next,
946 bi0, bi1, next0, next1);
949 while (n_left_from > 0 && n_left_to_next > 0)
953 u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
957 u32 new_addr0, old_addr0;
958 u16 new_port0, old_port0;
961 icmp46_header_t * icmp0;
962 snat_session_key_t key0, sm0;
965 snat_session_t * s0 = 0;
966 clib_bihash_kv_8_8_t kv0, value0;
968 /* speculatively enqueue b0 to the current next frame */
976 b0 = vlib_get_buffer (vm, bi0);
978 ip0 = vlib_buffer_get_current (b0);
979 udp0 = ip4_next_header (ip0);
980 tcp0 = (tcp_header_t *) udp0;
981 icmp0 = (icmp46_header_t *) udp0;
983 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
984 rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index,
987 proto0 = ip_proto_to_snat_proto (ip0->protocol);
989 if (PREDICT_FALSE (proto0 == ~0))
992 if (PREDICT_FALSE(ip0->ttl == 1))
994 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
995 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
996 ICMP4_time_exceeded_ttl_exceeded_in_transit,
998 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1002 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
1004 next0 = icmp_out2in_slow_path
1005 (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
1006 next0, now, thread_index, &s0);
1010 key0.addr = ip0->dst_address;
1011 key0.port = udp0->dst_port;
1012 key0.protocol = proto0;
1013 key0.fib_index = rx_fib_index0;
1015 kv0.key = key0.as_u64;
1017 if (clib_bihash_search_8_8 (&sm->out2in, &kv0, &value0))
1019 /* Try to match static mapping by external address and port,
1020 destination address and port in packet */
1021 if (snat_static_mapping_match(sm, key0, &sm0, 1))
1023 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1025 * Send DHCP packets to the ipv4 stack, or we won't
1026 * be able to use dhcp client on the outside interface
1028 if (proto0 != SNAT_PROTOCOL_UDP
1030 != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
1032 next0 = SNAT_OUT2IN_NEXT_DROP;
1036 /* Create session initiated by host from external network */
1037 s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node,
1041 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1042 next0 = SNAT_OUT2IN_NEXT_DROP;
1047 s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions,
1050 old_addr0 = ip0->dst_address.as_u32;
1051 ip0->dst_address = s0->in2out.addr;
1052 new_addr0 = ip0->dst_address.as_u32;
1053 vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
1055 sum0 = ip0->checksum;
1056 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1058 dst_address /* changed member */);
1059 ip0->checksum = ip_csum_fold (sum0);
1061 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1063 old_port0 = tcp0->dst_port;
1064 tcp0->dst_port = s0->in2out.port;
1065 new_port0 = tcp0->dst_port;
1067 sum0 = tcp0->checksum;
1068 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1070 dst_address /* changed member */);
1072 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1073 ip4_header_t /* cheat */,
1074 length /* changed member */);
1075 tcp0->checksum = ip_csum_fold(sum0);
1079 old_port0 = udp0->dst_port;
1080 udp0->dst_port = s0->in2out.port;
1085 s0->last_heard = now;
1087 s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
1088 /* Per-user LRU list maintenance for dynamic translation */
1089 if (!snat_is_session_static (s0))
1091 clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
1092 s0->per_user_index);
1093 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
1094 s0->per_user_list_head_index,
1095 s0->per_user_index);
1099 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1100 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1102 snat_out2in_trace_t *t =
1103 vlib_add_trace (vm, node, b0, sizeof (*t));
1104 t->sw_if_index = sw_if_index0;
1105 t->next_index = next0;
1106 t->session_index = ~0;
1108 t->session_index = s0 - sm->per_thread_data[thread_index].sessions;
1111 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
1113 /* verify speculative enqueue, maybe switch current next frame */
1114 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1115 to_next, n_left_to_next,
1119 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1122 vlib_node_increment_counter (vm, snat_out2in_node.index,
1123 SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
1125 return frame->n_vectors;
1128 VLIB_REGISTER_NODE (snat_out2in_node) = {
1129 .function = snat_out2in_node_fn,
1130 .name = "snat-out2in",
1131 .vector_size = sizeof (u32),
1132 .format_trace = format_snat_out2in_trace,
1133 .type = VLIB_NODE_TYPE_INTERNAL,
1135 .n_errors = ARRAY_LEN(snat_out2in_error_strings),
1136 .error_strings = snat_out2in_error_strings,
1138 .runtime_data_bytes = sizeof (snat_runtime_t),
1140 .n_next_nodes = SNAT_OUT2IN_N_NEXT,
1142 /* edit / add dispositions here */
1144 [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
1145 [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
1146 [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1149 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn);
1151 /**************************/
1152 /*** deterministic mode ***/
1153 /**************************/
1155 snat_det_out2in_node_fn (vlib_main_t * vm,
1156 vlib_node_runtime_t * node,
1157 vlib_frame_t * frame)
1159 u32 n_left_from, * from, * to_next;
1160 snat_out2in_next_t next_index;
1161 u32 pkts_processed = 0;
1162 snat_main_t * sm = &snat_main;
1164 from = vlib_frame_vector_args (frame);
1165 n_left_from = frame->n_vectors;
1166 next_index = node->cached_next_index;
1168 while (n_left_from > 0)
1172 vlib_get_next_frame (vm, node, next_index,
1173 to_next, n_left_to_next);
1175 while (n_left_from >= 4 && n_left_to_next >= 2)
1178 vlib_buffer_t * b0, * b1;
1179 u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
1180 u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP;
1181 u32 sw_if_index0, sw_if_index1;
1182 ip4_header_t * ip0, * ip1;
1183 ip_csum_t sum0, sum1;
1184 ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1;
1185 u16 new_port0, old_port0, old_port1, new_port1;
1186 udp_header_t * udp0, * udp1;
1187 tcp_header_t * tcp0, * tcp1;
1189 snat_det_out_key_t key0, key1;
1190 snat_det_map_t * dm0, * dm1;
1191 snat_det_session_t * ses0 = 0, * ses1 = 0;
1193 /* Prefetch next iteration. */
1195 vlib_buffer_t * p2, * p3;
1197 p2 = vlib_get_buffer (vm, from[2]);
1198 p3 = vlib_get_buffer (vm, from[3]);
1200 vlib_prefetch_buffer_header (p2, LOAD);
1201 vlib_prefetch_buffer_header (p3, LOAD);
1203 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
1204 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
1207 /* speculatively enqueue b0 and b1 to the current next frame */
1208 to_next[0] = bi0 = from[0];
1209 to_next[1] = bi1 = from[1];
1213 n_left_to_next -= 2;
1215 b0 = vlib_get_buffer (vm, bi0);
1216 b1 = vlib_get_buffer (vm, bi1);
1218 ip0 = vlib_buffer_get_current (b0);
1219 udp0 = ip4_next_header (ip0);
1220 tcp0 = (tcp_header_t *) udp0;
1222 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1224 if (PREDICT_FALSE(ip0->ttl == 1))
1226 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1227 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1228 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1230 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1234 key0.ext_host_addr = ip0->src_address;
1235 key0.ext_host_port = tcp0->src;
1236 key0.out_port = tcp0->dst;
1238 dm0 = snat_det_map_by_out(sm, &ip0->dst_address);
1239 if (PREDICT_FALSE(!dm0))
1241 clib_warning("unknown dst address: %U",
1242 format_ip4_address, &ip0->dst_address);
1243 next0 = SNAT_OUT2IN_NEXT_DROP;
1244 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1248 snat_det_reverse(dm0, &ip0->dst_address,
1249 clib_net_to_host_u16(tcp0->dst), &new_addr0);
1251 ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64);
1252 if (PREDICT_FALSE(!ses0))
1254 clib_warning("no match src %U:%d dst %U:%d for user %U",
1255 format_ip4_address, &ip0->src_address,
1256 clib_net_to_host_u16 (tcp0->src),
1257 format_ip4_address, &ip0->dst_address,
1258 clib_net_to_host_u16 (tcp0->dst),
1259 format_ip4_address, &new_addr0);
1260 next0 = SNAT_OUT2IN_NEXT_DROP;
1261 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1264 new_port0 = ses0->in_port;
1266 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1268 old_addr0 = ip0->dst_address;
1269 ip0->dst_address = new_addr0;
1270 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
1272 sum0 = ip0->checksum;
1273 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1275 dst_address /* changed member */);
1276 ip0->checksum = ip_csum_fold (sum0);
1278 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1280 if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED)
1281 ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT;
1282 else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK)
1283 snat_det_ses_close(dm0, ses0);
1285 old_port0 = tcp0->dst;
1286 tcp0->dst = new_port0;
1288 sum0 = tcp0->checksum;
1289 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1291 dst_address /* changed member */);
1293 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1294 ip4_header_t /* cheat */,
1295 length /* changed member */);
1296 tcp0->checksum = ip_csum_fold(sum0);
1300 old_port0 = udp0->dst_port;
1301 udp0->dst_port = new_port0;
1307 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1308 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1310 snat_out2in_trace_t *t =
1311 vlib_add_trace (vm, node, b0, sizeof (*t));
1312 t->sw_if_index = sw_if_index0;
1313 t->next_index = next0;
1314 t->session_index = ~0;
1316 t->session_index = ses0 - dm0->sessions;
1319 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
1321 b1 = vlib_get_buffer (vm, bi1);
1323 ip1 = vlib_buffer_get_current (b1);
1324 udp1 = ip4_next_header (ip1);
1325 tcp1 = (tcp_header_t *) udp1;
1327 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
1329 if (PREDICT_FALSE(ip1->ttl == 1))
1331 vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1332 icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
1333 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1335 next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1339 key1.ext_host_addr = ip1->src_address;
1340 key1.ext_host_port = tcp1->src;
1341 key1.out_port = tcp1->dst;
1343 dm1 = snat_det_map_by_out(sm, &ip1->dst_address);
1344 if (PREDICT_FALSE(!dm1))
1346 clib_warning("unknown dst address: %U",
1347 format_ip4_address, &ip1->dst_address);
1348 next1 = SNAT_OUT2IN_NEXT_DROP;
1349 b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1353 snat_det_reverse(dm1, &ip1->dst_address,
1354 clib_net_to_host_u16(tcp1->dst), &new_addr1);
1356 ses1 = snat_det_get_ses_by_out (dm1, &new_addr1, key1.as_u64);
1357 if (PREDICT_FALSE(!ses1))
1359 clib_warning("no match src %U:%d dst %U:%d for user %U",
1360 format_ip4_address, &ip1->src_address,
1361 clib_net_to_host_u16 (tcp1->src),
1362 format_ip4_address, &ip1->dst_address,
1363 clib_net_to_host_u16 (tcp1->dst),
1364 format_ip4_address, &new_addr1);
1365 next1 = SNAT_OUT2IN_NEXT_DROP;
1366 b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1369 new_port1 = ses1->in_port;
1371 proto1 = ip_proto_to_snat_proto (ip1->protocol);
1373 old_addr1 = ip1->dst_address;
1374 ip1->dst_address = new_addr1;
1375 vnet_buffer(b1)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
1377 sum1 = ip1->checksum;
1378 sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
1380 dst_address /* changed member */);
1381 ip1->checksum = ip_csum_fold (sum1);
1383 if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
1385 if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_ESTABLISHED)
1386 ses1->state = SNAT_SESSION_TCP_CLOSE_WAIT;
1387 else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_LAST_ACK)
1388 snat_det_ses_close(dm1, ses1);
1390 old_port1 = tcp1->dst;
1391 tcp1->dst = new_port1;
1393 sum1 = tcp1->checksum;
1394 sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
1396 dst_address /* changed member */);
1398 sum1 = ip_csum_update (sum1, old_port1, new_port1,
1399 ip4_header_t /* cheat */,
1400 length /* changed member */);
1401 tcp1->checksum = ip_csum_fold(sum1);
1405 old_port1 = udp1->dst_port;
1406 udp1->dst_port = new_port1;
1412 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1413 && (b1->flags & VLIB_BUFFER_IS_TRACED)))
1415 snat_out2in_trace_t *t =
1416 vlib_add_trace (vm, node, b1, sizeof (*t));
1417 t->sw_if_index = sw_if_index1;
1418 t->next_index = next1;
1419 t->session_index = ~0;
1421 t->session_index = ses1 - dm1->sessions;
1424 pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP;
1426 /* verify speculative enqueues, maybe switch current next frame */
1427 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1428 to_next, n_left_to_next,
1429 bi0, bi1, next0, next1);
1432 while (n_left_from > 0 && n_left_to_next > 0)
1436 u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
1440 ip4_address_t new_addr0, old_addr0;
1441 u16 new_port0, old_port0;
1442 udp_header_t * udp0;
1443 tcp_header_t * tcp0;
1445 snat_det_out_key_t key0;
1446 snat_det_map_t * dm0;
1447 snat_det_session_t * ses0 = 0;
1449 /* speculatively enqueue b0 to the current next frame */
1455 n_left_to_next -= 1;
1457 b0 = vlib_get_buffer (vm, bi0);
1459 ip0 = vlib_buffer_get_current (b0);
1460 udp0 = ip4_next_header (ip0);
1461 tcp0 = (tcp_header_t *) udp0;
1463 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1465 if (PREDICT_FALSE(ip0->ttl == 1))
1467 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1468 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1469 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1471 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1475 key0.ext_host_addr = ip0->src_address;
1476 key0.ext_host_port = tcp0->src;
1477 key0.out_port = tcp0->dst;
1479 dm0 = snat_det_map_by_out(sm, &ip0->dst_address);
1480 if (PREDICT_FALSE(!dm0))
1482 clib_warning("unknown dst address: %U",
1483 format_ip4_address, &ip0->dst_address);
1484 next0 = SNAT_OUT2IN_NEXT_DROP;
1485 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1489 snat_det_reverse(dm0, &ip0->dst_address,
1490 clib_net_to_host_u16(tcp0->dst), &new_addr0);
1492 ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64);
1493 if (PREDICT_FALSE(!ses0))
1495 clib_warning("no match src %U:%d dst %U:%d for user %U",
1496 format_ip4_address, &ip0->src_address,
1497 clib_net_to_host_u16 (tcp0->src),
1498 format_ip4_address, &ip0->dst_address,
1499 clib_net_to_host_u16 (tcp0->dst),
1500 format_ip4_address, &new_addr0);
1501 next0 = SNAT_OUT2IN_NEXT_DROP;
1502 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1505 new_port0 = ses0->in_port;
1507 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1509 old_addr0 = ip0->dst_address;
1510 ip0->dst_address = new_addr0;
1511 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
1513 sum0 = ip0->checksum;
1514 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1516 dst_address /* changed member */);
1517 ip0->checksum = ip_csum_fold (sum0);
1519 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1521 if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED)
1522 ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT;
1523 else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK)
1524 snat_det_ses_close(dm0, ses0);
1526 old_port0 = tcp0->dst;
1527 tcp0->dst = new_port0;
1529 sum0 = tcp0->checksum;
1530 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1532 dst_address /* changed member */);
1534 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1535 ip4_header_t /* cheat */,
1536 length /* changed member */);
1537 tcp0->checksum = ip_csum_fold(sum0);
1541 old_port0 = udp0->dst_port;
1542 udp0->dst_port = new_port0;
1548 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1549 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1551 snat_out2in_trace_t *t =
1552 vlib_add_trace (vm, node, b0, sizeof (*t));
1553 t->sw_if_index = sw_if_index0;
1554 t->next_index = next0;
1555 t->session_index = ~0;
1557 t->session_index = ses0 - dm0->sessions;
1560 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
1562 /* verify speculative enqueue, maybe switch current next frame */
1563 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1564 to_next, n_left_to_next,
1568 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1571 vlib_node_increment_counter (vm, snat_det_out2in_node.index,
1572 SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
1574 return frame->n_vectors;
1577 VLIB_REGISTER_NODE (snat_det_out2in_node) = {
1578 .function = snat_det_out2in_node_fn,
1579 .name = "snat-det-out2in",
1580 .vector_size = sizeof (u32),
1581 .format_trace = format_snat_out2in_trace,
1582 .type = VLIB_NODE_TYPE_INTERNAL,
1584 .n_errors = ARRAY_LEN(snat_out2in_error_strings),
1585 .error_strings = snat_out2in_error_strings,
1587 .runtime_data_bytes = sizeof (snat_runtime_t),
1589 .n_next_nodes = SNAT_OUT2IN_N_NEXT,
1591 /* edit / add dispositions here */
1593 [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
1594 [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
1595 [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1598 VLIB_NODE_FUNCTION_MULTIARCH (snat_det_out2in_node, snat_det_out2in_node_fn);
1600 /**********************/
1601 /*** worker handoff ***/
1602 /**********************/
1604 snat_out2in_worker_handoff_fn (vlib_main_t * vm,
1605 vlib_node_runtime_t * node,
1606 vlib_frame_t * frame)
1608 snat_main_t *sm = &snat_main;
1609 vlib_thread_main_t *tm = vlib_get_thread_main ();
1610 u32 n_left_from, *from, *to_next = 0;
1611 static __thread vlib_frame_queue_elt_t **handoff_queue_elt_by_worker_index;
1612 static __thread vlib_frame_queue_t **congested_handoff_queue_by_worker_index
1614 vlib_frame_queue_elt_t *hf = 0;
1615 vlib_frame_t *f = 0;
1617 u32 n_left_to_next_worker = 0, *to_next_worker = 0;
1618 u32 next_worker_index = 0;
1619 u32 current_worker_index = ~0;
1620 u32 thread_index = vlib_get_thread_index ();
1622 ASSERT (vec_len (sm->workers));
1624 if (PREDICT_FALSE (handoff_queue_elt_by_worker_index == 0))
1626 vec_validate (handoff_queue_elt_by_worker_index, tm->n_vlib_mains - 1);
1628 vec_validate_init_empty (congested_handoff_queue_by_worker_index,
1629 sm->first_worker_index + sm->num_workers - 1,
1630 (vlib_frame_queue_t *) (~0));
1633 from = vlib_frame_vector_args (frame);
1634 n_left_from = frame->n_vectors;
1636 while (n_left_from > 0)
1649 b0 = vlib_get_buffer (vm, bi0);
1651 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1652 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
1654 ip0 = vlib_buffer_get_current (b0);
1656 next_worker_index = sm->worker_out2in_cb(ip0, rx_fib_index0);
1658 if (PREDICT_FALSE (next_worker_index != thread_index))
1662 if (next_worker_index != current_worker_index)
1665 hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker;
1667 hf = vlib_get_worker_handoff_queue_elt (sm->fq_out2in_index,
1669 handoff_queue_elt_by_worker_index);
1671 n_left_to_next_worker = VLIB_FRAME_SIZE - hf->n_vectors;
1672 to_next_worker = &hf->buffer_index[hf->n_vectors];
1673 current_worker_index = next_worker_index;
1676 /* enqueue to correct worker thread */
1677 to_next_worker[0] = bi0;
1679 n_left_to_next_worker--;
1681 if (n_left_to_next_worker == 0)
1683 hf->n_vectors = VLIB_FRAME_SIZE;
1684 vlib_put_frame_queue_elt (hf);
1685 current_worker_index = ~0;
1686 handoff_queue_elt_by_worker_index[next_worker_index] = 0;
1693 /* if this is 1st frame */
1696 f = vlib_get_frame_to_node (vm, sm->out2in_node_index);
1697 to_next = vlib_frame_vector_args (f);
1705 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1706 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1708 snat_out2in_worker_handoff_trace_t *t =
1709 vlib_add_trace (vm, node, b0, sizeof (*t));
1710 t->next_worker_index = next_worker_index;
1711 t->do_handoff = do_handoff;
1716 vlib_put_frame_to_node (vm, sm->out2in_node_index, f);
1719 hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker;
1721 /* Ship frames to the worker nodes */
1722 for (i = 0; i < vec_len (handoff_queue_elt_by_worker_index); i++)
1724 if (handoff_queue_elt_by_worker_index[i])
1726 hf = handoff_queue_elt_by_worker_index[i];
1728 * It works better to let the handoff node
1729 * rate-adapt, always ship the handoff queue element.
1731 if (1 || hf->n_vectors == hf->last_n_vectors)
1733 vlib_put_frame_queue_elt (hf);
1734 handoff_queue_elt_by_worker_index[i] = 0;
1737 hf->last_n_vectors = hf->n_vectors;
1739 congested_handoff_queue_by_worker_index[i] =
1740 (vlib_frame_queue_t *) (~0);
1743 current_worker_index = ~0;
1744 return frame->n_vectors;
1747 VLIB_REGISTER_NODE (snat_out2in_worker_handoff_node) = {
1748 .function = snat_out2in_worker_handoff_fn,
1749 .name = "snat-out2in-worker-handoff",
1750 .vector_size = sizeof (u32),
1751 .format_trace = format_snat_out2in_worker_handoff_trace,
1752 .type = VLIB_NODE_TYPE_INTERNAL,
1761 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_worker_handoff_node, snat_out2in_worker_handoff_fn);
1764 snat_out2in_fast_node_fn (vlib_main_t * vm,
1765 vlib_node_runtime_t * node,
1766 vlib_frame_t * frame)
1768 u32 n_left_from, * from, * to_next;
1769 snat_out2in_next_t next_index;
1770 u32 pkts_processed = 0;
1771 snat_main_t * sm = &snat_main;
1773 from = vlib_frame_vector_args (frame);
1774 n_left_from = frame->n_vectors;
1775 next_index = node->cached_next_index;
1777 while (n_left_from > 0)
1781 vlib_get_next_frame (vm, node, next_index,
1782 to_next, n_left_to_next);
1784 while (n_left_from > 0 && n_left_to_next > 0)
1788 u32 next0 = SNAT_OUT2IN_NEXT_DROP;
1792 u32 new_addr0, old_addr0;
1793 u16 new_port0, old_port0;
1794 udp_header_t * udp0;
1795 tcp_header_t * tcp0;
1796 icmp46_header_t * icmp0;
1797 snat_session_key_t key0, sm0;
1801 /* speculatively enqueue b0 to the current next frame */
1807 n_left_to_next -= 1;
1809 b0 = vlib_get_buffer (vm, bi0);
1811 ip0 = vlib_buffer_get_current (b0);
1812 udp0 = ip4_next_header (ip0);
1813 tcp0 = (tcp_header_t *) udp0;
1814 icmp0 = (icmp46_header_t *) udp0;
1816 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1817 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
1819 vnet_feature_next (sw_if_index0, &next0, b0);
1821 if (PREDICT_FALSE(ip0->ttl == 1))
1823 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1824 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1825 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1827 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1831 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1833 if (PREDICT_FALSE (proto0 == ~0))
1836 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
1838 next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0,
1839 rx_fib_index0, node, next0, ~0, 0);
1843 key0.addr = ip0->dst_address;
1844 key0.port = udp0->dst_port;
1845 key0.fib_index = rx_fib_index0;
1847 if (snat_static_mapping_match(sm, key0, &sm0, 1))
1849 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1853 new_addr0 = sm0.addr.as_u32;
1854 new_port0 = sm0.port;
1855 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
1856 old_addr0 = ip0->dst_address.as_u32;
1857 ip0->dst_address.as_u32 = new_addr0;
1859 sum0 = ip0->checksum;
1860 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1862 dst_address /* changed member */);
1863 ip0->checksum = ip_csum_fold (sum0);
1865 if (PREDICT_FALSE(new_port0 != udp0->dst_port))
1867 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1869 old_port0 = tcp0->dst_port;
1870 tcp0->dst_port = new_port0;
1872 sum0 = tcp0->checksum;
1873 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1875 dst_address /* changed member */);
1877 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1878 ip4_header_t /* cheat */,
1879 length /* changed member */);
1880 tcp0->checksum = ip_csum_fold(sum0);
1884 old_port0 = udp0->dst_port;
1885 udp0->dst_port = new_port0;
1891 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1893 sum0 = tcp0->checksum;
1894 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1896 dst_address /* changed member */);
1898 tcp0->checksum = ip_csum_fold(sum0);
1904 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1905 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1907 snat_out2in_trace_t *t =
1908 vlib_add_trace (vm, node, b0, sizeof (*t));
1909 t->sw_if_index = sw_if_index0;
1910 t->next_index = next0;
1913 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
1915 /* verify speculative enqueue, maybe switch current next frame */
1916 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1917 to_next, n_left_to_next,
1921 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1924 vlib_node_increment_counter (vm, snat_out2in_fast_node.index,
1925 SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
1927 return frame->n_vectors;
1930 VLIB_REGISTER_NODE (snat_out2in_fast_node) = {
1931 .function = snat_out2in_fast_node_fn,
1932 .name = "snat-out2in-fast",
1933 .vector_size = sizeof (u32),
1934 .format_trace = format_snat_out2in_fast_trace,
1935 .type = VLIB_NODE_TYPE_INTERNAL,
1937 .n_errors = ARRAY_LEN(snat_out2in_error_strings),
1938 .error_strings = snat_out2in_error_strings,
1940 .runtime_data_bytes = sizeof (snat_runtime_t),
1942 .n_next_nodes = SNAT_OUT2IN_N_NEXT,
1944 /* edit / add dispositions here */
1946 [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
1947 [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
1948 [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1951 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_fast_node, snat_out2in_fast_node_fn);