virtio: enable the interrupt support for uio_pci_generic
[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 sw_if_index0;
432
433           /* speculatively enqueue b0 to the current next frame */
434           bi0 = from[0];
435           to_next[0] = bi0;
436           from += 1;
437           to_next += 1;
438           n_left_from -= 1;
439           n_left_to_next -= 1;
440
441           b0 = vlib_get_buffer (vm, bi0);
442           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
443           vnet_feature_next (&next0, b0);
444
445           pool_foreach (i, nm->output_feature_interfaces)
446             {
447               /* Only packets from NAT inside interface */
448               if ((nat44_ei_interface_is_inside (i)) &&
449                   (sw_if_index0 == i->sw_if_index))
450                 {
451                   if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) &
452                                      NAT44_EI_FLAG_HAIRPINNING))
453                     {
454                       if (PREDICT_TRUE (nm->num_workers > 1))
455                         next0 = NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH;
456                       else
457                         next0 = NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT;
458                     }
459                   break;
460                 }
461             }
462
463           if (next0 != NAT44_EI_HAIRPIN_SRC_NEXT_DROP)
464             {
465               vlib_increment_simple_counter (
466                 &nm->counters.hairpinning, vm->thread_index, sw_if_index0, 1);
467             }
468
469           /* verify speculative enqueue, maybe switch current next frame */
470           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
471                                            n_left_to_next, bi0, next0);
472         }
473
474       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
475     }
476
477   return frame->n_vectors;
478 }
479
480 VLIB_NODE_FN (nat44_ei_hairpin_dst_node)
481 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
482 {
483   u32 n_left_from, *from, *to_next;
484   u32 thread_index = vm->thread_index;
485   nat44_ei_hairpin_next_t next_index;
486   nat44_ei_main_t *nm = &nat44_ei_main;
487
488   from = vlib_frame_vector_args (frame);
489   n_left_from = frame->n_vectors;
490   next_index = node->cached_next_index;
491
492   while (n_left_from > 0)
493     {
494       u32 n_left_to_next;
495
496       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
497
498       while (n_left_from > 0 && n_left_to_next > 0)
499         {
500           u32 bi0;
501           vlib_buffer_t *b0;
502           u32 next0;
503           ip4_header_t *ip0;
504           u32 proto0;
505           u32 sw_if_index0;
506           u32 required_thread_index = thread_index;
507
508           /* speculatively enqueue b0 to the current next frame */
509           bi0 = from[0];
510           to_next[0] = bi0;
511           from += 1;
512           to_next += 1;
513           n_left_from -= 1;
514           n_left_to_next -= 1;
515
516           b0 = vlib_get_buffer (vm, bi0);
517           next0 = NAT44_EI_HAIRPIN_NEXT_LOOKUP;
518           ip0 = vlib_buffer_get_current (b0);
519           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
520
521           proto0 = ip_proto_to_nat_proto (ip0->protocol);
522
523           vnet_buffer (b0)->snat.flags = 0;
524           if (PREDICT_FALSE (nat44_ei_is_hairpinning (nm, &ip0->dst_address)))
525             {
526               if (proto0 == NAT_PROTOCOL_TCP || proto0 == NAT_PROTOCOL_UDP)
527                 {
528                   udp_header_t *udp0 = ip4_next_header (ip0);
529                   tcp_header_t *tcp0 = (tcp_header_t *) udp0;
530
531                   nat44_ei_hairpinning (vm, node, nm, thread_index, b0, ip0,
532                                         udp0, tcp0, proto0, 1 /* do_trace */,
533                                         &required_thread_index);
534                 }
535               else if (proto0 == NAT_PROTOCOL_ICMP)
536                 {
537                   icmp46_header_t *icmp0 = ip4_next_header (ip0);
538
539                   nat44_ei_icmp_hairpinning (nm, b0, thread_index, ip0, icmp0,
540                                              &required_thread_index);
541                 }
542               else
543                 {
544                   nat44_ei_hairpinning_unknown_proto (nm, b0, ip0);
545                 }
546
547               vnet_buffer (b0)->snat.flags = NAT44_EI_FLAG_HAIRPINNING;
548             }
549
550           if (thread_index != required_thread_index)
551             {
552               vnet_buffer (b0)->snat.required_thread_index =
553                 required_thread_index;
554               next0 = NAT44_EI_HAIRPIN_NEXT_HANDOFF;
555             }
556
557           if (next0 != NAT44_EI_HAIRPIN_NEXT_DROP)
558             {
559               vlib_increment_simple_counter (
560                 &nm->counters.hairpinning, vm->thread_index, sw_if_index0, 1);
561             }
562
563           /* verify speculative enqueue, maybe switch current next frame */
564           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
565                                            n_left_to_next, bi0, next0);
566         }
567
568       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
569     }
570
571   return frame->n_vectors;
572 }
573
574 VLIB_NODE_FN (nat44_ei_hairpinning_node)
575 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
576 {
577   u32 n_left_from, *from, *to_next;
578   u32 thread_index = vm->thread_index;
579   nat44_ei_hairpin_next_t next_index;
580   nat44_ei_main_t *nm = &nat44_ei_main;
581   vnet_feature_main_t *fm = &feature_main;
582   u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index;
583   vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index];
584
585   from = vlib_frame_vector_args (frame);
586   n_left_from = frame->n_vectors;
587   next_index = node->cached_next_index;
588
589   while (n_left_from > 0)
590     {
591       u32 n_left_to_next;
592
593       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
594
595       while (n_left_from > 0 && n_left_to_next > 0)
596         {
597           u32 bi0;
598           vlib_buffer_t *b0;
599           u32 next0;
600           ip4_header_t *ip0;
601           u32 proto0;
602           udp_header_t *udp0;
603           tcp_header_t *tcp0;
604           u32 sw_if_index0;
605           u32 required_thread_index = thread_index;
606
607           /* speculatively enqueue b0 to the current next frame */
608           bi0 = from[0];
609           to_next[0] = bi0;
610           from += 1;
611           to_next += 1;
612           n_left_from -= 1;
613           n_left_to_next -= 1;
614
615           b0 = vlib_get_buffer (vm, bi0);
616           ip0 = vlib_buffer_get_current (b0);
617           udp0 = ip4_next_header (ip0);
618           tcp0 = (tcp_header_t *) udp0;
619           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
620
621           proto0 = ip_proto_to_nat_proto (ip0->protocol);
622           int next0_resolved = 0;
623
624           if (nat44_ei_hairpinning (vm, node, nm, thread_index, b0, ip0, udp0,
625                                     tcp0, proto0, 1 /* do_trace */,
626                                     &required_thread_index))
627             {
628               next0 = NAT44_EI_HAIRPIN_NEXT_LOOKUP;
629               next0_resolved = 1;
630             }
631
632           if (thread_index != required_thread_index)
633             {
634               vnet_buffer (b0)->snat.required_thread_index =
635                 required_thread_index;
636               next0 = NAT44_EI_HAIRPIN_NEXT_HANDOFF;
637               next0_resolved = 1;
638             }
639
640           if (!next0_resolved)
641             vnet_get_config_data (&cm->config_main, &b0->current_config_index,
642                                   &next0, 0);
643
644           if (next0 != NAT44_EI_HAIRPIN_NEXT_DROP)
645             {
646               vlib_increment_simple_counter (
647                 &nm->counters.hairpinning, vm->thread_index, sw_if_index0, 1);
648             }
649
650           /* verify speculative enqueue, maybe switch current next frame */
651           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
652                                            n_left_to_next, bi0, next0);
653         }
654
655       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
656     }
657
658   return frame->n_vectors;
659 }
660
661 VLIB_NODE_FN (nat44_ei_hairpinning_dst_handoff_node)
662 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
663 {
664   return nat44_ei_hairpinning_handoff_fn_inline (
665     vm, node, frame, nat44_ei_main.hairpin_dst_fq_index);
666 }
667
668 VLIB_NODE_FN (nat44_ei_hairpinning_handoff_node)
669 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
670 {
671   return nat44_ei_hairpinning_handoff_fn_inline (
672     vm, node, frame, nat44_ei_main.hairpinning_fq_index);
673 }
674
675 VLIB_REGISTER_NODE (nat44_ei_hairpinning_dst_handoff_node) = {
676   .name = "nat44-ei-hairpin-dst-handoff",
677   .vector_size = sizeof (u32),
678   .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings),
679   .error_strings = nat44_ei_hairpinning_handoff_error_strings,
680   .format_trace = format_nat44_ei_hairpinning_handoff_trace,
681
682   .n_next_nodes = 1,
683
684   .next_nodes = {
685     [0] = "error-drop",
686   },
687 };
688
689 VLIB_REGISTER_NODE (nat44_ei_hairpinning_handoff_node) = {
690   .name = "nat44-ei-hairpinning-handoff",
691   .vector_size = sizeof (u32),
692   .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings),
693   .error_strings = nat44_ei_hairpinning_handoff_error_strings,
694   .format_trace = format_nat44_ei_hairpinning_handoff_trace,
695
696   .n_next_nodes = 1,
697
698   .next_nodes = {
699     [0] = "error-drop",
700   },
701 };
702
703 VLIB_REGISTER_NODE (nat44_ei_hairpin_src_node) = {
704   .name = "nat44-ei-hairpin-src",
705   .vector_size = sizeof (u32),
706   .type = VLIB_NODE_TYPE_INTERNAL,
707   .n_next_nodes = NAT44_EI_HAIRPIN_SRC_N_NEXT,
708   .next_nodes = {
709      [NAT44_EI_HAIRPIN_SRC_NEXT_DROP] = "error-drop",
710      [NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-ei-in2out-output",
711      [NAT44_EI_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output",
712      [NAT44_EI_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-ei-in2out-output-worker-handoff",
713   },
714 };
715
716 VLIB_REGISTER_NODE (nat44_ei_hairpin_dst_node) = {
717   .name = "nat44-ei-hairpin-dst",
718   .vector_size = sizeof (u32),
719   .type = VLIB_NODE_TYPE_INTERNAL,
720   .format_trace = format_nat44_ei_hairpin_trace,
721   .n_next_nodes = NAT44_EI_HAIRPIN_N_NEXT,
722   .next_nodes = {
723     [NAT44_EI_HAIRPIN_NEXT_DROP] = "error-drop",
724     [NAT44_EI_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
725     [NAT44_EI_HAIRPIN_NEXT_HANDOFF] = "nat44-ei-hairpin-dst-handoff",
726   },
727 };
728
729 VLIB_REGISTER_NODE (nat44_ei_hairpinning_node) = {
730   .name = "nat44-ei-hairpinning",
731   .vector_size = sizeof (u32),
732   .type = VLIB_NODE_TYPE_INTERNAL,
733   .format_trace = format_nat44_ei_hairpin_trace,
734   .n_next_nodes = NAT44_EI_HAIRPIN_N_NEXT,
735   .next_nodes = {
736     [NAT44_EI_HAIRPIN_NEXT_DROP] = "error-drop",
737     [NAT44_EI_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
738     [NAT44_EI_HAIRPIN_NEXT_HANDOFF] = "nat44-ei-hairpinning-handoff",
739   },
740 };
741
742 /*
743  * fd.io coding-style-patch-verification: ON
744  *
745  * Local Variables:
746  * eval: (c-set-style "gnu")
747  * End:
748  */