37dfd7827f604329ae5f290511d0819f4943dc68
[vpp.git] / src / plugins / nat / nat44_hairpinning.c
1 /*
2  * Copyright (c) 2018 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:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
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.
14  */
15 /**
16  * @file
17  * @brief NAT44 hairpinning
18  */
19
20 #include <vlib/vlib.h>
21 #include <vnet/vnet.h>
22 #include <vnet/fib/ip4_fib.h>
23 #include <nat/nat.h>
24 #include <nat/nat_inlines.h>
25
26 typedef enum
27 {
28   SNAT_HAIRPIN_SRC_NEXT_DROP,
29   SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT,
30   SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH,
31   SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT,
32   SNAT_HAIRPIN_SRC_N_NEXT,
33 } snat_hairpin_src_next_t;
34
35 typedef enum
36 {
37   NAT_HAIRPIN_NEXT_LOOKUP,
38   NAT_HAIRPIN_NEXT_DROP,
39   NAT_HAIRPIN_N_NEXT,
40 } nat_hairpin_next_t;
41
42 typedef struct
43 {
44   ip4_address_t addr;
45   u16 port;
46   u32 fib_index;
47   u32 session_index;
48 } nat_hairpin_trace_t;
49
50 static u8 *
51 format_nat_hairpin_trace (u8 * s, va_list * args)
52 {
53   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
54   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
55   nat_hairpin_trace_t *t = va_arg (*args, nat_hairpin_trace_t *);
56
57   s =
58     format (s, "new dst addr %U port %u fib-index %u", format_ip4_address,
59             &t->addr, clib_net_to_host_u16 (t->port), t->fib_index);
60   if (~0 == t->session_index)
61     {
62       s = format (s, " is-static-mapping");
63     }
64   else
65     {
66       s = format (s, " session-index %u", t->session_index);
67     }
68
69   return s;
70 }
71
72 extern vnet_feature_arc_registration_t vnet_feat_arc_ip4_local;
73
74 static_always_inline int
75 is_hairpinning (snat_main_t * sm, ip4_address_t * dst_addr)
76 {
77   snat_address_t *ap;
78   clib_bihash_kv_8_8_t kv, value;
79
80   /* *INDENT-OFF* */
81   vec_foreach (ap, sm->addresses)
82     {
83       if (ap->addr.as_u32 == dst_addr->as_u32)
84         return 1;
85     }
86   /* *INDENT-ON* */
87
88   init_nat_k (&kv, *dst_addr, 0, 0, 0);
89   if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
90     return 1;
91
92   return 0;
93 }
94
95 #ifndef CLIB_MARCH_VARIANT
96 int
97 snat_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node, snat_main_t *sm,
98                   vlib_buffer_t *b0, ip4_header_t *ip0, udp_header_t *udp0,
99                   tcp_header_t *tcp0, u32 proto0, int do_trace)
100 {
101   snat_session_t *s0 = NULL;
102   clib_bihash_kv_8_8_t kv0, value0;
103   ip_csum_t sum0;
104   u32 new_dst_addr0 = 0, old_dst_addr0, ti = 0, si = ~0;
105   u16 new_dst_port0 = ~0, old_dst_port0;
106   int rv;
107   ip4_address_t sm0_addr;
108   u16 sm0_port;
109   u32 sm0_fib_index;
110   u32 old_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
111   /* Check if destination is static mappings */
112   if (!snat_static_mapping_match
113       (sm, ip0->dst_address, udp0->dst_port, sm->outside_fib_index, proto0,
114        &sm0_addr, &sm0_port, &sm0_fib_index, 1, 0, 0, 0, 0, 0, 0))
115     {
116       new_dst_addr0 = sm0_addr.as_u32;
117       new_dst_port0 = sm0_port;
118       vnet_buffer (b0)->sw_if_index[VLIB_TX] = sm0_fib_index;
119     }
120   /* or active session */
121   else
122     {
123       if (sm->num_workers > 1)
124         ti =
125           (clib_net_to_host_u16 (udp0->dst_port) -
126            1024) / sm->port_per_thread;
127       else
128         ti = sm->num_workers;
129
130       init_nat_k (&kv0, ip0->dst_address, udp0->dst_port,
131                   sm->outside_fib_index, proto0);
132       rv = clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0,
133                                    &value0);
134       if (rv)
135         {
136           rv = 0;
137           goto trace;
138         }
139
140       si = value0.value;
141       s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
142       new_dst_addr0 = s0->in2out.addr.as_u32;
143       new_dst_port0 = s0->in2out.port;
144       vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
145     }
146
147   /* Check if anything has changed and if not, then return 0. This
148      helps avoid infinite loop, repeating the three nodes
149      nat44-hairpinning-->ip4-lookup-->ip4-local, in case nothing has
150      changed. */
151   old_dst_addr0 = ip0->dst_address.as_u32;
152   old_dst_port0 = tcp0->dst;
153   if (new_dst_addr0 == old_dst_addr0
154       && new_dst_port0 == old_dst_port0
155       && vnet_buffer (b0)->sw_if_index[VLIB_TX] == old_sw_if_index)
156     return 0;
157
158   /* Destination is behind the same NAT, use internal address and port */
159   if (new_dst_addr0)
160     {
161       old_dst_addr0 = ip0->dst_address.as_u32;
162       ip0->dst_address.as_u32 = new_dst_addr0;
163       sum0 = ip0->checksum;
164       sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
165                              ip4_header_t, dst_address);
166       ip0->checksum = ip_csum_fold (sum0);
167
168       old_dst_port0 = tcp0->dst;
169       if (PREDICT_TRUE (new_dst_port0 != old_dst_port0))
170         {
171           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
172             {
173               tcp0->dst = new_dst_port0;
174               sum0 = tcp0->checksum;
175               sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
176                                      ip4_header_t, dst_address);
177               sum0 = ip_csum_update (sum0, old_dst_port0, new_dst_port0,
178                                      ip4_header_t /* cheat */ , length);
179               tcp0->checksum = ip_csum_fold (sum0);
180             }
181           else
182             {
183               udp0->dst_port = new_dst_port0;
184               udp0->checksum = 0;
185             }
186         }
187       else
188         {
189           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
190             {
191               sum0 = tcp0->checksum;
192               sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
193                                      ip4_header_t, dst_address);
194               tcp0->checksum = ip_csum_fold (sum0);
195             }
196         }
197       rv = 1;
198       goto trace;
199     }
200   rv = 0;
201 trace:
202   if (do_trace && PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
203                                  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
204     {
205       nat_hairpin_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
206       t->addr.as_u32 = new_dst_addr0;
207       t->port = new_dst_port0;
208       t->fib_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
209       if (s0)
210         {
211           t->session_index = si;
212         }
213       else
214         {
215           t->session_index = ~0;
216         }
217     }
218   return rv;
219 }
220 #endif
221
222 #ifndef CLIB_MARCH_VARIANT
223 u32
224 snat_icmp_hairpinning (snat_main_t *sm, vlib_buffer_t *b0, ip4_header_t *ip0,
225                        icmp46_header_t *icmp0)
226 {
227   clib_bihash_kv_8_8_t kv0, value0;
228   u32 old_dst_addr0, new_dst_addr0;
229   u32 old_addr0, new_addr0;
230   u16 old_port0, new_port0;
231   u16 old_checksum0, new_checksum0;
232   u32 si, ti = 0;
233   ip_csum_t sum0;
234   snat_session_t *s0;
235   snat_static_mapping_t *m0;
236
237   if (icmp_type_is_error_message
238       (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags))
239     {
240       ip4_header_t *inner_ip0 = 0;
241       tcp_udp_header_t *l4_header = 0;
242
243       inner_ip0 = (ip4_header_t *) ((icmp_echo_header_t *) (icmp0 + 1) + 1);
244       l4_header = ip4_next_header (inner_ip0);
245       u32 protocol = ip_proto_to_nat_proto (inner_ip0->protocol);
246
247       if (protocol != NAT_PROTOCOL_TCP && protocol != NAT_PROTOCOL_UDP)
248         return 1;
249
250       init_nat_k (&kv0, ip0->dst_address, l4_header->src_port,
251                   sm->outside_fib_index, protocol);
252       if (clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0,
253                                   &value0))
254         return 1;
255       si = value0.value;
256       s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
257       new_dst_addr0 = s0->in2out.addr.as_u32;
258       vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
259
260       /* update inner source IP address */
261       old_addr0 = inner_ip0->src_address.as_u32;
262       inner_ip0->src_address.as_u32 = new_dst_addr0;
263       new_addr0 = inner_ip0->src_address.as_u32;
264       sum0 = icmp0->checksum;
265       sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
266                              src_address);
267       icmp0->checksum = ip_csum_fold (sum0);
268
269       /* update inner IP header checksum */
270       old_checksum0 = inner_ip0->checksum;
271       sum0 = inner_ip0->checksum;
272       sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
273                              src_address);
274       inner_ip0->checksum = ip_csum_fold (sum0);
275       new_checksum0 = inner_ip0->checksum;
276       sum0 = icmp0->checksum;
277       sum0 = ip_csum_update (sum0, old_checksum0, new_checksum0, ip4_header_t,
278                              checksum);
279       icmp0->checksum = ip_csum_fold (sum0);
280
281       /* update inner source port */
282       old_port0 = l4_header->src_port;
283       l4_header->src_port = s0->in2out.port;
284       new_port0 = l4_header->src_port;
285       sum0 = icmp0->checksum;
286       sum0 = ip_csum_update (sum0, old_port0, new_port0, tcp_udp_header_t,
287                              src_port);
288       icmp0->checksum = ip_csum_fold (sum0);
289     }
290   else
291     {
292       init_nat_k (&kv0, ip0->dst_address, 0, sm->outside_fib_index, 0);
293       if (clib_bihash_search_8_8
294           (&sm->static_mapping_by_external, &kv0, &value0))
295         {
296           icmp_echo_header_t *echo0 = (icmp_echo_header_t *) (icmp0 + 1);
297           u16 icmp_id0 = echo0->identifier;
298           init_nat_k (&kv0, ip0->dst_address, icmp_id0, sm->outside_fib_index,
299                       NAT_PROTOCOL_ICMP);
300           if (sm->num_workers > 1)
301             ti =
302               (clib_net_to_host_u16 (icmp_id0) - 1024) / sm->port_per_thread;
303           else
304             ti = sm->num_workers;
305           int rv = clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in,
306                                            &kv0, &value0);
307           if (!rv)
308             {
309               si = value0.value;
310               s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
311               new_dst_addr0 = s0->in2out.addr.as_u32;
312               vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
313               echo0->identifier = s0->in2out.port;
314               sum0 = icmp0->checksum;
315               sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port,
316                                      icmp_echo_header_t, identifier);
317               icmp0->checksum = ip_csum_fold (sum0);
318               goto change_addr;
319             }
320
321           return 1;
322         }
323
324       m0 = pool_elt_at_index (sm->static_mappings, value0.value);
325
326       new_dst_addr0 = m0->local_addr.as_u32;
327       if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0)
328         vnet_buffer (b0)->sw_if_index[VLIB_TX] = m0->fib_index;
329     }
330 change_addr:
331   /* Destination is behind the same NAT, use internal address and port */
332   if (new_dst_addr0)
333     {
334       old_dst_addr0 = ip0->dst_address.as_u32;
335       ip0->dst_address.as_u32 = new_dst_addr0;
336       sum0 = ip0->checksum;
337       sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
338                              ip4_header_t, dst_address);
339       ip0->checksum = ip_csum_fold (sum0);
340     }
341   return 0;
342 }
343 #endif
344
345 #ifndef CLIB_MARCH_VARIANT
346 void
347 nat_hairpinning_sm_unknown_proto (snat_main_t * sm,
348                                   vlib_buffer_t * b, ip4_header_t * ip)
349 {
350   clib_bihash_kv_8_8_t kv, value;
351   snat_static_mapping_t *m;
352   u32 old_addr, new_addr;
353   ip_csum_t sum;
354
355   init_nat_k (&kv, ip->dst_address, 0, 0, 0);
356   if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
357     return;
358
359   m = pool_elt_at_index (sm->static_mappings, value.value);
360
361   old_addr = ip->dst_address.as_u32;
362   new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
363   sum = ip->checksum;
364   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
365   ip->checksum = ip_csum_fold (sum);
366
367   if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0)
368     vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index;
369 }
370 #endif
371
372 static inline uword
373 nat44_hairpinning_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
374                              vlib_frame_t *frame)
375 {
376   u32 n_left_from, *from, *to_next;
377   nat_hairpin_next_t next_index;
378   snat_main_t *sm = &snat_main;
379   vnet_feature_main_t *fm = &feature_main;
380   u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index;
381   vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index];
382
383   from = vlib_frame_vector_args (frame);
384   n_left_from = frame->n_vectors;
385   next_index = node->cached_next_index;
386
387   while (n_left_from > 0)
388     {
389       u32 n_left_to_next;
390
391       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
392
393       while (n_left_from > 0 && n_left_to_next > 0)
394         {
395           u32 bi0;
396           vlib_buffer_t *b0;
397           u32 next0;
398           ip4_header_t *ip0;
399           u32 proto0;
400           udp_header_t *udp0;
401           tcp_header_t *tcp0;
402           u32 sw_if_index0;
403
404           /* speculatively enqueue b0 to the current next frame */
405           bi0 = from[0];
406           to_next[0] = bi0;
407           from += 1;
408           to_next += 1;
409           n_left_from -= 1;
410           n_left_to_next -= 1;
411
412           b0 = vlib_get_buffer (vm, bi0);
413           ip0 = vlib_buffer_get_current (b0);
414           udp0 = ip4_next_header (ip0);
415           tcp0 = (tcp_header_t *) udp0;
416           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
417
418           proto0 = ip_proto_to_nat_proto (ip0->protocol);
419
420           vnet_get_config_data (&cm->config_main, &b0->current_config_index,
421                                 &next0, 0);
422
423           if (snat_hairpinning (vm, node, sm, b0, ip0, udp0, tcp0, proto0,
424                                 1 /* do_trace */))
425             next0 = NAT_HAIRPIN_NEXT_LOOKUP;
426
427           if (next0 != NAT_HAIRPIN_NEXT_DROP)
428             {
429               vlib_increment_simple_counter (&sm->counters.hairpinning,
430                                              vm->thread_index, sw_if_index0,
431                                              1);
432             }
433
434           /* verify speculative enqueue, maybe switch current next frame */
435           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
436                                            to_next, n_left_to_next,
437                                            bi0, next0);
438         }
439
440       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
441     }
442
443   return frame->n_vectors;
444 }
445
446 VLIB_NODE_FN (nat44_hairpinning_node) (vlib_main_t * vm,
447                                        vlib_node_runtime_t * node,
448                                        vlib_frame_t * frame)
449 {
450   return nat44_hairpinning_fn_inline (vm, node, frame);
451 }
452
453 /* *INDENT-OFF* */
454 VLIB_REGISTER_NODE (nat44_hairpinning_node) = {
455   .name = "nat44-hairpinning",
456   .vector_size = sizeof (u32),
457   .type = VLIB_NODE_TYPE_INTERNAL,
458   .format_trace = format_nat_hairpin_trace,
459   .n_next_nodes = NAT_HAIRPIN_N_NEXT,
460   .next_nodes = {
461     [NAT_HAIRPIN_NEXT_DROP] = "error-drop",
462     [NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
463   },
464 };
465 /* *INDENT-ON* */
466
467 static inline uword
468 snat_hairpin_dst_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
469                             vlib_frame_t *frame)
470 {
471   u32 n_left_from, *from, *to_next;
472   nat_hairpin_next_t next_index;
473   snat_main_t *sm = &snat_main;
474
475   from = vlib_frame_vector_args (frame);
476   n_left_from = frame->n_vectors;
477   next_index = node->cached_next_index;
478
479   while (n_left_from > 0)
480     {
481       u32 n_left_to_next;
482
483       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
484
485       while (n_left_from > 0 && n_left_to_next > 0)
486         {
487           u32 bi0;
488           vlib_buffer_t *b0;
489           u32 next0;
490           ip4_header_t *ip0;
491           u32 proto0;
492           u32 sw_if_index0;
493
494           /* speculatively enqueue b0 to the current next frame */
495           bi0 = from[0];
496           to_next[0] = bi0;
497           from += 1;
498           to_next += 1;
499           n_left_from -= 1;
500           n_left_to_next -= 1;
501
502           b0 = vlib_get_buffer (vm, bi0);
503           next0 = NAT_HAIRPIN_NEXT_LOOKUP;
504           ip0 = vlib_buffer_get_current (b0);
505           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
506
507           proto0 = ip_proto_to_nat_proto (ip0->protocol);
508
509           vnet_buffer (b0)->snat.flags = 0;
510           if (PREDICT_FALSE (is_hairpinning (sm, &ip0->dst_address)))
511             {
512               if (proto0 == NAT_PROTOCOL_TCP || proto0 == NAT_PROTOCOL_UDP)
513                 {
514                   udp_header_t *udp0 = ip4_next_header (ip0);
515                   tcp_header_t *tcp0 = (tcp_header_t *) udp0;
516
517                   snat_hairpinning (vm, node, sm, b0, ip0, udp0, tcp0, proto0,
518                                     1 /* do_trace */);
519                 }
520               else if (proto0 == NAT_PROTOCOL_ICMP)
521                 {
522                   icmp46_header_t *icmp0 = ip4_next_header (ip0);
523
524                   snat_icmp_hairpinning (sm, b0, ip0, icmp0);
525                 }
526               else
527                 {
528                   nat_hairpinning_sm_unknown_proto (sm, b0, ip0);
529                 }
530
531               vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING;
532             }
533
534
535           if (next0 != NAT_HAIRPIN_NEXT_DROP)
536             {
537               vlib_increment_simple_counter (&sm->counters.hairpinning,
538                                              vm->thread_index, sw_if_index0,
539                                              1);
540             }
541
542           /* verify speculative enqueue, maybe switch current next frame */
543           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
544                                            to_next, n_left_to_next,
545                                            bi0, next0);
546         }
547
548       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
549     }
550
551   return frame->n_vectors;
552 }
553
554 VLIB_NODE_FN (snat_hairpin_dst_node) (vlib_main_t * vm,
555                                       vlib_node_runtime_t * node,
556                                       vlib_frame_t * frame)
557 {
558   return snat_hairpin_dst_fn_inline (vm, node, frame);
559 }
560
561 /* *INDENT-OFF* */
562 VLIB_REGISTER_NODE (snat_hairpin_dst_node) = {
563   .name = "nat44-hairpin-dst",
564   .vector_size = sizeof (u32),
565   .type = VLIB_NODE_TYPE_INTERNAL,
566   .format_trace = format_nat_hairpin_trace,
567   .n_next_nodes = NAT_HAIRPIN_N_NEXT,
568   .next_nodes = {
569     [NAT_HAIRPIN_NEXT_DROP] = "error-drop",
570     [NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
571   },
572 };
573 /* *INDENT-ON* */
574
575 static inline uword
576 snat_hairpin_src_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
577                             vlib_frame_t *frame)
578 {
579   u32 n_left_from, *from, *to_next;
580   snat_hairpin_src_next_t next_index;
581   snat_main_t *sm = &snat_main;
582
583   from = vlib_frame_vector_args (frame);
584   n_left_from = frame->n_vectors;
585   next_index = node->cached_next_index;
586
587   while (n_left_from > 0)
588     {
589       u32 n_left_to_next;
590
591       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
592
593       while (n_left_from > 0 && n_left_to_next > 0)
594         {
595           u32 bi0;
596           vlib_buffer_t *b0;
597           u32 next0;
598           snat_interface_t *i;
599           u32 sw_if_index0;
600
601           /* speculatively enqueue b0 to the current next frame */
602           bi0 = from[0];
603           to_next[0] = bi0;
604           from += 1;
605           to_next += 1;
606           n_left_from -= 1;
607           n_left_to_next -= 1;
608
609           b0 = vlib_get_buffer (vm, bi0);
610           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
611           vnet_feature_next (&next0, b0);
612
613           /* *INDENT-OFF* */
614           pool_foreach (i, sm->output_feature_interfaces)
615            {
616             /* Only packets from NAT inside interface */
617             if ((nat_interface_is_inside(i)) && (sw_if_index0 == i->sw_if_index))
618               {
619                 if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) &
620                                     SNAT_FLAG_HAIRPINNING))
621                   {
622                     if (PREDICT_TRUE (sm->num_workers > 1))
623                       next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH;
624                     else
625                       next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT;
626                   }
627                 break;
628               }
629           }
630           /* *INDENT-ON* */
631
632           if (next0 != SNAT_HAIRPIN_SRC_NEXT_DROP)
633             {
634               vlib_increment_simple_counter (&sm->counters.hairpinning,
635                                              vm->thread_index, sw_if_index0,
636                                              1);
637             }
638
639           /* verify speculative enqueue, maybe switch current next frame */
640           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
641                                            to_next, n_left_to_next,
642                                            bi0, next0);
643         }
644
645       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
646     }
647
648   return frame->n_vectors;
649 }
650
651 VLIB_NODE_FN (snat_hairpin_src_node) (vlib_main_t * vm,
652                                       vlib_node_runtime_t * node,
653                                       vlib_frame_t * frame)
654 {
655   return snat_hairpin_src_fn_inline (vm, node, frame);
656 }
657
658 /* *INDENT-OFF* */
659 VLIB_REGISTER_NODE (snat_hairpin_src_node) = {
660   .name = "nat44-hairpin-src",
661   .vector_size = sizeof (u32),
662   .type = VLIB_NODE_TYPE_INTERNAL,
663   .n_next_nodes = SNAT_HAIRPIN_SRC_N_NEXT,
664   .next_nodes = {
665      [SNAT_HAIRPIN_SRC_NEXT_DROP] = "error-drop",
666      [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-in2out-output",
667      [SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output",
668      [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-in2out-output-worker-handoff",
669   },
670 };
671 /* *INDENT-ON* */
672
673 /*
674  * fd.io coding-style-patch-verification: ON
675  *
676  * Local Variables:
677  * eval: (c-set-style "gnu")
678  * End:
679  */