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>
26 #include <nat/nat_ipfix_logging.h>
27 #include <nat/nat_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, "NAT44_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, "NAT44_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, "NAT44_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, "unsupported ICMP type") \
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 NAT main.
119 * @param b0 Vlib buffer.
120 * @param in2out In2out NAT44 session key.
121 * @param out2in Out2in NAT44 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;
142 ip0 = vlib_buffer_get_current (b0);
144 user_key.addr = in2out.addr;
145 user_key.fib_index = in2out.fib_index;
146 kv0.key = user_key.as_u64;
148 /* Ever heard of the "user" = inside ip4 address before? */
149 if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].user_hash,
152 /* no, make a new one */
153 pool_get (sm->per_thread_data[thread_index].users, u);
154 memset (u, 0, sizeof (*u));
155 u->addr = in2out.addr;
156 u->fib_index = in2out.fib_index;
158 pool_get (sm->per_thread_data[thread_index].list_pool,
159 per_user_list_head_elt);
161 u->sessions_per_user_list_head_index = per_user_list_head_elt -
162 sm->per_thread_data[thread_index].list_pool;
164 clib_dlist_init (sm->per_thread_data[thread_index].list_pool,
165 u->sessions_per_user_list_head_index);
167 kv0.value = u - sm->per_thread_data[thread_index].users;
170 clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].user_hash,
171 &kv0, 1 /* is_add */);
173 /* add non-traslated packets worker lookup */
174 kv0.value = thread_index;
175 clib_bihash_add_del_8_8 (&sm->worker_by_in, &kv0, 1);
179 u = pool_elt_at_index (sm->per_thread_data[thread_index].users,
183 pool_get (sm->per_thread_data[thread_index].sessions, s);
184 memset (s, 0, sizeof (*s));
186 s->outside_address_index = ~0;
187 s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
188 s->ext_host_addr.as_u32 = ip0->dst_address.as_u32;
189 u->nstaticsessions++;
191 /* Create list elts */
192 pool_get (sm->per_thread_data[thread_index].list_pool,
193 per_user_translation_list_elt);
194 clib_dlist_init (sm->per_thread_data[thread_index].list_pool,
195 per_user_translation_list_elt -
196 sm->per_thread_data[thread_index].list_pool);
198 per_user_translation_list_elt->value =
199 s - sm->per_thread_data[thread_index].sessions;
201 per_user_translation_list_elt - sm->per_thread_data[thread_index].list_pool;
202 s->per_user_list_head_index = u->sessions_per_user_list_head_index;
204 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
205 s->per_user_list_head_index,
206 per_user_translation_list_elt -
207 sm->per_thread_data[thread_index].list_pool);
211 s->in2out.protocol = out2in.protocol;
213 /* Add to translation hashes */
214 kv0.key = s->in2out.as_u64;
215 kv0.value = s - sm->per_thread_data[thread_index].sessions;
216 if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].in2out, &kv0,
218 clib_warning ("in2out key add failed");
220 kv0.key = s->out2in.as_u64;
221 kv0.value = s - sm->per_thread_data[thread_index].sessions;
223 if (clib_bihash_add_del_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0,
225 clib_warning ("out2in key add failed");
228 snat_ipfix_logging_nat44_ses_create(s->in2out.addr.as_u32,
229 s->out2in.addr.as_u32,
233 s->in2out.fib_index);
238 snat_out2in_error_t icmp_get_key(ip4_header_t *ip0,
239 snat_session_key_t *p_key0)
241 icmp46_header_t *icmp0;
242 snat_session_key_t key0;
243 icmp_echo_header_t *echo0, *inner_echo0 = 0;
244 ip4_header_t *inner_ip0;
246 icmp46_header_t *inner_icmp0;
248 icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
249 echo0 = (icmp_echo_header_t *)(icmp0+1);
251 if (!icmp_is_error_message (icmp0))
253 key0.protocol = SNAT_PROTOCOL_ICMP;
254 key0.addr = ip0->dst_address;
255 key0.port = echo0->identifier;
259 inner_ip0 = (ip4_header_t *)(echo0+1);
260 l4_header = ip4_next_header (inner_ip0);
261 key0.protocol = ip_proto_to_snat_proto (inner_ip0->protocol);
262 key0.addr = inner_ip0->src_address;
263 switch (key0.protocol)
265 case SNAT_PROTOCOL_ICMP:
266 inner_icmp0 = (icmp46_header_t*)l4_header;
267 inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1);
268 key0.port = inner_echo0->identifier;
270 case SNAT_PROTOCOL_UDP:
271 case SNAT_PROTOCOL_TCP:
272 key0.port = ((tcp_udp_header_t*)l4_header)->src_port;
275 return SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL;
279 return -1; /* success */
283 * Get address and port values to be used for ICMP packet translation
284 * and create session if needed
286 * @param[in,out] sm NAT main
287 * @param[in,out] node NAT node runtime
288 * @param[in] thread_index thread index
289 * @param[in,out] b0 buffer containing packet to be translated
290 * @param[out] p_proto protocol used for matching
291 * @param[out] p_value address and port after NAT translation
292 * @param[out] p_dont_translate if packet should not be translated
293 * @param d optional parameter
294 * @param e optional parameter
296 u32 icmp_match_out2in_slow(snat_main_t *sm, vlib_node_runtime_t *node,
297 u32 thread_index, vlib_buffer_t *b0, u8 *p_proto,
298 snat_session_key_t *p_value,
299 u8 *p_dont_translate, void *d, void *e)
302 icmp46_header_t *icmp0;
305 snat_session_key_t key0;
306 snat_session_key_t sm0;
307 snat_session_t *s0 = 0;
308 u8 dont_translate = 0;
309 clib_bihash_kv_8_8_t kv0, value0;
314 ip0 = vlib_buffer_get_current (b0);
315 icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
316 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
317 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
321 err = icmp_get_key (ip0, &key0);
324 b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
325 next0 = SNAT_OUT2IN_NEXT_DROP;
328 key0.fib_index = rx_fib_index0;
330 kv0.key = key0.as_u64;
332 if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in, &kv0,
335 /* Try to match static mapping by external address and port,
336 destination address and port in packet */
337 if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only))
339 /* Don't NAT packet aimed at the intfc address */
340 if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0,
341 ip0->dst_address.as_u32)))
346 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
347 next0 = SNAT_OUT2IN_NEXT_DROP;
351 if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply &&
352 (icmp0->type != ICMP4_echo_request || !is_addr_only)))
354 b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE];
355 next0 = SNAT_OUT2IN_NEXT_DROP;
359 /* Create session initiated by host from external network */
360 s0 = create_session_for_static_mapping(sm, b0, sm0, key0,
365 next0 = SNAT_OUT2IN_NEXT_DROP;
371 if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply &&
372 icmp0->type != ICMP4_echo_request &&
373 !icmp_is_error_message (icmp0)))
375 b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE];
376 next0 = SNAT_OUT2IN_NEXT_DROP;
380 s0 = pool_elt_at_index (sm->per_thread_data[thread_index].sessions,
385 *p_proto = key0.protocol;
387 *p_value = s0->in2out;
388 *p_dont_translate = dont_translate;
390 *(snat_session_t**)d = s0;
395 * Get address and port values to be used for ICMP packet translation
397 * @param[in] sm NAT main
398 * @param[in,out] node NAT node runtime
399 * @param[in] thread_index thread index
400 * @param[in,out] b0 buffer containing packet to be translated
401 * @param[out] p_proto protocol used for matching
402 * @param[out] p_value address and port after NAT translation
403 * @param[out] p_dont_translate if packet should not be translated
404 * @param d optional parameter
405 * @param e optional parameter
407 u32 icmp_match_out2in_fast(snat_main_t *sm, vlib_node_runtime_t *node,
408 u32 thread_index, vlib_buffer_t *b0, u8 *p_proto,
409 snat_session_key_t *p_value,
410 u8 *p_dont_translate, void *d, void *e)
413 icmp46_header_t *icmp0;
416 snat_session_key_t key0;
417 snat_session_key_t sm0;
418 u8 dont_translate = 0;
423 ip0 = vlib_buffer_get_current (b0);
424 icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
425 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
426 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
428 err = icmp_get_key (ip0, &key0);
431 b0->error = node->errors[err];
432 next0 = SNAT_OUT2IN_NEXT_DROP;
435 key0.fib_index = rx_fib_index0;
437 if (snat_static_mapping_match(sm, key0, &sm0, 1, &is_addr_only))
439 /* Don't NAT packet aimed at the intfc address */
440 if (is_interface_addr(sm, node, sw_if_index0, ip0->dst_address.as_u32))
445 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
446 next0 = SNAT_OUT2IN_NEXT_DROP;
450 if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply &&
451 (icmp0->type != ICMP4_echo_request || !is_addr_only) &&
452 !icmp_is_error_message (icmp0)))
454 b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE];
455 next0 = SNAT_OUT2IN_NEXT_DROP;
462 *p_proto = key0.protocol;
463 *p_dont_translate = dont_translate;
467 static inline u32 icmp_out2in (snat_main_t *sm,
470 icmp46_header_t * icmp0,
473 vlib_node_runtime_t * node,
479 snat_session_key_t sm0;
481 icmp_echo_header_t *echo0, *inner_echo0 = 0;
482 ip4_header_t *inner_ip0 = 0;
484 icmp46_header_t *inner_icmp0;
486 u32 new_addr0, old_addr0;
487 u16 old_id0, new_id0;
492 echo0 = (icmp_echo_header_t *)(icmp0+1);
494 next0_tmp = sm->icmp_match_out2in_cb(sm, node, thread_index, b0,
495 &protocol, &sm0, &dont_translate, d, e);
498 if (next0 == SNAT_OUT2IN_NEXT_DROP || dont_translate)
501 sum0 = ip_incremental_checksum (0, icmp0,
502 ntohs(ip0->length) - ip4_header_bytes (ip0));
503 checksum0 = ~ip_csum_fold (sum0);
504 if (checksum0 != 0 && checksum0 != 0xffff)
506 next0 = SNAT_OUT2IN_NEXT_DROP;
510 old_addr0 = ip0->dst_address.as_u32;
511 new_addr0 = ip0->dst_address.as_u32 = sm0.addr.as_u32;
512 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
514 sum0 = ip0->checksum;
515 sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
516 dst_address /* changed member */);
517 ip0->checksum = ip_csum_fold (sum0);
519 if (!icmp_is_error_message (icmp0))
522 if (PREDICT_FALSE(new_id0 != echo0->identifier))
524 old_id0 = echo0->identifier;
526 echo0->identifier = new_id0;
528 sum0 = icmp0->checksum;
529 sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
530 identifier /* changed member */);
531 icmp0->checksum = ip_csum_fold (sum0);
536 inner_ip0 = (ip4_header_t *)(echo0+1);
537 l4_header = ip4_next_header (inner_ip0);
539 if (!ip4_header_checksum_is_valid (inner_ip0))
541 next0 = SNAT_OUT2IN_NEXT_DROP;
545 old_addr0 = inner_ip0->src_address.as_u32;
546 inner_ip0->src_address = sm0.addr;
547 new_addr0 = inner_ip0->src_address.as_u32;
549 sum0 = icmp0->checksum;
550 sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
551 src_address /* changed member */);
552 icmp0->checksum = ip_csum_fold (sum0);
556 case SNAT_PROTOCOL_ICMP:
557 inner_icmp0 = (icmp46_header_t*)l4_header;
558 inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1);
560 old_id0 = inner_echo0->identifier;
562 inner_echo0->identifier = new_id0;
564 sum0 = icmp0->checksum;
565 sum0 = ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
567 icmp0->checksum = ip_csum_fold (sum0);
569 case SNAT_PROTOCOL_UDP:
570 case SNAT_PROTOCOL_TCP:
571 old_id0 = ((tcp_udp_header_t*)l4_header)->src_port;
573 ((tcp_udp_header_t*)l4_header)->src_port = new_id0;
575 sum0 = icmp0->checksum;
576 sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t,
578 icmp0->checksum = ip_csum_fold (sum0);
590 static inline u32 icmp_out2in_slow_path (snat_main_t *sm,
593 icmp46_header_t * icmp0,
596 vlib_node_runtime_t * node,
599 snat_session_t ** p_s0)
601 next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
602 next0, thread_index, p_s0, 0);
603 snat_session_t * s0 = *p_s0;
604 if (PREDICT_TRUE(next0 != SNAT_OUT2IN_NEXT_DROP && s0))
607 s0->last_heard = now;
609 s0->total_bytes += vlib_buffer_length_in_chain (sm->vlib_main, b0);
610 /* Per-user LRU list maintenance for dynamic translation */
611 if (!snat_is_session_static (s0))
613 clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
615 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
616 s0->per_user_list_head_index,
624 snat_out2in_unknown_proto (snat_main_t *sm,
632 clib_bihash_kv_8_8_t kv, value;
633 clib_bihash_kv_16_8_t s_kv, s_value;
634 snat_static_mapping_t *m;
635 snat_session_key_t m_key;
636 u32 old_addr, new_addr;
638 nat_ed_ses_key_t key;
640 snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
641 snat_user_key_t u_key;
643 dlist_elt_t *head, *elt;
645 old_addr = ip->dst_address.as_u32;
647 key.l_addr = ip->dst_address;
648 key.r_addr = ip->src_address;
649 key.fib_index = rx_fib_index;
650 key.proto = ip->protocol;
653 s_kv.key[0] = key.as_u64[0];
654 s_kv.key[1] = key.as_u64[1];
656 if (!clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value))
658 s = pool_elt_at_index (tsm->sessions, s_value.value);
659 new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32;
663 m_key.addr = ip->dst_address;
666 m_key.fib_index = rx_fib_index;
667 kv.key = m_key.as_u64;
668 if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
671 m = pool_elt_at_index (sm->static_mappings, value.value);
673 new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
675 u_key.addr = ip->src_address;
676 u_key.fib_index = m->fib_index;
677 kv.key = u_key.as_u64;
679 /* Ever heard of the "user" = src ip4 address before? */
680 if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
682 /* no, make a new one */
683 pool_get (tsm->users, u);
684 memset (u, 0, sizeof (*u));
685 u->addr = ip->src_address;
686 u->fib_index = rx_fib_index;
688 pool_get (tsm->list_pool, head);
689 u->sessions_per_user_list_head_index = head - tsm->list_pool;
691 clib_dlist_init (tsm->list_pool,
692 u->sessions_per_user_list_head_index);
694 kv.value = u - tsm->users;
697 clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1);
701 u = pool_elt_at_index (tsm->users, value.value);
704 /* Create a new session */
705 pool_get (tsm->sessions, s);
706 memset (s, 0, sizeof (*s));
708 s->ext_host_addr.as_u32 = ip->src_address.as_u32;
709 s->flags |= SNAT_SESSION_FLAG_UNKNOWN_PROTO;
710 s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
711 s->outside_address_index = ~0;
712 s->out2in.addr.as_u32 = old_addr;
713 s->out2in.fib_index = rx_fib_index;
714 s->in2out.addr.as_u32 = new_addr;
715 s->in2out.fib_index = m->fib_index;
716 s->in2out.port = s->out2in.port = ip->protocol;
717 u->nstaticsessions++;
719 /* Create list elts */
720 pool_get (tsm->list_pool, elt);
721 clib_dlist_init (tsm->list_pool, elt - tsm->list_pool);
722 elt->value = s - tsm->sessions;
723 s->per_user_index = elt - tsm->list_pool;
724 s->per_user_list_head_index = u->sessions_per_user_list_head_index;
725 clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index,
728 /* Add to lookup tables */
729 s_kv.value = s - tsm->sessions;
730 if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1))
731 clib_warning ("out2in key add failed");
733 key.l_addr = ip->dst_address;
734 key.fib_index = m->fib_index;
735 s_kv.key[0] = key.as_u64[0];
736 s_kv.key[1] = key.as_u64[1];
737 if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 1))
738 clib_warning ("in2out key add failed");
741 /* Update IP checksum */
743 sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
744 ip->checksum = ip_csum_fold (sum);
746 vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index;
751 s->total_bytes += vlib_buffer_length_in_chain (vm, b);
752 /* Per-user LRU list maintenance */
753 clib_dlist_remove (tsm->list_pool, s->per_user_index);
754 clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index,
758 static snat_session_t *
759 snat_out2in_lb (snat_main_t *sm,
767 nat_ed_ses_key_t key;
768 clib_bihash_kv_16_8_t s_kv, s_value;
769 udp_header_t *udp = ip4_next_header (ip);
770 tcp_header_t *tcp = (tcp_header_t *) udp;
771 snat_session_t *s = 0;
772 snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
773 snat_session_key_t e_key, l_key;
774 clib_bihash_kv_8_8_t kv, value;
775 u32 old_addr, new_addr;
776 u32 proto = ip_proto_to_snat_proto (ip->protocol);
777 u16 new_port, old_port;
779 snat_user_key_t u_key;
781 dlist_elt_t *head, *elt;
783 old_addr = ip->dst_address.as_u32;
785 key.l_addr = ip->dst_address;
786 key.r_addr = ip->src_address;
787 key.fib_index = rx_fib_index;
788 key.proto = ip->protocol;
790 key.l_port = udp->dst_port;
791 s_kv.key[0] = key.as_u64[0];
792 s_kv.key[1] = key.as_u64[1];
794 if (!clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value))
796 s = pool_elt_at_index (tsm->sessions, s_value.value);
800 e_key.addr = ip->dst_address;
801 e_key.port = udp->dst_port;
802 e_key.protocol = proto;
803 e_key.fib_index = rx_fib_index;
804 if (snat_static_mapping_match(sm, e_key, &l_key, 1, 0))
807 u_key.addr = l_key.addr;
808 u_key.fib_index = l_key.fib_index;
809 kv.key = u_key.as_u64;
811 /* Ever heard of the "user" = src ip4 address before? */
812 if (clib_bihash_search_8_8 (&tsm->user_hash, &kv, &value))
814 /* no, make a new one */
815 pool_get (tsm->users, u);
816 memset (u, 0, sizeof (*u));
817 u->addr = l_key.addr;
818 u->fib_index = l_key.fib_index;
820 pool_get (tsm->list_pool, head);
821 u->sessions_per_user_list_head_index = head - tsm->list_pool;
823 clib_dlist_init (tsm->list_pool,
824 u->sessions_per_user_list_head_index);
826 kv.value = u - tsm->users;
829 if (clib_bihash_add_del_8_8 (&tsm->user_hash, &kv, 1))
830 clib_warning ("user key add failed");
834 u = pool_elt_at_index (tsm->users, value.value);
837 /* Create a new session */
838 pool_get (tsm->sessions, s);
839 memset (s, 0, sizeof (*s));
841 s->ext_host_addr.as_u32 = ip->src_address.as_u32;
842 s->flags |= SNAT_SESSION_FLAG_STATIC_MAPPING;
843 s->flags |= SNAT_SESSION_FLAG_LOAD_BALANCING;
844 s->outside_address_index = ~0;
847 u->nstaticsessions++;
849 /* Create list elts */
850 pool_get (tsm->list_pool, elt);
851 clib_dlist_init (tsm->list_pool, elt - tsm->list_pool);
852 elt->value = s - tsm->sessions;
853 s->per_user_index = elt - tsm->list_pool;
854 s->per_user_list_head_index = u->sessions_per_user_list_head_index;
855 clib_dlist_addtail (tsm->list_pool, s->per_user_list_head_index,
858 /* Add to lookup tables */
859 s_kv.value = s - tsm->sessions;
860 if (clib_bihash_add_del_16_8 (&sm->out2in_ed, &s_kv, 1))
861 clib_warning ("out2in-ed key add failed");
863 key.l_addr = l_key.addr;
864 key.fib_index = l_key.fib_index;
865 key.l_port = l_key.port;
866 s_kv.key[0] = key.as_u64[0];
867 s_kv.key[1] = key.as_u64[1];
868 if (clib_bihash_add_del_16_8 (&sm->in2out_ed, &s_kv, 1))
869 clib_warning ("in2out-ed key add failed");
872 new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32;
874 /* Update IP checksum */
876 sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
877 ip->checksum = ip_csum_fold (sum);
879 if (PREDICT_TRUE(proto == SNAT_PROTOCOL_TCP))
881 old_port = tcp->dst_port;
882 tcp->dst_port = s->in2out.port;
883 new_port = tcp->dst_port;
886 sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
887 sum = ip_csum_update (sum, old_port, new_port, ip4_header_t, length);
888 tcp->checksum = ip_csum_fold(sum);
892 udp->dst_port = s->in2out.port;
896 vnet_buffer(b)->sw_if_index[VLIB_TX] = s->in2out.fib_index;
901 s->total_bytes += vlib_buffer_length_in_chain (vm, b);
906 snat_out2in_node_fn (vlib_main_t * vm,
907 vlib_node_runtime_t * node,
908 vlib_frame_t * frame)
910 u32 n_left_from, * from, * to_next;
911 snat_out2in_next_t next_index;
912 u32 pkts_processed = 0;
913 snat_main_t * sm = &snat_main;
914 f64 now = vlib_time_now (vm);
915 u32 thread_index = vlib_get_thread_index ();
917 from = vlib_frame_vector_args (frame);
918 n_left_from = frame->n_vectors;
919 next_index = node->cached_next_index;
921 while (n_left_from > 0)
925 vlib_get_next_frame (vm, node, next_index,
926 to_next, n_left_to_next);
928 while (n_left_from >= 4 && n_left_to_next >= 2)
931 vlib_buffer_t * b0, * b1;
932 u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
933 u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP;
934 u32 sw_if_index0, sw_if_index1;
935 ip4_header_t * ip0, *ip1;
936 ip_csum_t sum0, sum1;
937 u32 new_addr0, old_addr0;
938 u16 new_port0, old_port0;
939 u32 new_addr1, old_addr1;
940 u16 new_port1, old_port1;
941 udp_header_t * udp0, * udp1;
942 tcp_header_t * tcp0, * tcp1;
943 icmp46_header_t * icmp0, * icmp1;
944 snat_session_key_t key0, key1, sm0, sm1;
945 u32 rx_fib_index0, rx_fib_index1;
947 snat_session_t * s0 = 0, * s1 = 0;
948 clib_bihash_kv_8_8_t kv0, kv1, value0, value1;
950 /* Prefetch next iteration. */
952 vlib_buffer_t * p2, * p3;
954 p2 = vlib_get_buffer (vm, from[2]);
955 p3 = vlib_get_buffer (vm, from[3]);
957 vlib_prefetch_buffer_header (p2, LOAD);
958 vlib_prefetch_buffer_header (p3, LOAD);
960 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
961 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
964 /* speculatively enqueue b0 and b1 to the current next frame */
965 to_next[0] = bi0 = from[0];
966 to_next[1] = bi1 = from[1];
972 b0 = vlib_get_buffer (vm, bi0);
973 b1 = vlib_get_buffer (vm, bi1);
975 vnet_buffer (b0)->snat.flags = 0;
976 vnet_buffer (b1)->snat.flags = 0;
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 if (PREDICT_FALSE(ip0->ttl == 1))
989 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
990 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
991 ICMP4_time_exceeded_ttl_exceeded_in_transit,
993 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
997 proto0 = ip_proto_to_snat_proto (ip0->protocol);
999 if (PREDICT_FALSE (proto0 == ~0))
1001 snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0,
1002 thread_index, now, vm);
1006 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
1008 next0 = icmp_out2in_slow_path
1009 (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
1010 next0, now, thread_index, &s0);
1014 key0.addr = ip0->dst_address;
1015 key0.port = udp0->dst_port;
1016 key0.protocol = proto0;
1017 key0.fib_index = rx_fib_index0;
1019 kv0.key = key0.as_u64;
1021 if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in,
1024 /* Try to match static mapping by external address and port,
1025 destination address and port in packet */
1026 if (snat_static_mapping_match(sm, key0, &sm0, 1, 0))
1028 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1030 * Send DHCP packets to the ipv4 stack, or we won't
1031 * be able to use dhcp client on the outside interface
1033 if (proto0 != SNAT_PROTOCOL_UDP
1035 != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
1036 next0 = SNAT_OUT2IN_NEXT_DROP;
1040 /* Create session initiated by host from external network */
1041 s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node,
1045 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1046 next0 = SNAT_OUT2IN_NEXT_DROP;
1052 if (PREDICT_FALSE (value0.value == ~0ULL))
1054 s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, now,
1060 s0 = pool_elt_at_index (
1061 sm->per_thread_data[thread_index].sessions,
1066 old_addr0 = ip0->dst_address.as_u32;
1067 ip0->dst_address = s0->in2out.addr;
1068 new_addr0 = ip0->dst_address.as_u32;
1069 vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
1071 sum0 = ip0->checksum;
1072 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1074 dst_address /* changed member */);
1075 ip0->checksum = ip_csum_fold (sum0);
1077 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1079 old_port0 = tcp0->dst_port;
1080 tcp0->dst_port = s0->in2out.port;
1081 new_port0 = tcp0->dst_port;
1083 sum0 = tcp0->checksum;
1084 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1086 dst_address /* changed member */);
1088 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1089 ip4_header_t /* cheat */,
1090 length /* changed member */);
1091 tcp0->checksum = ip_csum_fold(sum0);
1095 old_port0 = udp0->dst_port;
1096 udp0->dst_port = s0->in2out.port;
1101 s0->last_heard = now;
1103 s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
1104 /* Per-user LRU list maintenance for dynamic translation */
1105 if (!snat_is_session_static (s0))
1107 clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
1108 s0->per_user_index);
1109 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
1110 s0->per_user_list_head_index,
1111 s0->per_user_index);
1115 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1116 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1118 snat_out2in_trace_t *t =
1119 vlib_add_trace (vm, node, b0, sizeof (*t));
1120 t->sw_if_index = sw_if_index0;
1121 t->next_index = next0;
1122 t->session_index = ~0;
1124 t->session_index = s0 - sm->per_thread_data[thread_index].sessions;
1127 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
1130 ip1 = vlib_buffer_get_current (b1);
1131 udp1 = ip4_next_header (ip1);
1132 tcp1 = (tcp_header_t *) udp1;
1133 icmp1 = (icmp46_header_t *) udp1;
1135 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
1136 rx_fib_index1 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index,
1139 if (PREDICT_FALSE(ip1->ttl == 1))
1141 vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1142 icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
1143 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1145 next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1149 proto1 = ip_proto_to_snat_proto (ip1->protocol);
1151 if (PREDICT_FALSE (proto1 == ~0))
1153 snat_out2in_unknown_proto(sm, b1, ip1, rx_fib_index1,
1154 thread_index, now, vm);
1158 if (PREDICT_FALSE (proto1 == SNAT_PROTOCOL_ICMP))
1160 next1 = icmp_out2in_slow_path
1161 (sm, b1, ip1, icmp1, sw_if_index1, rx_fib_index1, node,
1162 next1, now, thread_index, &s1);
1166 key1.addr = ip1->dst_address;
1167 key1.port = udp1->dst_port;
1168 key1.protocol = proto1;
1169 key1.fib_index = rx_fib_index1;
1171 kv1.key = key1.as_u64;
1173 if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in,
1176 /* Try to match static mapping by external address and port,
1177 destination address and port in packet */
1178 if (snat_static_mapping_match(sm, key1, &sm1, 1, 0))
1180 b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1182 * Send DHCP packets to the ipv4 stack, or we won't
1183 * be able to use dhcp client on the outside interface
1185 if (proto1 != SNAT_PROTOCOL_UDP
1187 != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
1188 next1 = SNAT_OUT2IN_NEXT_DROP;
1192 /* Create session initiated by host from external network */
1193 s1 = create_session_for_static_mapping(sm, b1, sm1, key1, node,
1197 b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1198 next1 = SNAT_OUT2IN_NEXT_DROP;
1204 if (PREDICT_FALSE (value1.value == ~0ULL))
1206 s1 = snat_out2in_lb(sm, b1, ip1, rx_fib_index1, thread_index, now,
1212 s1 = pool_elt_at_index (
1213 sm->per_thread_data[thread_index].sessions,
1218 old_addr1 = ip1->dst_address.as_u32;
1219 ip1->dst_address = s1->in2out.addr;
1220 new_addr1 = ip1->dst_address.as_u32;
1221 vnet_buffer(b1)->sw_if_index[VLIB_TX] = s1->in2out.fib_index;
1223 sum1 = ip1->checksum;
1224 sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
1226 dst_address /* changed member */);
1227 ip1->checksum = ip_csum_fold (sum1);
1229 if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
1231 old_port1 = tcp1->dst_port;
1232 tcp1->dst_port = s1->in2out.port;
1233 new_port1 = tcp1->dst_port;
1235 sum1 = tcp1->checksum;
1236 sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
1238 dst_address /* changed member */);
1240 sum1 = ip_csum_update (sum1, old_port1, new_port1,
1241 ip4_header_t /* cheat */,
1242 length /* changed member */);
1243 tcp1->checksum = ip_csum_fold(sum1);
1247 old_port1 = udp1->dst_port;
1248 udp1->dst_port = s1->in2out.port;
1253 s1->last_heard = now;
1255 s1->total_bytes += vlib_buffer_length_in_chain (vm, b1);
1256 /* Per-user LRU list maintenance for dynamic translation */
1257 if (!snat_is_session_static (s1))
1259 clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
1260 s1->per_user_index);
1261 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
1262 s1->per_user_list_head_index,
1263 s1->per_user_index);
1267 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1268 && (b1->flags & VLIB_BUFFER_IS_TRACED)))
1270 snat_out2in_trace_t *t =
1271 vlib_add_trace (vm, node, b1, sizeof (*t));
1272 t->sw_if_index = sw_if_index1;
1273 t->next_index = next1;
1274 t->session_index = ~0;
1276 t->session_index = s1 - sm->per_thread_data[thread_index].sessions;
1279 pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP;
1281 /* verify speculative enqueues, maybe switch current next frame */
1282 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1283 to_next, n_left_to_next,
1284 bi0, bi1, next0, next1);
1287 while (n_left_from > 0 && n_left_to_next > 0)
1291 u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
1295 u32 new_addr0, old_addr0;
1296 u16 new_port0, old_port0;
1297 udp_header_t * udp0;
1298 tcp_header_t * tcp0;
1299 icmp46_header_t * icmp0;
1300 snat_session_key_t key0, sm0;
1303 snat_session_t * s0 = 0;
1304 clib_bihash_kv_8_8_t kv0, value0;
1306 /* speculatively enqueue b0 to the current next frame */
1312 n_left_to_next -= 1;
1314 b0 = vlib_get_buffer (vm, bi0);
1316 vnet_buffer (b0)->snat.flags = 0;
1318 ip0 = vlib_buffer_get_current (b0);
1319 udp0 = ip4_next_header (ip0);
1320 tcp0 = (tcp_header_t *) udp0;
1321 icmp0 = (icmp46_header_t *) udp0;
1323 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1324 rx_fib_index0 = vec_elt (sm->ip4_main->fib_index_by_sw_if_index,
1327 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1329 if (PREDICT_FALSE (proto0 == ~0))
1331 snat_out2in_unknown_proto(sm, b0, ip0, rx_fib_index0,
1332 thread_index, now, vm);
1336 if (PREDICT_FALSE(ip0->ttl == 1))
1338 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1339 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1340 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1342 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1346 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
1348 next0 = icmp_out2in_slow_path
1349 (sm, b0, ip0, icmp0, sw_if_index0, rx_fib_index0, node,
1350 next0, now, thread_index, &s0);
1354 key0.addr = ip0->dst_address;
1355 key0.port = udp0->dst_port;
1356 key0.protocol = proto0;
1357 key0.fib_index = rx_fib_index0;
1359 kv0.key = key0.as_u64;
1361 if (clib_bihash_search_8_8 (&sm->per_thread_data[thread_index].out2in,
1364 /* Try to match static mapping by external address and port,
1365 destination address and port in packet */
1366 if (snat_static_mapping_match(sm, key0, &sm0, 1, 0))
1368 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1370 * Send DHCP packets to the ipv4 stack, or we won't
1371 * be able to use dhcp client on the outside interface
1373 if (proto0 != SNAT_PROTOCOL_UDP
1375 != clib_host_to_net_u16(UDP_DST_PORT_dhcp_to_client)))
1377 next0 = SNAT_OUT2IN_NEXT_DROP;
1381 /* Create session initiated by host from external network */
1382 s0 = create_session_for_static_mapping(sm, b0, sm0, key0, node,
1386 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1387 next0 = SNAT_OUT2IN_NEXT_DROP;
1393 if (PREDICT_FALSE (value0.value == ~0ULL))
1395 s0 = snat_out2in_lb(sm, b0, ip0, rx_fib_index0, thread_index, now,
1401 s0 = pool_elt_at_index (
1402 sm->per_thread_data[thread_index].sessions,
1407 old_addr0 = ip0->dst_address.as_u32;
1408 ip0->dst_address = s0->in2out.addr;
1409 new_addr0 = ip0->dst_address.as_u32;
1410 vnet_buffer(b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
1412 sum0 = ip0->checksum;
1413 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1415 dst_address /* changed member */);
1416 ip0->checksum = ip_csum_fold (sum0);
1418 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1420 old_port0 = tcp0->dst_port;
1421 tcp0->dst_port = s0->in2out.port;
1422 new_port0 = tcp0->dst_port;
1424 sum0 = tcp0->checksum;
1425 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1427 dst_address /* changed member */);
1429 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1430 ip4_header_t /* cheat */,
1431 length /* changed member */);
1432 tcp0->checksum = ip_csum_fold(sum0);
1436 old_port0 = udp0->dst_port;
1437 udp0->dst_port = s0->in2out.port;
1442 s0->last_heard = now;
1444 s0->total_bytes += vlib_buffer_length_in_chain (vm, b0);
1445 /* Per-user LRU list maintenance for dynamic translation */
1446 if (!snat_is_session_static (s0))
1448 clib_dlist_remove (sm->per_thread_data[thread_index].list_pool,
1449 s0->per_user_index);
1450 clib_dlist_addtail (sm->per_thread_data[thread_index].list_pool,
1451 s0->per_user_list_head_index,
1452 s0->per_user_index);
1456 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1457 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1459 snat_out2in_trace_t *t =
1460 vlib_add_trace (vm, node, b0, sizeof (*t));
1461 t->sw_if_index = sw_if_index0;
1462 t->next_index = next0;
1463 t->session_index = ~0;
1465 t->session_index = s0 - sm->per_thread_data[thread_index].sessions;
1468 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
1470 /* verify speculative enqueue, maybe switch current next frame */
1471 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1472 to_next, n_left_to_next,
1476 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1479 vlib_node_increment_counter (vm, snat_out2in_node.index,
1480 SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
1482 return frame->n_vectors;
1485 VLIB_REGISTER_NODE (snat_out2in_node) = {
1486 .function = snat_out2in_node_fn,
1487 .name = "nat44-out2in",
1488 .vector_size = sizeof (u32),
1489 .format_trace = format_snat_out2in_trace,
1490 .type = VLIB_NODE_TYPE_INTERNAL,
1492 .n_errors = ARRAY_LEN(snat_out2in_error_strings),
1493 .error_strings = snat_out2in_error_strings,
1495 .runtime_data_bytes = sizeof (snat_runtime_t),
1497 .n_next_nodes = SNAT_OUT2IN_N_NEXT,
1499 /* edit / add dispositions here */
1501 [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
1502 [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
1503 [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1506 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_node, snat_out2in_node_fn);
1508 /**************************/
1509 /*** deterministic mode ***/
1510 /**************************/
1512 snat_det_out2in_node_fn (vlib_main_t * vm,
1513 vlib_node_runtime_t * node,
1514 vlib_frame_t * frame)
1516 u32 n_left_from, * from, * to_next;
1517 snat_out2in_next_t next_index;
1518 u32 pkts_processed = 0;
1519 snat_main_t * sm = &snat_main;
1520 u32 thread_index = vlib_get_thread_index ();
1522 from = vlib_frame_vector_args (frame);
1523 n_left_from = frame->n_vectors;
1524 next_index = node->cached_next_index;
1526 while (n_left_from > 0)
1530 vlib_get_next_frame (vm, node, next_index,
1531 to_next, n_left_to_next);
1533 while (n_left_from >= 4 && n_left_to_next >= 2)
1536 vlib_buffer_t * b0, * b1;
1537 u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
1538 u32 next1 = SNAT_OUT2IN_NEXT_LOOKUP;
1539 u32 sw_if_index0, sw_if_index1;
1540 ip4_header_t * ip0, * ip1;
1541 ip_csum_t sum0, sum1;
1542 ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1;
1543 u16 new_port0, old_port0, old_port1, new_port1;
1544 udp_header_t * udp0, * udp1;
1545 tcp_header_t * tcp0, * tcp1;
1547 snat_det_out_key_t key0, key1;
1548 snat_det_map_t * dm0, * dm1;
1549 snat_det_session_t * ses0 = 0, * ses1 = 0;
1550 u32 rx_fib_index0, rx_fib_index1;
1551 icmp46_header_t * icmp0, * icmp1;
1553 /* Prefetch next iteration. */
1555 vlib_buffer_t * p2, * p3;
1557 p2 = vlib_get_buffer (vm, from[2]);
1558 p3 = vlib_get_buffer (vm, from[3]);
1560 vlib_prefetch_buffer_header (p2, LOAD);
1561 vlib_prefetch_buffer_header (p3, LOAD);
1563 CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
1564 CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, STORE);
1567 /* speculatively enqueue b0 and b1 to the current next frame */
1568 to_next[0] = bi0 = from[0];
1569 to_next[1] = bi1 = from[1];
1573 n_left_to_next -= 2;
1575 b0 = vlib_get_buffer (vm, bi0);
1576 b1 = vlib_get_buffer (vm, bi1);
1578 ip0 = vlib_buffer_get_current (b0);
1579 udp0 = ip4_next_header (ip0);
1580 tcp0 = (tcp_header_t *) udp0;
1582 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1584 if (PREDICT_FALSE(ip0->ttl == 1))
1586 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1587 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1588 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1590 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1594 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1596 if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP))
1598 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
1599 icmp0 = (icmp46_header_t *) udp0;
1601 next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0,
1602 rx_fib_index0, node, next0, thread_index,
1607 key0.ext_host_addr = ip0->src_address;
1608 key0.ext_host_port = tcp0->src;
1609 key0.out_port = tcp0->dst;
1611 dm0 = snat_det_map_by_out(sm, &ip0->dst_address);
1612 if (PREDICT_FALSE(!dm0))
1614 clib_warning("unknown dst address: %U",
1615 format_ip4_address, &ip0->dst_address);
1616 next0 = SNAT_OUT2IN_NEXT_DROP;
1617 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1621 snat_det_reverse(dm0, &ip0->dst_address,
1622 clib_net_to_host_u16(tcp0->dst), &new_addr0);
1624 ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64);
1625 if (PREDICT_FALSE(!ses0))
1627 clib_warning("no match src %U:%d dst %U:%d for user %U",
1628 format_ip4_address, &ip0->src_address,
1629 clib_net_to_host_u16 (tcp0->src),
1630 format_ip4_address, &ip0->dst_address,
1631 clib_net_to_host_u16 (tcp0->dst),
1632 format_ip4_address, &new_addr0);
1633 next0 = SNAT_OUT2IN_NEXT_DROP;
1634 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1637 new_port0 = ses0->in_port;
1639 old_addr0 = ip0->dst_address;
1640 ip0->dst_address = new_addr0;
1641 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
1643 sum0 = ip0->checksum;
1644 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1646 dst_address /* changed member */);
1647 ip0->checksum = ip_csum_fold (sum0);
1649 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1651 if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED)
1652 ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT;
1653 else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK)
1654 snat_det_ses_close(dm0, ses0);
1656 old_port0 = tcp0->dst;
1657 tcp0->dst = new_port0;
1659 sum0 = tcp0->checksum;
1660 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1662 dst_address /* changed member */);
1664 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1665 ip4_header_t /* cheat */,
1666 length /* changed member */);
1667 tcp0->checksum = ip_csum_fold(sum0);
1671 old_port0 = udp0->dst_port;
1672 udp0->dst_port = new_port0;
1678 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1679 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1681 snat_out2in_trace_t *t =
1682 vlib_add_trace (vm, node, b0, sizeof (*t));
1683 t->sw_if_index = sw_if_index0;
1684 t->next_index = next0;
1685 t->session_index = ~0;
1687 t->session_index = ses0 - dm0->sessions;
1690 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
1692 b1 = vlib_get_buffer (vm, bi1);
1694 ip1 = vlib_buffer_get_current (b1);
1695 udp1 = ip4_next_header (ip1);
1696 tcp1 = (tcp_header_t *) udp1;
1698 sw_if_index1 = vnet_buffer(b1)->sw_if_index[VLIB_RX];
1700 if (PREDICT_FALSE(ip1->ttl == 1))
1702 vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1703 icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
1704 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1706 next1 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1710 proto1 = ip_proto_to_snat_proto (ip1->protocol);
1712 if (PREDICT_FALSE(proto1 == SNAT_PROTOCOL_ICMP))
1714 rx_fib_index1 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index1);
1715 icmp1 = (icmp46_header_t *) udp1;
1717 next1 = icmp_out2in(sm, b1, ip1, icmp1, sw_if_index1,
1718 rx_fib_index1, node, next1, thread_index,
1723 key1.ext_host_addr = ip1->src_address;
1724 key1.ext_host_port = tcp1->src;
1725 key1.out_port = tcp1->dst;
1727 dm1 = snat_det_map_by_out(sm, &ip1->dst_address);
1728 if (PREDICT_FALSE(!dm1))
1730 clib_warning("unknown dst address: %U",
1731 format_ip4_address, &ip1->dst_address);
1732 next1 = SNAT_OUT2IN_NEXT_DROP;
1733 b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1737 snat_det_reverse(dm1, &ip1->dst_address,
1738 clib_net_to_host_u16(tcp1->dst), &new_addr1);
1740 ses1 = snat_det_get_ses_by_out (dm1, &new_addr1, key1.as_u64);
1741 if (PREDICT_FALSE(!ses1))
1743 clib_warning("no match src %U:%d dst %U:%d for user %U",
1744 format_ip4_address, &ip1->src_address,
1745 clib_net_to_host_u16 (tcp1->src),
1746 format_ip4_address, &ip1->dst_address,
1747 clib_net_to_host_u16 (tcp1->dst),
1748 format_ip4_address, &new_addr1);
1749 next1 = SNAT_OUT2IN_NEXT_DROP;
1750 b1->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1753 new_port1 = ses1->in_port;
1755 old_addr1 = ip1->dst_address;
1756 ip1->dst_address = new_addr1;
1757 vnet_buffer(b1)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
1759 sum1 = ip1->checksum;
1760 sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
1762 dst_address /* changed member */);
1763 ip1->checksum = ip_csum_fold (sum1);
1765 if (PREDICT_TRUE(proto1 == SNAT_PROTOCOL_TCP))
1767 if (tcp1->flags & TCP_FLAG_FIN && ses1->state == SNAT_SESSION_TCP_ESTABLISHED)
1768 ses1->state = SNAT_SESSION_TCP_CLOSE_WAIT;
1769 else if (tcp1->flags & TCP_FLAG_ACK && ses1->state == SNAT_SESSION_TCP_LAST_ACK)
1770 snat_det_ses_close(dm1, ses1);
1772 old_port1 = tcp1->dst;
1773 tcp1->dst = new_port1;
1775 sum1 = tcp1->checksum;
1776 sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
1778 dst_address /* changed member */);
1780 sum1 = ip_csum_update (sum1, old_port1, new_port1,
1781 ip4_header_t /* cheat */,
1782 length /* changed member */);
1783 tcp1->checksum = ip_csum_fold(sum1);
1787 old_port1 = udp1->dst_port;
1788 udp1->dst_port = new_port1;
1794 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1795 && (b1->flags & VLIB_BUFFER_IS_TRACED)))
1797 snat_out2in_trace_t *t =
1798 vlib_add_trace (vm, node, b1, sizeof (*t));
1799 t->sw_if_index = sw_if_index1;
1800 t->next_index = next1;
1801 t->session_index = ~0;
1803 t->session_index = ses1 - dm1->sessions;
1806 pkts_processed += next1 != SNAT_OUT2IN_NEXT_DROP;
1808 /* verify speculative enqueues, maybe switch current next frame */
1809 vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
1810 to_next, n_left_to_next,
1811 bi0, bi1, next0, next1);
1814 while (n_left_from > 0 && n_left_to_next > 0)
1818 u32 next0 = SNAT_OUT2IN_NEXT_LOOKUP;
1822 ip4_address_t new_addr0, old_addr0;
1823 u16 new_port0, old_port0;
1824 udp_header_t * udp0;
1825 tcp_header_t * tcp0;
1827 snat_det_out_key_t key0;
1828 snat_det_map_t * dm0;
1829 snat_det_session_t * ses0 = 0;
1831 icmp46_header_t * icmp0;
1833 /* speculatively enqueue b0 to the current next frame */
1839 n_left_to_next -= 1;
1841 b0 = vlib_get_buffer (vm, bi0);
1843 ip0 = vlib_buffer_get_current (b0);
1844 udp0 = ip4_next_header (ip0);
1845 tcp0 = (tcp_header_t *) udp0;
1847 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
1849 if (PREDICT_FALSE(ip0->ttl == 1))
1851 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1852 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1853 ICMP4_time_exceeded_ttl_exceeded_in_transit,
1855 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
1859 proto0 = ip_proto_to_snat_proto (ip0->protocol);
1861 if (PREDICT_FALSE(proto0 == SNAT_PROTOCOL_ICMP))
1863 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
1864 icmp0 = (icmp46_header_t *) udp0;
1866 next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0,
1867 rx_fib_index0, node, next0, thread_index,
1872 key0.ext_host_addr = ip0->src_address;
1873 key0.ext_host_port = tcp0->src;
1874 key0.out_port = tcp0->dst;
1876 dm0 = snat_det_map_by_out(sm, &ip0->dst_address);
1877 if (PREDICT_FALSE(!dm0))
1879 clib_warning("unknown dst address: %U",
1880 format_ip4_address, &ip0->dst_address);
1881 next0 = SNAT_OUT2IN_NEXT_DROP;
1882 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1886 snat_det_reverse(dm0, &ip0->dst_address,
1887 clib_net_to_host_u16(tcp0->dst), &new_addr0);
1889 ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64);
1890 if (PREDICT_FALSE(!ses0))
1892 clib_warning("no match src %U:%d dst %U:%d for user %U",
1893 format_ip4_address, &ip0->src_address,
1894 clib_net_to_host_u16 (tcp0->src),
1895 format_ip4_address, &ip0->dst_address,
1896 clib_net_to_host_u16 (tcp0->dst),
1897 format_ip4_address, &new_addr0);
1898 next0 = SNAT_OUT2IN_NEXT_DROP;
1899 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
1902 new_port0 = ses0->in_port;
1904 old_addr0 = ip0->dst_address;
1905 ip0->dst_address = new_addr0;
1906 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm->inside_fib_index;
1908 sum0 = ip0->checksum;
1909 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1911 dst_address /* changed member */);
1912 ip0->checksum = ip_csum_fold (sum0);
1914 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
1916 if (tcp0->flags & TCP_FLAG_FIN && ses0->state == SNAT_SESSION_TCP_ESTABLISHED)
1917 ses0->state = SNAT_SESSION_TCP_CLOSE_WAIT;
1918 else if (tcp0->flags & TCP_FLAG_ACK && ses0->state == SNAT_SESSION_TCP_LAST_ACK)
1919 snat_det_ses_close(dm0, ses0);
1921 old_port0 = tcp0->dst;
1922 tcp0->dst = new_port0;
1924 sum0 = tcp0->checksum;
1925 sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
1927 dst_address /* changed member */);
1929 sum0 = ip_csum_update (sum0, old_port0, new_port0,
1930 ip4_header_t /* cheat */,
1931 length /* changed member */);
1932 tcp0->checksum = ip_csum_fold(sum0);
1936 old_port0 = udp0->dst_port;
1937 udp0->dst_port = new_port0;
1943 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
1944 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1946 snat_out2in_trace_t *t =
1947 vlib_add_trace (vm, node, b0, sizeof (*t));
1948 t->sw_if_index = sw_if_index0;
1949 t->next_index = next0;
1950 t->session_index = ~0;
1952 t->session_index = ses0 - dm0->sessions;
1955 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
1957 /* verify speculative enqueue, maybe switch current next frame */
1958 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1959 to_next, n_left_to_next,
1963 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1966 vlib_node_increment_counter (vm, snat_det_out2in_node.index,
1967 SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
1969 return frame->n_vectors;
1972 VLIB_REGISTER_NODE (snat_det_out2in_node) = {
1973 .function = snat_det_out2in_node_fn,
1974 .name = "nat44-det-out2in",
1975 .vector_size = sizeof (u32),
1976 .format_trace = format_snat_out2in_trace,
1977 .type = VLIB_NODE_TYPE_INTERNAL,
1979 .n_errors = ARRAY_LEN(snat_out2in_error_strings),
1980 .error_strings = snat_out2in_error_strings,
1982 .runtime_data_bytes = sizeof (snat_runtime_t),
1984 .n_next_nodes = SNAT_OUT2IN_N_NEXT,
1986 /* edit / add dispositions here */
1988 [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
1989 [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
1990 [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1993 VLIB_NODE_FUNCTION_MULTIARCH (snat_det_out2in_node, snat_det_out2in_node_fn);
1996 * Get address and port values to be used for ICMP packet translation
1997 * and create session if needed
1999 * @param[in,out] sm NAT main
2000 * @param[in,out] node NAT node runtime
2001 * @param[in] thread_index thread index
2002 * @param[in,out] b0 buffer containing packet to be translated
2003 * @param[out] p_proto protocol used for matching
2004 * @param[out] p_value address and port after NAT translation
2005 * @param[out] p_dont_translate if packet should not be translated
2006 * @param d optional parameter
2007 * @param e optional parameter
2009 u32 icmp_match_out2in_det(snat_main_t *sm, vlib_node_runtime_t *node,
2010 u32 thread_index, vlib_buffer_t *b0, u8 *p_proto,
2011 snat_session_key_t *p_value,
2012 u8 *p_dont_translate, void *d, void *e)
2015 icmp46_header_t *icmp0;
2018 snat_det_out_key_t key0;
2019 u8 dont_translate = 0;
2021 icmp_echo_header_t *echo0, *inner_echo0 = 0;
2022 ip4_header_t *inner_ip0;
2023 void *l4_header = 0;
2024 icmp46_header_t *inner_icmp0;
2025 snat_det_map_t * dm0 = 0;
2026 ip4_address_t new_addr0 = {{0}};
2027 snat_det_session_t * ses0 = 0;
2028 ip4_address_t out_addr;
2030 ip0 = vlib_buffer_get_current (b0);
2031 icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
2032 echo0 = (icmp_echo_header_t *)(icmp0+1);
2033 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
2035 if (!icmp_is_error_message (icmp0))
2037 protocol = SNAT_PROTOCOL_ICMP;
2038 key0.ext_host_addr = ip0->src_address;
2039 key0.ext_host_port = 0;
2040 key0.out_port = echo0->identifier;
2041 out_addr = ip0->dst_address;
2045 inner_ip0 = (ip4_header_t *)(echo0+1);
2046 l4_header = ip4_next_header (inner_ip0);
2047 protocol = ip_proto_to_snat_proto (inner_ip0->protocol);
2048 key0.ext_host_addr = inner_ip0->dst_address;
2049 out_addr = inner_ip0->src_address;
2052 case SNAT_PROTOCOL_ICMP:
2053 inner_icmp0 = (icmp46_header_t*)l4_header;
2054 inner_echo0 = (icmp_echo_header_t *)(inner_icmp0+1);
2055 key0.ext_host_port = 0;
2056 key0.out_port = inner_echo0->identifier;
2058 case SNAT_PROTOCOL_UDP:
2059 case SNAT_PROTOCOL_TCP:
2060 key0.ext_host_port = ((tcp_udp_header_t*)l4_header)->dst_port;
2061 key0.out_port = ((tcp_udp_header_t*)l4_header)->src_port;
2064 b0->error = node->errors[SNAT_OUT2IN_ERROR_UNSUPPORTED_PROTOCOL];
2065 next0 = SNAT_OUT2IN_NEXT_DROP;
2070 dm0 = snat_det_map_by_out(sm, &out_addr);
2071 if (PREDICT_FALSE(!dm0))
2073 /* Don't NAT packet aimed at the intfc address */
2074 if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0,
2075 ip0->dst_address.as_u32)))
2080 clib_warning("unknown dst address: %U",
2081 format_ip4_address, &ip0->dst_address);
2085 snat_det_reverse(dm0, &ip0->dst_address,
2086 clib_net_to_host_u16(key0.out_port), &new_addr0);
2088 ses0 = snat_det_get_ses_by_out (dm0, &new_addr0, key0.as_u64);
2089 if (PREDICT_FALSE(!ses0))
2091 /* Don't NAT packet aimed at the intfc address */
2092 if (PREDICT_FALSE(is_interface_addr(sm, node, sw_if_index0,
2093 ip0->dst_address.as_u32)))
2098 clib_warning("no match src %U:%d dst %U:%d for user %U",
2099 format_ip4_address, &key0.ext_host_addr,
2100 clib_net_to_host_u16 (key0.ext_host_port),
2101 format_ip4_address, &out_addr,
2102 clib_net_to_host_u16 (key0.out_port),
2103 format_ip4_address, &new_addr0);
2104 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
2105 next0 = SNAT_OUT2IN_NEXT_DROP;
2109 if (PREDICT_FALSE(icmp0->type != ICMP4_echo_reply &&
2110 !icmp_is_error_message (icmp0)))
2112 b0->error = node->errors[SNAT_OUT2IN_ERROR_BAD_ICMP_TYPE];
2113 next0 = SNAT_OUT2IN_NEXT_DROP;
2120 *p_proto = protocol;
2123 p_value->addr = new_addr0;
2124 p_value->fib_index = sm->inside_fib_index;
2125 p_value->port = ses0->in_port;
2127 *p_dont_translate = dont_translate;
2129 *(snat_det_session_t**)d = ses0;
2131 *(snat_det_map_t**)e = dm0;
2135 /**********************/
2136 /*** worker handoff ***/
2137 /**********************/
2139 snat_out2in_worker_handoff_fn (vlib_main_t * vm,
2140 vlib_node_runtime_t * node,
2141 vlib_frame_t * frame)
2143 snat_main_t *sm = &snat_main;
2144 vlib_thread_main_t *tm = vlib_get_thread_main ();
2145 u32 n_left_from, *from, *to_next = 0;
2146 static __thread vlib_frame_queue_elt_t **handoff_queue_elt_by_worker_index;
2147 static __thread vlib_frame_queue_t **congested_handoff_queue_by_worker_index
2149 vlib_frame_queue_elt_t *hf = 0;
2150 vlib_frame_t *f = 0;
2152 u32 n_left_to_next_worker = 0, *to_next_worker = 0;
2153 u32 next_worker_index = 0;
2154 u32 current_worker_index = ~0;
2155 u32 thread_index = vlib_get_thread_index ();
2157 ASSERT (vec_len (sm->workers));
2159 if (PREDICT_FALSE (handoff_queue_elt_by_worker_index == 0))
2161 vec_validate (handoff_queue_elt_by_worker_index, tm->n_vlib_mains - 1);
2163 vec_validate_init_empty (congested_handoff_queue_by_worker_index,
2164 sm->first_worker_index + sm->num_workers - 1,
2165 (vlib_frame_queue_t *) (~0));
2168 from = vlib_frame_vector_args (frame);
2169 n_left_from = frame->n_vectors;
2171 while (n_left_from > 0)
2184 b0 = vlib_get_buffer (vm, bi0);
2186 sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
2187 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
2189 ip0 = vlib_buffer_get_current (b0);
2191 next_worker_index = sm->worker_out2in_cb(ip0, rx_fib_index0);
2193 if (PREDICT_FALSE (next_worker_index != thread_index))
2197 if (next_worker_index != current_worker_index)
2200 hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker;
2202 hf = vlib_get_worker_handoff_queue_elt (sm->fq_out2in_index,
2204 handoff_queue_elt_by_worker_index);
2206 n_left_to_next_worker = VLIB_FRAME_SIZE - hf->n_vectors;
2207 to_next_worker = &hf->buffer_index[hf->n_vectors];
2208 current_worker_index = next_worker_index;
2211 /* enqueue to correct worker thread */
2212 to_next_worker[0] = bi0;
2214 n_left_to_next_worker--;
2216 if (n_left_to_next_worker == 0)
2218 hf->n_vectors = VLIB_FRAME_SIZE;
2219 vlib_put_frame_queue_elt (hf);
2220 current_worker_index = ~0;
2221 handoff_queue_elt_by_worker_index[next_worker_index] = 0;
2228 /* if this is 1st frame */
2231 f = vlib_get_frame_to_node (vm, sm->out2in_node_index);
2232 to_next = vlib_frame_vector_args (f);
2240 if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
2241 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
2243 snat_out2in_worker_handoff_trace_t *t =
2244 vlib_add_trace (vm, node, b0, sizeof (*t));
2245 t->next_worker_index = next_worker_index;
2246 t->do_handoff = do_handoff;
2251 vlib_put_frame_to_node (vm, sm->out2in_node_index, f);
2254 hf->n_vectors = VLIB_FRAME_SIZE - n_left_to_next_worker;
2256 /* Ship frames to the worker nodes */
2257 for (i = 0; i < vec_len (handoff_queue_elt_by_worker_index); i++)
2259 if (handoff_queue_elt_by_worker_index[i])
2261 hf = handoff_queue_elt_by_worker_index[i];
2263 * It works better to let the handoff node
2264 * rate-adapt, always ship the handoff queue element.
2266 if (1 || hf->n_vectors == hf->last_n_vectors)
2268 vlib_put_frame_queue_elt (hf);
2269 handoff_queue_elt_by_worker_index[i] = 0;
2272 hf->last_n_vectors = hf->n_vectors;
2274 congested_handoff_queue_by_worker_index[i] =
2275 (vlib_frame_queue_t *) (~0);
2278 current_worker_index = ~0;
2279 return frame->n_vectors;
2282 VLIB_REGISTER_NODE (snat_out2in_worker_handoff_node) = {
2283 .function = snat_out2in_worker_handoff_fn,
2284 .name = "nat44-out2in-worker-handoff",
2285 .vector_size = sizeof (u32),
2286 .format_trace = format_snat_out2in_worker_handoff_trace,
2287 .type = VLIB_NODE_TYPE_INTERNAL,
2296 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_worker_handoff_node, snat_out2in_worker_handoff_fn);
2299 snat_out2in_fast_node_fn (vlib_main_t * vm,
2300 vlib_node_runtime_t * node,
2301 vlib_frame_t * frame)
2303 u32 n_left_from, * from, * to_next;
2304 snat_out2in_next_t next_index;
2305 u32 pkts_processed = 0;
2306 snat_main_t * sm = &snat_main;
2308 from = vlib_frame_vector_args (frame);
2309 n_left_from = frame->n_vectors;
2310 next_index = node->cached_next_index;
2312 while (n_left_from > 0)
2316 vlib_get_next_frame (vm, node, next_index,
2317 to_next, n_left_to_next);
2319 while (n_left_from > 0 && n_left_to_next > 0)
2323 u32 next0 = SNAT_OUT2IN_NEXT_DROP;
2327 u32 new_addr0, old_addr0;
2328 u16 new_port0, old_port0;
2329 udp_header_t * udp0;
2330 tcp_header_t * tcp0;
2331 icmp46_header_t * icmp0;
2332 snat_session_key_t key0, sm0;
2336 /* speculatively enqueue b0 to the current next frame */
2342 n_left_to_next -= 1;
2344 b0 = vlib_get_buffer (vm, bi0);
2346 ip0 = vlib_buffer_get_current (b0);
2347 udp0 = ip4_next_header (ip0);
2348 tcp0 = (tcp_header_t *) udp0;
2349 icmp0 = (icmp46_header_t *) udp0;
2351 sw_if_index0 = vnet_buffer(b0)->sw_if_index[VLIB_RX];
2352 rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index(sw_if_index0);
2354 vnet_feature_next (sw_if_index0, &next0, b0);
2356 if (PREDICT_FALSE(ip0->ttl == 1))
2358 vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
2359 icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
2360 ICMP4_time_exceeded_ttl_exceeded_in_transit,
2362 next0 = SNAT_OUT2IN_NEXT_ICMP_ERROR;
2366 proto0 = ip_proto_to_snat_proto (ip0->protocol);
2368 if (PREDICT_FALSE (proto0 == ~0))
2371 if (PREDICT_FALSE (proto0 == SNAT_PROTOCOL_ICMP))
2373 next0 = icmp_out2in(sm, b0, ip0, icmp0, sw_if_index0,
2374 rx_fib_index0, node, next0, ~0, 0, 0);
2378 key0.addr = ip0->dst_address;
2379 key0.port = udp0->dst_port;
2380 key0.fib_index = rx_fib_index0;
2382 if (snat_static_mapping_match(sm, key0, &sm0, 1, 0))
2384 b0->error = node->errors[SNAT_OUT2IN_ERROR_NO_TRANSLATION];
2388 new_addr0 = sm0.addr.as_u32;
2389 new_port0 = sm0.port;
2390 vnet_buffer(b0)->sw_if_index[VLIB_TX] = sm0.fib_index;
2391 old_addr0 = ip0->dst_address.as_u32;
2392 ip0->dst_address.as_u32 = new_addr0;
2394 sum0 = ip0->checksum;
2395 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
2397 dst_address /* changed member */);
2398 ip0->checksum = ip_csum_fold (sum0);
2400 if (PREDICT_FALSE(new_port0 != udp0->dst_port))
2402 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
2404 old_port0 = tcp0->dst_port;
2405 tcp0->dst_port = new_port0;
2407 sum0 = tcp0->checksum;
2408 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
2410 dst_address /* changed member */);
2412 sum0 = ip_csum_update (sum0, old_port0, new_port0,
2413 ip4_header_t /* cheat */,
2414 length /* changed member */);
2415 tcp0->checksum = ip_csum_fold(sum0);
2419 old_port0 = udp0->dst_port;
2420 udp0->dst_port = new_port0;
2426 if (PREDICT_TRUE(proto0 == SNAT_PROTOCOL_TCP))
2428 sum0 = tcp0->checksum;
2429 sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
2431 dst_address /* changed member */);
2433 tcp0->checksum = ip_csum_fold(sum0);
2439 if (PREDICT_FALSE((node->flags & VLIB_NODE_FLAG_TRACE)
2440 && (b0->flags & VLIB_BUFFER_IS_TRACED)))
2442 snat_out2in_trace_t *t =
2443 vlib_add_trace (vm, node, b0, sizeof (*t));
2444 t->sw_if_index = sw_if_index0;
2445 t->next_index = next0;
2448 pkts_processed += next0 != SNAT_OUT2IN_NEXT_DROP;
2450 /* verify speculative enqueue, maybe switch current next frame */
2451 vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
2452 to_next, n_left_to_next,
2456 vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2459 vlib_node_increment_counter (vm, snat_out2in_fast_node.index,
2460 SNAT_OUT2IN_ERROR_OUT2IN_PACKETS,
2462 return frame->n_vectors;
2465 VLIB_REGISTER_NODE (snat_out2in_fast_node) = {
2466 .function = snat_out2in_fast_node_fn,
2467 .name = "nat44-out2in-fast",
2468 .vector_size = sizeof (u32),
2469 .format_trace = format_snat_out2in_fast_trace,
2470 .type = VLIB_NODE_TYPE_INTERNAL,
2472 .n_errors = ARRAY_LEN(snat_out2in_error_strings),
2473 .error_strings = snat_out2in_error_strings,
2475 .runtime_data_bytes = sizeof (snat_runtime_t),
2477 .n_next_nodes = SNAT_OUT2IN_N_NEXT,
2479 /* edit / add dispositions here */
2481 [SNAT_OUT2IN_NEXT_LOOKUP] = "ip4-lookup",
2482 [SNAT_OUT2IN_NEXT_DROP] = "error-drop",
2483 [SNAT_OUT2IN_NEXT_ICMP_ERROR] = "ip4-icmp-error",
2486 VLIB_NODE_FUNCTION_MULTIARCH (snat_out2in_fast_node, snat_out2in_fast_node_fn);