nat: fix counters increment for output feature
[vpp.git] / src / plugins / nat / nat44-ei / nat44_ei_hairpinning.c
1 /*
2  * nat44_ei.c - nat44 endpoint dependent plugin
3  * * Copyright (c) 2020 Cisco and/or its affiliates.  * Licensed under the
4  * Apache License, Version 2.0 (the "License"); you may not use this file
5  * except in compliance with the License. You may obtain a copy of the License
6  * at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations
14  * under the License.
15  */
16
17 #include <vlib/vlib.h>
18 #include <vnet/vnet.h>
19 #include <vnet/fib/ip4_fib.h>
20
21 #include <nat/nat44-ei/nat44_ei.h>
22 #include <nat/nat44-ei/nat44_ei_inlines.h>
23 #include <nat/nat44-ei/nat44_ei_hairpinning.h>
24
25 /* NAT buffer flags */
26 #define NAT44_EI_FLAG_HAIRPINNING (1 << 0)
27
28 typedef enum
29 {
30   NAT44_EI_HAIRPIN_SRC_NEXT_DROP,
31   NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT,
32   NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH,
33   NAT44_EI_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT,
34   NAT44_EI_HAIRPIN_SRC_N_NEXT,
35 } nat44_ei_hairpin_src_next_t;
36
37 typedef enum
38 {
39   NAT44_EI_HAIRPIN_NEXT_LOOKUP,
40   NAT44_EI_HAIRPIN_NEXT_DROP,
41   NAT44_EI_HAIRPIN_NEXT_HANDOFF,
42   NAT44_EI_HAIRPIN_N_NEXT,
43 } nat44_ei_hairpin_next_t;
44
45 typedef struct
46 {
47   ip4_address_t addr;
48   u16 port;
49   u32 fib_index;
50   u32 session_index;
51 } nat44_ei_hairpin_trace_t;
52
53 static u8 *
54 format_nat44_ei_hairpin_trace (u8 *s, va_list *args)
55 {
56   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
57   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
58   nat44_ei_hairpin_trace_t *t = va_arg (*args, nat44_ei_hairpin_trace_t *);
59
60   s = 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 nat44_ei_is_hairpinning (nat44_ei_main_t *nm, ip4_address_t *dst_addr)
78 {
79   nat44_ei_address_t *ap;
80   clib_bihash_kv_8_8_t kv, value;
81
82   vec_foreach (ap, nm->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 (&nm->static_mapping_by_external, &kv, &value))
90     return 1;
91
92   return 0;
93 }
94
95 #ifndef CLIB_MARCH_VARIANT
96 void
97 nat44_ei_hairpinning_sm_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b,
98                                        ip4_header_t *ip)
99 {
100   clib_bihash_kv_8_8_t kv, value;
101   nat44_ei_static_mapping_t *m;
102   u32 old_addr, new_addr;
103   ip_csum_t sum;
104
105   init_nat_k (&kv, ip->dst_address, 0, 0, 0);
106   if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value))
107     return;
108
109   m = pool_elt_at_index (nm->static_mappings, value.value);
110
111   old_addr = ip->dst_address.as_u32;
112   new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
113   sum = ip->checksum;
114   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
115   ip->checksum = ip_csum_fold (sum);
116
117   if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0)
118     vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index;
119 }
120 #endif
121
122 #ifndef CLIB_MARCH_VARIANT
123 int
124 nat44_ei_hairpinning (vlib_main_t *vm, vlib_node_runtime_t *node,
125                       nat44_ei_main_t *nm, u32 thread_index, vlib_buffer_t *b0,
126                       ip4_header_t *ip0, udp_header_t *udp0,
127                       tcp_header_t *tcp0, u32 proto0, int do_trace,
128                       u32 *required_thread_index)
129 {
130   nat44_ei_session_t *s0 = NULL;
131   clib_bihash_kv_8_8_t kv0, value0;
132   ip_csum_t sum0;
133   u32 new_dst_addr0 = 0, old_dst_addr0, si = ~0;
134   u16 new_dst_port0 = ~0, old_dst_port0;
135   int rv;
136   ip4_address_t sm0_addr;
137   u16 sm0_port;
138   u32 sm0_fib_index;
139   u32 old_sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
140
141   /* Check if destination is static mappings */
142   if (!nat44_ei_static_mapping_match (
143         ip0->dst_address, udp0->dst_port, nm->outside_fib_index, proto0,
144         &sm0_addr, &sm0_port, &sm0_fib_index, 1 /* by external */, 0, 0))
145     {
146       new_dst_addr0 = sm0_addr.as_u32;
147       new_dst_port0 = sm0_port;
148       vnet_buffer (b0)->sw_if_index[VLIB_TX] = sm0_fib_index;
149     }
150   /* or active session */
151   else
152     {
153       init_nat_k (&kv0, ip0->dst_address, udp0->dst_port,
154                   nm->outside_fib_index, proto0);
155       rv = clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0);
156       if (rv)
157         {
158           rv = 0;
159           goto trace;
160         }
161
162       if (thread_index != nat_value_get_thread_index (&value0))
163         {
164           *required_thread_index = nat_value_get_thread_index (&value0);
165           return 0;
166         }
167
168       si = nat_value_get_session_index (&value0);
169       s0 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions, si);
170       new_dst_addr0 = s0->in2out.addr.as_u32;
171       new_dst_port0 = s0->in2out.port;
172       vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
173     }
174
175   /* Check if anything has changed and if not, then return 0. This
176      helps avoid infinite loop, repeating the three nodes
177      nat44-hairpinning-->ip4-lookup-->ip4-local, in case nothing has
178      changed. */
179   old_dst_addr0 = ip0->dst_address.as_u32;
180   old_dst_port0 = tcp0->dst;
181   if (new_dst_addr0 == old_dst_addr0 && new_dst_port0 == old_dst_port0 &&
182       vnet_buffer (b0)->sw_if_index[VLIB_TX] == old_sw_if_index)
183     return 0;
184
185   /* Destination is behind the same NAT, use internal address and port */
186   if (new_dst_addr0)
187     {
188       old_dst_addr0 = ip0->dst_address.as_u32;
189       ip0->dst_address.as_u32 = new_dst_addr0;
190       sum0 = ip0->checksum;
191       sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, ip4_header_t,
192                              dst_address);
193       ip0->checksum = ip_csum_fold (sum0);
194
195       old_dst_port0 = tcp0->dst;
196       if (PREDICT_TRUE (new_dst_port0 != old_dst_port0))
197         {
198           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
199             {
200               tcp0->dst = new_dst_port0;
201               sum0 = tcp0->checksum;
202               sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
203                                      ip4_header_t, dst_address);
204               sum0 = ip_csum_update (sum0, old_dst_port0, new_dst_port0,
205                                      ip4_header_t /* cheat */, length);
206               tcp0->checksum = ip_csum_fold (sum0);
207             }
208           else
209             {
210               udp0->dst_port = new_dst_port0;
211               udp0->checksum = 0;
212             }
213         }
214       else
215         {
216           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
217             {
218               sum0 = tcp0->checksum;
219               sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
220                                      ip4_header_t, dst_address);
221               tcp0->checksum = ip_csum_fold (sum0);
222             }
223         }
224       rv = 1;
225       goto trace;
226     }
227   rv = 0;
228 trace:
229   if (do_trace && PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
230                                  (b0->flags & VLIB_BUFFER_IS_TRACED)))
231     {
232       nat44_ei_hairpin_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
233       t->addr.as_u32 = new_dst_addr0;
234       t->port = new_dst_port0;
235       t->fib_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
236       if (s0)
237         {
238           t->session_index = si;
239         }
240       else
241         {
242           t->session_index = ~0;
243         }
244     }
245   return rv;
246 }
247 #endif
248
249 #ifndef CLIB_MARCH_VARIANT
250 u32
251 nat44_ei_icmp_hairpinning (nat44_ei_main_t *nm, vlib_buffer_t *b0,
252                            u32 thread_index, ip4_header_t *ip0,
253                            icmp46_header_t *icmp0, u32 *required_thread_index)
254 {
255   clib_bihash_kv_8_8_t kv0, value0;
256   u32 old_dst_addr0, new_dst_addr0;
257   u32 old_addr0, new_addr0;
258   u16 old_port0, new_port0;
259   u16 old_checksum0, new_checksum0;
260   u32 si, ti = 0;
261   ip_csum_t sum0;
262   nat44_ei_session_t *s0;
263   nat44_ei_static_mapping_t *m0;
264
265   if (icmp_type_is_error_message (
266         vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags))
267     {
268       ip4_header_t *inner_ip0 = 0;
269       tcp_udp_header_t *l4_header = 0;
270
271       inner_ip0 = (ip4_header_t *) ((icmp_echo_header_t *) (icmp0 + 1) + 1);
272       l4_header = ip4_next_header (inner_ip0);
273       u32 protocol = ip_proto_to_nat_proto (inner_ip0->protocol);
274
275       if (protocol != NAT_PROTOCOL_TCP && protocol != NAT_PROTOCOL_UDP)
276         return 1;
277
278       init_nat_k (&kv0, ip0->dst_address, l4_header->src_port,
279                   nm->outside_fib_index, protocol);
280       if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0))
281         return 1;
282       ti = nat_value_get_thread_index (&value0);
283       if (ti != thread_index)
284         {
285           *required_thread_index = ti;
286           return 1;
287         }
288       si = nat_value_get_session_index (&value0);
289       s0 = pool_elt_at_index (nm->per_thread_data[ti].sessions, si);
290       new_dst_addr0 = s0->in2out.addr.as_u32;
291       vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
292
293       /* update inner source IP address */
294       old_addr0 = inner_ip0->src_address.as_u32;
295       inner_ip0->src_address.as_u32 = new_dst_addr0;
296       new_addr0 = inner_ip0->src_address.as_u32;
297       sum0 = icmp0->checksum;
298       sum0 =
299         ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, src_address);
300       icmp0->checksum = ip_csum_fold (sum0);
301
302       /* update inner IP header checksum */
303       old_checksum0 = inner_ip0->checksum;
304       sum0 = inner_ip0->checksum;
305       sum0 =
306         ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, src_address);
307       inner_ip0->checksum = ip_csum_fold (sum0);
308       new_checksum0 = inner_ip0->checksum;
309       sum0 = icmp0->checksum;
310       sum0 = ip_csum_update (sum0, old_checksum0, new_checksum0, ip4_header_t,
311                              checksum);
312       icmp0->checksum = ip_csum_fold (sum0);
313
314       /* update inner source port */
315       old_port0 = l4_header->src_port;
316       l4_header->src_port = s0->in2out.port;
317       new_port0 = l4_header->src_port;
318       sum0 = icmp0->checksum;
319       sum0 = ip_csum_update (sum0, old_port0, new_port0, tcp_udp_header_t,
320                              src_port);
321       icmp0->checksum = ip_csum_fold (sum0);
322     }
323   else
324     {
325       init_nat_k (&kv0, ip0->dst_address, 0, nm->outside_fib_index, 0);
326       if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv0,
327                                   &value0))
328         {
329           icmp_echo_header_t *echo0 = (icmp_echo_header_t *) (icmp0 + 1);
330           u16 icmp_id0 = echo0->identifier;
331           init_nat_k (&kv0, ip0->dst_address, icmp_id0, nm->outside_fib_index,
332                       NAT_PROTOCOL_ICMP);
333           int rv = clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0);
334           if (!rv)
335             {
336               ti = nat_value_get_thread_index (&value0);
337               if (ti != thread_index)
338                 {
339                   *required_thread_index = ti;
340                   return 1;
341                 }
342               si = nat_value_get_session_index (&value0);
343               s0 = pool_elt_at_index (nm->per_thread_data[ti].sessions, si);
344               new_dst_addr0 = s0->in2out.addr.as_u32;
345               vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
346               echo0->identifier = s0->in2out.port;
347               sum0 = icmp0->checksum;
348               sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port,
349                                      icmp_echo_header_t, identifier);
350               icmp0->checksum = ip_csum_fold (sum0);
351               goto change_addr;
352             }
353
354           return 1;
355         }
356
357       m0 = pool_elt_at_index (nm->static_mappings, value0.value);
358
359       new_dst_addr0 = m0->local_addr.as_u32;
360       if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0)
361         vnet_buffer (b0)->sw_if_index[VLIB_TX] = m0->fib_index;
362     }
363 change_addr:
364   /* Destination is behind the same NAT, use internal address and port */
365   if (new_dst_addr0)
366     {
367       old_dst_addr0 = ip0->dst_address.as_u32;
368       ip0->dst_address.as_u32 = new_dst_addr0;
369       sum0 = ip0->checksum;
370       sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0, ip4_header_t,
371                              dst_address);
372       ip0->checksum = ip_csum_fold (sum0);
373     }
374   return 0;
375 }
376 #endif
377
378 void nat44_ei_hairpinning_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b,
379                                          ip4_header_t *ip);
380
381 #ifndef CLIB_MARCH_VARIANT
382 void
383 nat44_ei_hairpinning_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b,
384                                     ip4_header_t *ip)
385 {
386   clib_bihash_kv_8_8_t kv, value;
387   nat44_ei_static_mapping_t *m;
388   u32 old_addr, new_addr;
389   ip_csum_t sum;
390
391   init_nat_k (&kv, ip->dst_address, 0, 0, 0);
392   if (clib_bihash_search_8_8 (&nm->static_mapping_by_external, &kv, &value))
393     return;
394
395   m = pool_elt_at_index (nm->static_mappings, value.value);
396
397   old_addr = ip->dst_address.as_u32;
398   new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
399   sum = ip->checksum;
400   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
401   ip->checksum = ip_csum_fold (sum);
402
403   if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0)
404     vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index;
405 }
406 #endif
407
408 VLIB_NODE_FN (nat44_ei_hairpin_src_node)
409 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
410 {
411   u32 n_left_from, *from, *to_next;
412   nat44_ei_hairpin_src_next_t next_index;
413   nat44_ei_main_t *nm = &nat44_ei_main;
414
415   from = vlib_frame_vector_args (frame);
416   n_left_from = frame->n_vectors;
417   next_index = node->cached_next_index;
418
419   while (n_left_from > 0)
420     {
421       u32 n_left_to_next;
422
423       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
424
425       while (n_left_from > 0 && n_left_to_next > 0)
426         {
427           u32 bi0;
428           vlib_buffer_t *b0;
429           u32 next0;
430           nat44_ei_interface_t *i;
431           u32 rx_sw_if_index0;
432           u32 tx_sw_if_index0;
433
434           /* speculatively enqueue b0 to the current next frame */
435           bi0 = from[0];
436           to_next[0] = bi0;
437           from += 1;
438           to_next += 1;
439           n_left_from -= 1;
440           n_left_to_next -= 1;
441
442           b0 = vlib_get_buffer (vm, bi0);
443           rx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
444           tx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
445
446           pool_foreach (i, nm->output_feature_interfaces)
447             {
448               /* Only packets from NAT inside interface */
449               if ((nat44_ei_interface_is_inside (i)) &&
450                   (rx_sw_if_index0 == i->sw_if_index))
451                 {
452                   if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) &
453                                      NAT44_EI_FLAG_HAIRPINNING))
454                     {
455                       if (PREDICT_TRUE (nm->num_workers > 1))
456                         {
457                           next0 = NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH;
458                           goto skip_feature_next;
459                         }
460                       else
461                         {
462                           next0 = NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT;
463                           goto skip_feature_next;
464                         }
465                     }
466                   break;
467                 }
468             }
469
470           vnet_feature_next (&next0, b0);
471         skip_feature_next:
472
473           if (next0 != NAT44_EI_HAIRPIN_SRC_NEXT_DROP)
474             {
475               vlib_increment_simple_counter (&nm->counters.hairpinning,
476                                              vm->thread_index, tx_sw_if_index0,
477                                              1);
478             }
479
480           /* verify speculative enqueue, maybe switch current next frame */
481           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
482                                            n_left_to_next, bi0, next0);
483         }
484
485       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
486     }
487
488   return frame->n_vectors;
489 }
490
491 VLIB_NODE_FN (nat44_ei_hairpin_dst_node)
492 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
493 {
494   u32 n_left_from, *from, *to_next;
495   u32 thread_index = vm->thread_index;
496   nat44_ei_hairpin_next_t next_index;
497   nat44_ei_main_t *nm = &nat44_ei_main;
498
499   from = vlib_frame_vector_args (frame);
500   n_left_from = frame->n_vectors;
501   next_index = node->cached_next_index;
502
503   while (n_left_from > 0)
504     {
505       u32 n_left_to_next;
506
507       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
508
509       while (n_left_from > 0 && n_left_to_next > 0)
510         {
511           u32 bi0;
512           vlib_buffer_t *b0;
513           u32 next0;
514           ip4_header_t *ip0;
515           u32 proto0;
516           u32 sw_if_index0;
517           u32 required_thread_index = thread_index;
518
519           /* speculatively enqueue b0 to the current next frame */
520           bi0 = from[0];
521           to_next[0] = bi0;
522           from += 1;
523           to_next += 1;
524           n_left_from -= 1;
525           n_left_to_next -= 1;
526
527           b0 = vlib_get_buffer (vm, bi0);
528           next0 = NAT44_EI_HAIRPIN_NEXT_LOOKUP;
529           ip0 = vlib_buffer_get_current (b0);
530           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
531
532           proto0 = ip_proto_to_nat_proto (ip0->protocol);
533
534           vnet_buffer (b0)->snat.flags = 0;
535           if (PREDICT_FALSE (nat44_ei_is_hairpinning (nm, &ip0->dst_address)))
536             {
537               if (proto0 == NAT_PROTOCOL_TCP || proto0 == NAT_PROTOCOL_UDP)
538                 {
539                   udp_header_t *udp0 = ip4_next_header (ip0);
540                   tcp_header_t *tcp0 = (tcp_header_t *) udp0;
541
542                   nat44_ei_hairpinning (vm, node, nm, thread_index, b0, ip0,
543                                         udp0, tcp0, proto0, 1 /* do_trace */,
544                                         &required_thread_index);
545                 }
546               else if (proto0 == NAT_PROTOCOL_ICMP)
547                 {
548                   icmp46_header_t *icmp0 = ip4_next_header (ip0);
549
550                   nat44_ei_icmp_hairpinning (nm, b0, thread_index, ip0, icmp0,
551                                              &required_thread_index);
552                 }
553               else
554                 {
555                   nat44_ei_hairpinning_unknown_proto (nm, b0, ip0);
556                 }
557
558               vnet_buffer (b0)->snat.flags = NAT44_EI_FLAG_HAIRPINNING;
559             }
560
561           if (thread_index != required_thread_index)
562             {
563               vnet_buffer (b0)->snat.required_thread_index =
564                 required_thread_index;
565               next0 = NAT44_EI_HAIRPIN_NEXT_HANDOFF;
566             }
567
568           if (next0 != NAT44_EI_HAIRPIN_NEXT_DROP)
569             {
570               vlib_increment_simple_counter (
571                 &nm->counters.hairpinning, vm->thread_index, sw_if_index0, 1);
572             }
573
574           /* verify speculative enqueue, maybe switch current next frame */
575           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
576                                            n_left_to_next, bi0, next0);
577         }
578
579       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
580     }
581
582   return frame->n_vectors;
583 }
584
585 VLIB_NODE_FN (nat44_ei_hairpinning_node)
586 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
587 {
588   u32 n_left_from, *from, *to_next;
589   u32 thread_index = vm->thread_index;
590   nat44_ei_hairpin_next_t next_index;
591   nat44_ei_main_t *nm = &nat44_ei_main;
592   vnet_feature_main_t *fm = &feature_main;
593   u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index;
594   vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index];
595
596   from = vlib_frame_vector_args (frame);
597   n_left_from = frame->n_vectors;
598   next_index = node->cached_next_index;
599
600   while (n_left_from > 0)
601     {
602       u32 n_left_to_next;
603
604       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
605
606       while (n_left_from > 0 && n_left_to_next > 0)
607         {
608           u32 bi0;
609           vlib_buffer_t *b0;
610           u32 next0;
611           ip4_header_t *ip0;
612           u32 proto0;
613           udp_header_t *udp0;
614           tcp_header_t *tcp0;
615           u32 sw_if_index0;
616           u32 required_thread_index = thread_index;
617
618           /* speculatively enqueue b0 to the current next frame */
619           bi0 = from[0];
620           to_next[0] = bi0;
621           from += 1;
622           to_next += 1;
623           n_left_from -= 1;
624           n_left_to_next -= 1;
625
626           b0 = vlib_get_buffer (vm, bi0);
627           ip0 = vlib_buffer_get_current (b0);
628           udp0 = ip4_next_header (ip0);
629           tcp0 = (tcp_header_t *) udp0;
630           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
631
632           proto0 = ip_proto_to_nat_proto (ip0->protocol);
633           int next0_resolved = 0;
634
635           if (nat44_ei_hairpinning (vm, node, nm, thread_index, b0, ip0, udp0,
636                                     tcp0, proto0, 1 /* do_trace */,
637                                     &required_thread_index))
638             {
639               next0 = NAT44_EI_HAIRPIN_NEXT_LOOKUP;
640               next0_resolved = 1;
641             }
642
643           if (thread_index != required_thread_index)
644             {
645               vnet_buffer (b0)->snat.required_thread_index =
646                 required_thread_index;
647               next0 = NAT44_EI_HAIRPIN_NEXT_HANDOFF;
648               next0_resolved = 1;
649             }
650
651           if (!next0_resolved)
652             vnet_get_config_data (&cm->config_main, &b0->current_config_index,
653                                   &next0, 0);
654
655           if (next0 != NAT44_EI_HAIRPIN_NEXT_DROP)
656             {
657               vlib_increment_simple_counter (
658                 &nm->counters.hairpinning, vm->thread_index, sw_if_index0, 1);
659             }
660
661           /* verify speculative enqueue, maybe switch current next frame */
662           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
663                                            n_left_to_next, bi0, next0);
664         }
665
666       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
667     }
668
669   return frame->n_vectors;
670 }
671
672 VLIB_NODE_FN (nat44_ei_hairpinning_dst_handoff_node)
673 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
674 {
675   return nat44_ei_hairpinning_handoff_fn_inline (
676     vm, node, frame, nat44_ei_main.hairpin_dst_fq_index);
677 }
678
679 VLIB_NODE_FN (nat44_ei_hairpinning_handoff_node)
680 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
681 {
682   return nat44_ei_hairpinning_handoff_fn_inline (
683     vm, node, frame, nat44_ei_main.hairpinning_fq_index);
684 }
685
686 VLIB_REGISTER_NODE (nat44_ei_hairpinning_dst_handoff_node) = {
687   .name = "nat44-ei-hairpin-dst-handoff",
688   .vector_size = sizeof (u32),
689   .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings),
690   .error_strings = nat44_ei_hairpinning_handoff_error_strings,
691   .format_trace = format_nat44_ei_hairpinning_handoff_trace,
692
693   .n_next_nodes = 1,
694
695   .next_nodes = {
696     [0] = "error-drop",
697   },
698 };
699
700 VLIB_REGISTER_NODE (nat44_ei_hairpinning_handoff_node) = {
701   .name = "nat44-ei-hairpinning-handoff",
702   .vector_size = sizeof (u32),
703   .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings),
704   .error_strings = nat44_ei_hairpinning_handoff_error_strings,
705   .format_trace = format_nat44_ei_hairpinning_handoff_trace,
706
707   .n_next_nodes = 1,
708
709   .next_nodes = {
710     [0] = "error-drop",
711   },
712 };
713
714 VLIB_REGISTER_NODE (nat44_ei_hairpin_src_node) = {
715   .name = "nat44-ei-hairpin-src",
716   .vector_size = sizeof (u32),
717   .type = VLIB_NODE_TYPE_INTERNAL,
718   .n_next_nodes = NAT44_EI_HAIRPIN_SRC_N_NEXT,
719   .next_nodes = {
720      [NAT44_EI_HAIRPIN_SRC_NEXT_DROP] = "error-drop",
721      [NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-ei-in2out-output",
722      [NAT44_EI_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output",
723      [NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-ei-in2out-output-worker-handoff",
724   },
725 };
726
727 VLIB_REGISTER_NODE (nat44_ei_hairpin_dst_node) = {
728   .name = "nat44-ei-hairpin-dst",
729   .vector_size = sizeof (u32),
730   .type = VLIB_NODE_TYPE_INTERNAL,
731   .format_trace = format_nat44_ei_hairpin_trace,
732   .n_next_nodes = NAT44_EI_HAIRPIN_N_NEXT,
733   .next_nodes = {
734     [NAT44_EI_HAIRPIN_NEXT_DROP] = "error-drop",
735     [NAT44_EI_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
736     [NAT44_EI_HAIRPIN_NEXT_HANDOFF] = "nat44-ei-hairpin-dst-handoff",
737   },
738 };
739
740 VLIB_REGISTER_NODE (nat44_ei_hairpinning_node) = {
741   .name = "nat44-ei-hairpinning",
742   .vector_size = sizeof (u32),
743   .type = VLIB_NODE_TYPE_INTERNAL,
744   .format_trace = format_nat44_ei_hairpin_trace,
745   .n_next_nodes = NAT44_EI_HAIRPIN_N_NEXT,
746   .next_nodes = {
747     [NAT44_EI_HAIRPIN_NEXT_DROP] = "error-drop",
748     [NAT44_EI_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
749     [NAT44_EI_HAIRPIN_NEXT_HANDOFF] = "nat44-ei-hairpinning-handoff",
750   },
751 };
752
753 /*
754  * fd.io coding-style-patch-verification: ON
755  *
756  * Local Variables:
757  * eval: (c-set-style "gnu")
758  * End:
759  */