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