nat: twice-nat static mapping pool address
[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,
98                   snat_main_t * sm, vlib_buffer_t * b0, ip4_header_t * ip0,
99                   udp_header_t * udp0, tcp_header_t * tcp0, u32 proto0,
100                   int is_ed, int do_trace)
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, ti = 0, 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   /* 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       if (is_ed)
131         {
132           clib_bihash_kv_16_8_t ed_kv, ed_value;
133           init_ed_k (&ed_kv, ip0->dst_address, udp0->dst_port,
134                      ip0->src_address, udp0->src_port, sm->outside_fib_index,
135                      ip0->protocol);
136           rv = clib_bihash_search_16_8 (&sm->out2in_ed, &ed_kv, &ed_value);
137           ASSERT (ti == ed_value_get_thread_index (&ed_value));
138           si = ed_value_get_session_index (&ed_value);
139         }
140       else
141         {
142
143           init_nat_k (&kv0, ip0->dst_address, udp0->dst_port,
144                       sm->outside_fib_index, proto0);
145           rv =
146             clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0,
147                                     &value0);
148           si = value0.value;
149         }
150       if (rv)
151         {
152           rv = 0;
153           goto trace;
154         }
155
156       s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
157       new_dst_addr0 = s0->in2out.addr.as_u32;
158       new_dst_port0 = s0->in2out.port;
159       vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
160     }
161
162   /* Destination is behind the same NAT, use internal address and port */
163   if (new_dst_addr0)
164     {
165       old_dst_addr0 = ip0->dst_address.as_u32;
166       ip0->dst_address.as_u32 = new_dst_addr0;
167       sum0 = ip0->checksum;
168       sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
169                              ip4_header_t, dst_address);
170       ip0->checksum = ip_csum_fold (sum0);
171
172       old_dst_port0 = tcp0->dst;
173       if (PREDICT_TRUE (new_dst_port0 != old_dst_port0))
174         {
175           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
176             {
177               tcp0->dst = new_dst_port0;
178               sum0 = tcp0->checksum;
179               sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
180                                      ip4_header_t, dst_address);
181               sum0 = ip_csum_update (sum0, old_dst_port0, new_dst_port0,
182                                      ip4_header_t /* cheat */ , length);
183               tcp0->checksum = ip_csum_fold (sum0);
184             }
185           else
186             {
187               udp0->dst_port = new_dst_port0;
188               udp0->checksum = 0;
189             }
190         }
191       else
192         {
193           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
194             {
195               sum0 = tcp0->checksum;
196               sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
197                                      ip4_header_t, dst_address);
198               tcp0->checksum = ip_csum_fold (sum0);
199             }
200         }
201       rv = 1;
202       goto trace;
203     }
204   rv = 0;
205 trace:
206   if (do_trace && PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
207                                  && (b0->flags & VLIB_BUFFER_IS_TRACED)))
208     {
209       nat_hairpin_trace_t *t = vlib_add_trace (vm, node, b0, sizeof (*t));
210       t->addr.as_u32 = new_dst_addr0;
211       t->port = new_dst_port0;
212       t->fib_index = vnet_buffer (b0)->sw_if_index[VLIB_TX];
213       if (s0)
214         {
215           t->session_index = si;
216         }
217       else
218         {
219           t->session_index = ~0;
220         }
221     }
222   return rv;
223 }
224 #endif
225
226 #ifndef CLIB_MARCH_VARIANT
227 u32
228 snat_icmp_hairpinning (snat_main_t * sm,
229                        vlib_buffer_t * b0,
230                        ip4_header_t * ip0, icmp46_header_t * icmp0, int is_ed)
231 {
232   clib_bihash_kv_8_8_t kv0, value0;
233   u32 old_dst_addr0, new_dst_addr0;
234   u32 old_addr0, new_addr0;
235   u16 old_port0, new_port0;
236   u16 old_checksum0, new_checksum0;
237   u32 si, ti = 0;
238   ip_csum_t sum0;
239   snat_session_t *s0;
240   snat_static_mapping_t *m0;
241
242   if (icmp_type_is_error_message
243       (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags))
244     {
245       ip4_header_t *inner_ip0 = 0;
246       tcp_udp_header_t *l4_header = 0;
247
248       inner_ip0 = (ip4_header_t *) ((icmp_echo_header_t *) (icmp0 + 1) + 1);
249       l4_header = ip4_next_header (inner_ip0);
250       u32 protocol = ip_proto_to_nat_proto (inner_ip0->protocol);
251
252       if (protocol != NAT_PROTOCOL_TCP && protocol != NAT_PROTOCOL_UDP)
253         return 1;
254
255       if (is_ed)
256         {
257           clib_bihash_kv_16_8_t ed_kv, ed_value;
258           init_ed_k (&ed_kv, ip0->dst_address, l4_header->src_port,
259                      ip0->src_address, l4_header->dst_port,
260                      sm->outside_fib_index, inner_ip0->protocol);
261           if (clib_bihash_search_16_8 (&sm->out2in_ed, &ed_kv, &ed_value))
262             return 1;
263           ASSERT (ti == ed_value_get_thread_index (&ed_value));
264           si = ed_value_get_session_index (&ed_value);
265         }
266       else
267         {
268           init_nat_k (&kv0, ip0->dst_address, l4_header->src_port,
269                       sm->outside_fib_index, protocol);
270           if (clib_bihash_search_8_8
271               (&sm->per_thread_data[ti].out2in, &kv0, &value0))
272             return 1;
273           si = value0.value;
274         }
275       s0 = pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
276       new_dst_addr0 = s0->in2out.addr.as_u32;
277       vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->in2out.fib_index;
278
279       /* update inner source IP address */
280       old_addr0 = inner_ip0->src_address.as_u32;
281       inner_ip0->src_address.as_u32 = new_dst_addr0;
282       new_addr0 = inner_ip0->src_address.as_u32;
283       sum0 = icmp0->checksum;
284       sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
285                              src_address);
286       icmp0->checksum = ip_csum_fold (sum0);
287
288       /* update inner IP header checksum */
289       old_checksum0 = inner_ip0->checksum;
290       sum0 = inner_ip0->checksum;
291       sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
292                              src_address);
293       inner_ip0->checksum = ip_csum_fold (sum0);
294       new_checksum0 = inner_ip0->checksum;
295       sum0 = icmp0->checksum;
296       sum0 = ip_csum_update (sum0, old_checksum0, new_checksum0, ip4_header_t,
297                              checksum);
298       icmp0->checksum = ip_csum_fold (sum0);
299
300       /* update inner source port */
301       old_port0 = l4_header->src_port;
302       l4_header->src_port = s0->in2out.port;
303       new_port0 = l4_header->src_port;
304       sum0 = icmp0->checksum;
305       sum0 = ip_csum_update (sum0, old_port0, new_port0, tcp_udp_header_t,
306                              src_port);
307       icmp0->checksum = ip_csum_fold (sum0);
308     }
309   else
310     {
311       init_nat_k (&kv0, ip0->dst_address, 0, sm->outside_fib_index, 0);
312       if (clib_bihash_search_8_8
313           (&sm->static_mapping_by_external, &kv0, &value0))
314         {
315           if (!is_ed)
316             {
317               icmp_echo_header_t *echo0 = (icmp_echo_header_t *) (icmp0 + 1);
318               u16 icmp_id0 = echo0->identifier;
319               init_nat_k (&kv0, ip0->dst_address, icmp_id0,
320                           sm->outside_fib_index, NAT_PROTOCOL_ICMP);
321               if (sm->num_workers > 1)
322                 ti =
323                   (clib_net_to_host_u16 (icmp_id0) -
324                    1024) / sm->port_per_thread;
325               else
326                 ti = sm->num_workers;
327               int rv =
328                 clib_bihash_search_8_8 (&sm->per_thread_data[ti].out2in, &kv0,
329                                         &value0);
330               if (!rv)
331                 {
332                   si = value0.value;
333                   s0 =
334                     pool_elt_at_index (sm->per_thread_data[ti].sessions, si);
335                   new_dst_addr0 = s0->in2out.addr.as_u32;
336                   vnet_buffer (b0)->sw_if_index[VLIB_TX] =
337                     s0->in2out.fib_index;
338                   echo0->identifier = s0->in2out.port;
339                   sum0 = icmp0->checksum;
340                   sum0 = ip_csum_update (sum0, icmp_id0, s0->in2out.port,
341                                          icmp_echo_header_t, identifier);
342                   icmp0->checksum = ip_csum_fold (sum0);
343                   goto change_addr;
344                 }
345             }
346
347           return 1;
348         }
349
350       m0 = pool_elt_at_index (sm->static_mappings, value0.value);
351
352       new_dst_addr0 = m0->local_addr.as_u32;
353       if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0)
354         vnet_buffer (b0)->sw_if_index[VLIB_TX] = m0->fib_index;
355     }
356 change_addr:
357   /* Destination is behind the same NAT, use internal address and port */
358   if (new_dst_addr0)
359     {
360       old_dst_addr0 = ip0->dst_address.as_u32;
361       ip0->dst_address.as_u32 = new_dst_addr0;
362       sum0 = ip0->checksum;
363       sum0 = ip_csum_update (sum0, old_dst_addr0, new_dst_addr0,
364                              ip4_header_t, dst_address);
365       ip0->checksum = ip_csum_fold (sum0);
366     }
367   return 0;
368 }
369 #endif
370
371 #ifndef CLIB_MARCH_VARIANT
372 void
373 nat_hairpinning_sm_unknown_proto (snat_main_t * sm,
374                                   vlib_buffer_t * b, ip4_header_t * ip)
375 {
376   clib_bihash_kv_8_8_t kv, value;
377   snat_static_mapping_t *m;
378   u32 old_addr, new_addr;
379   ip_csum_t sum;
380
381   init_nat_k (&kv, ip->dst_address, 0, 0, 0);
382   if (clib_bihash_search_8_8 (&sm->static_mapping_by_external, &kv, &value))
383     return;
384
385   m = pool_elt_at_index (sm->static_mappings, value.value);
386
387   old_addr = ip->dst_address.as_u32;
388   new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
389   sum = ip->checksum;
390   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
391   ip->checksum = ip_csum_fold (sum);
392
393   if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0)
394     vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index;
395 }
396 #endif
397
398 #ifndef CLIB_MARCH_VARIANT
399 void
400 nat44_ed_hairpinning_unknown_proto (snat_main_t * sm,
401                                     vlib_buffer_t * b, ip4_header_t * ip)
402 {
403   u32 old_addr, new_addr = 0, ti = 0;
404   clib_bihash_kv_8_8_t kv, value;
405   clib_bihash_kv_16_8_t s_kv, s_value;
406   snat_static_mapping_t *m;
407   ip_csum_t sum;
408   snat_session_t *s;
409
410   if (sm->num_workers > 1)
411     ti = sm->worker_out2in_cb (b, ip, sm->outside_fib_index, 0);
412   else
413     ti = sm->num_workers;
414
415   old_addr = ip->dst_address.as_u32;
416   init_ed_k (&s_kv, ip->dst_address, 0, ip->src_address, 0,
417              sm->outside_fib_index, ip->protocol);
418   if (clib_bihash_search_16_8 (&sm->out2in_ed, &s_kv, &s_value))
419     {
420       init_nat_k (&kv, ip->dst_address, 0, 0, 0);
421       if (clib_bihash_search_8_8
422           (&sm->static_mapping_by_external, &kv, &value))
423         return;
424
425       m = pool_elt_at_index (sm->static_mappings, value.value);
426       if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0)
427         vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index;
428       new_addr = ip->dst_address.as_u32 = m->local_addr.as_u32;
429     }
430   else
431     {
432       ASSERT (ti == ed_value_get_thread_index (&s_value));
433       s =
434         pool_elt_at_index (sm->per_thread_data[ti].sessions,
435                            ed_value_get_session_index (&s_value));
436       if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0)
437         vnet_buffer (b)->sw_if_index[VLIB_TX] = s->in2out.fib_index;
438       new_addr = ip->dst_address.as_u32 = s->in2out.addr.as_u32;
439     }
440   sum = ip->checksum;
441   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, dst_address);
442   ip->checksum = ip_csum_fold (sum);
443 }
444 #endif
445
446 static inline uword
447 nat44_hairpinning_fn_inline (vlib_main_t * vm,
448                              vlib_node_runtime_t * node,
449                              vlib_frame_t * frame, int is_ed)
450 {
451   u32 n_left_from, *from, *to_next;
452   nat_hairpin_next_t next_index;
453   snat_main_t *sm = &snat_main;
454   vnet_feature_main_t *fm = &feature_main;
455   u8 arc_index = vnet_feat_arc_ip4_local.feature_arc_index;
456   vnet_feature_config_main_t *cm = &fm->feature_config_mains[arc_index];
457
458   from = vlib_frame_vector_args (frame);
459   n_left_from = frame->n_vectors;
460   next_index = node->cached_next_index;
461
462   while (n_left_from > 0)
463     {
464       u32 n_left_to_next;
465
466       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
467
468       while (n_left_from > 0 && n_left_to_next > 0)
469         {
470           u32 bi0;
471           vlib_buffer_t *b0;
472           u32 next0;
473           ip4_header_t *ip0;
474           u32 proto0;
475           udp_header_t *udp0;
476           tcp_header_t *tcp0;
477           u32 sw_if_index0;
478
479           /* speculatively enqueue b0 to the current next frame */
480           bi0 = from[0];
481           to_next[0] = bi0;
482           from += 1;
483           to_next += 1;
484           n_left_from -= 1;
485           n_left_to_next -= 1;
486
487           b0 = vlib_get_buffer (vm, bi0);
488           ip0 = vlib_buffer_get_current (b0);
489           udp0 = ip4_next_header (ip0);
490           tcp0 = (tcp_header_t *) udp0;
491           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
492
493           proto0 = ip_proto_to_nat_proto (ip0->protocol);
494
495           vnet_get_config_data (&cm->config_main, &b0->current_config_index,
496                                 &next0, 0);
497
498           if (snat_hairpinning
499               (vm, node, sm, b0, ip0, udp0, tcp0, proto0, is_ed,
500                1 /* do_trace */ ))
501             next0 = NAT_HAIRPIN_NEXT_LOOKUP;
502
503           if (next0 != NAT_HAIRPIN_NEXT_DROP)
504             {
505               vlib_increment_simple_counter (&sm->counters.hairpinning,
506                                              vm->thread_index, sw_if_index0,
507                                              1);
508             }
509
510           /* verify speculative enqueue, maybe switch current next frame */
511           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
512                                            to_next, n_left_to_next,
513                                            bi0, next0);
514         }
515
516       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
517     }
518
519   return frame->n_vectors;
520 }
521
522 VLIB_NODE_FN (nat44_hairpinning_node) (vlib_main_t * vm,
523                                        vlib_node_runtime_t * node,
524                                        vlib_frame_t * frame)
525 {
526   return nat44_hairpinning_fn_inline (vm, node, frame, 0);
527 }
528
529 /* *INDENT-OFF* */
530 VLIB_REGISTER_NODE (nat44_hairpinning_node) = {
531   .name = "nat44-hairpinning",
532   .vector_size = sizeof (u32),
533   .type = VLIB_NODE_TYPE_INTERNAL,
534   .format_trace = format_nat_hairpin_trace,
535   .n_next_nodes = NAT_HAIRPIN_N_NEXT,
536   .next_nodes = {
537     [NAT_HAIRPIN_NEXT_DROP] = "error-drop",
538     [NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
539   },
540 };
541 /* *INDENT-ON* */
542
543 VLIB_NODE_FN (nat44_ed_hairpinning_node) (vlib_main_t * vm,
544                                           vlib_node_runtime_t * node,
545                                           vlib_frame_t * frame)
546 {
547   return nat44_hairpinning_fn_inline (vm, node, frame, 1);
548 }
549
550 /* *INDENT-OFF* */
551 VLIB_REGISTER_NODE (nat44_ed_hairpinning_node) = {
552   .name = "nat44-ed-hairpinning",
553   .vector_size = sizeof (u32),
554   .type = VLIB_NODE_TYPE_INTERNAL,
555   .format_trace = format_nat_hairpin_trace,
556   .n_next_nodes = NAT_HAIRPIN_N_NEXT,
557   .next_nodes = {
558     [NAT_HAIRPIN_NEXT_DROP] = "error-drop",
559     [NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
560   },
561 };
562 /* *INDENT-ON* */
563
564 static inline uword
565 snat_hairpin_dst_fn_inline (vlib_main_t * vm,
566                             vlib_node_runtime_t * node,
567                             vlib_frame_t * frame, int is_ed)
568 {
569   u32 n_left_from, *from, *to_next;
570   nat_hairpin_next_t next_index;
571   snat_main_t *sm = &snat_main;
572
573   from = vlib_frame_vector_args (frame);
574   n_left_from = frame->n_vectors;
575   next_index = node->cached_next_index;
576
577   while (n_left_from > 0)
578     {
579       u32 n_left_to_next;
580
581       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
582
583       while (n_left_from > 0 && n_left_to_next > 0)
584         {
585           u32 bi0;
586           vlib_buffer_t *b0;
587           u32 next0;
588           ip4_header_t *ip0;
589           u32 proto0;
590           u32 sw_if_index0;
591
592           /* speculatively enqueue b0 to the current next frame */
593           bi0 = from[0];
594           to_next[0] = bi0;
595           from += 1;
596           to_next += 1;
597           n_left_from -= 1;
598           n_left_to_next -= 1;
599
600           b0 = vlib_get_buffer (vm, bi0);
601           next0 = NAT_HAIRPIN_NEXT_LOOKUP;
602           ip0 = vlib_buffer_get_current (b0);
603           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
604
605           proto0 = ip_proto_to_nat_proto (ip0->protocol);
606
607           vnet_buffer (b0)->snat.flags = 0;
608           if (PREDICT_FALSE (is_hairpinning (sm, &ip0->dst_address)))
609             {
610               if (proto0 == NAT_PROTOCOL_TCP || proto0 == NAT_PROTOCOL_UDP)
611                 {
612                   udp_header_t *udp0 = ip4_next_header (ip0);
613                   tcp_header_t *tcp0 = (tcp_header_t *) udp0;
614
615                   snat_hairpinning (vm, node, sm, b0, ip0, udp0, tcp0, proto0,
616                                     is_ed, 1 /* do_trace */ );
617                 }
618               else if (proto0 == NAT_PROTOCOL_ICMP)
619                 {
620                   icmp46_header_t *icmp0 = ip4_next_header (ip0);
621
622                   snat_icmp_hairpinning (sm, b0, ip0, icmp0, is_ed);
623                 }
624               else
625                 {
626                   if (is_ed)
627                     nat44_ed_hairpinning_unknown_proto (sm, b0, ip0);
628                   else
629                     nat_hairpinning_sm_unknown_proto (sm, b0, ip0);
630                 }
631
632               vnet_buffer (b0)->snat.flags = SNAT_FLAG_HAIRPINNING;
633             }
634
635
636           if (next0 != NAT_HAIRPIN_NEXT_DROP)
637             {
638               vlib_increment_simple_counter (&sm->counters.hairpinning,
639                                              vm->thread_index, sw_if_index0,
640                                              1);
641             }
642
643           /* verify speculative enqueue, maybe switch current next frame */
644           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
645                                            to_next, n_left_to_next,
646                                            bi0, next0);
647         }
648
649       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
650     }
651
652   return frame->n_vectors;
653 }
654
655 VLIB_NODE_FN (snat_hairpin_dst_node) (vlib_main_t * vm,
656                                       vlib_node_runtime_t * node,
657                                       vlib_frame_t * frame)
658 {
659   return snat_hairpin_dst_fn_inline (vm, node, frame, 0);
660 }
661
662 /* *INDENT-OFF* */
663 VLIB_REGISTER_NODE (snat_hairpin_dst_node) = {
664   .name = "nat44-hairpin-dst",
665   .vector_size = sizeof (u32),
666   .type = VLIB_NODE_TYPE_INTERNAL,
667   .format_trace = format_nat_hairpin_trace,
668   .n_next_nodes = NAT_HAIRPIN_N_NEXT,
669   .next_nodes = {
670     [NAT_HAIRPIN_NEXT_DROP] = "error-drop",
671     [NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
672   },
673 };
674 /* *INDENT-ON* */
675
676 VLIB_NODE_FN (nat44_ed_hairpin_dst_node) (vlib_main_t * vm,
677                                           vlib_node_runtime_t * node,
678                                           vlib_frame_t * frame)
679 {
680   return snat_hairpin_dst_fn_inline (vm, node, frame, 1);
681 }
682
683 /* *INDENT-OFF* */
684 VLIB_REGISTER_NODE (nat44_ed_hairpin_dst_node) = {
685   .name = "nat44-ed-hairpin-dst",
686   .vector_size = sizeof (u32),
687   .type = VLIB_NODE_TYPE_INTERNAL,
688   .format_trace = format_nat_hairpin_trace,
689   .n_next_nodes = NAT_HAIRPIN_N_NEXT,
690   .next_nodes = {
691     [NAT_HAIRPIN_NEXT_DROP] = "error-drop",
692     [NAT_HAIRPIN_NEXT_LOOKUP] = "ip4-lookup",
693   },
694 };
695 /* *INDENT-ON* */
696
697 static inline uword
698 snat_hairpin_src_fn_inline (vlib_main_t * vm,
699                             vlib_node_runtime_t * node,
700                             vlib_frame_t * frame, int is_ed)
701 {
702   u32 n_left_from, *from, *to_next;
703   snat_hairpin_src_next_t next_index;
704   snat_main_t *sm = &snat_main;
705
706   from = vlib_frame_vector_args (frame);
707   n_left_from = frame->n_vectors;
708   next_index = node->cached_next_index;
709
710   while (n_left_from > 0)
711     {
712       u32 n_left_to_next;
713
714       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
715
716       while (n_left_from > 0 && n_left_to_next > 0)
717         {
718           u32 bi0;
719           vlib_buffer_t *b0;
720           u32 next0;
721           snat_interface_t *i;
722           u32 sw_if_index0;
723
724           /* speculatively enqueue b0 to the current next frame */
725           bi0 = from[0];
726           to_next[0] = bi0;
727           from += 1;
728           to_next += 1;
729           n_left_from -= 1;
730           n_left_to_next -= 1;
731
732           b0 = vlib_get_buffer (vm, bi0);
733           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
734           vnet_feature_next (&next0, b0);
735
736           /* *INDENT-OFF* */
737           pool_foreach (i, sm->output_feature_interfaces,
738           ({
739             /* Only packets from NAT inside interface */
740             if ((nat_interface_is_inside(i)) && (sw_if_index0 == i->sw_if_index))
741               {
742                 if (PREDICT_FALSE ((vnet_buffer (b0)->snat.flags) &
743                                     SNAT_FLAG_HAIRPINNING))
744                   {
745                     if (PREDICT_TRUE (sm->num_workers > 1))
746                       next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH;
747                     else
748                       next0 = SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT;
749                   }
750                 break;
751               }
752           }));
753           /* *INDENT-ON* */
754
755           if (next0 != SNAT_HAIRPIN_SRC_NEXT_DROP)
756             {
757               vlib_increment_simple_counter (&sm->counters.hairpinning,
758                                              vm->thread_index, sw_if_index0,
759                                              1);
760             }
761
762           /* verify speculative enqueue, maybe switch current next frame */
763           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
764                                            to_next, n_left_to_next,
765                                            bi0, next0);
766         }
767
768       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
769     }
770
771   return frame->n_vectors;
772 }
773
774 VLIB_NODE_FN (snat_hairpin_src_node) (vlib_main_t * vm,
775                                       vlib_node_runtime_t * node,
776                                       vlib_frame_t * frame)
777 {
778   return snat_hairpin_src_fn_inline (vm, node, frame, 0);
779 }
780
781 /* *INDENT-OFF* */
782 VLIB_REGISTER_NODE (snat_hairpin_src_node) = {
783   .name = "nat44-hairpin-src",
784   .vector_size = sizeof (u32),
785   .type = VLIB_NODE_TYPE_INTERNAL,
786   .n_next_nodes = SNAT_HAIRPIN_SRC_N_NEXT,
787   .next_nodes = {
788      [SNAT_HAIRPIN_SRC_NEXT_DROP] = "error-drop",
789      [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-in2out-output",
790      [SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output",
791      [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-in2out-output-worker-handoff",
792   },
793 };
794 /* *INDENT-ON* */
795
796 VLIB_NODE_FN (nat44_ed_hairpin_src_node) (vlib_main_t * vm,
797                                           vlib_node_runtime_t * node,
798                                           vlib_frame_t * frame)
799 {
800   return snat_hairpin_src_fn_inline (vm, node, frame, 1);
801 }
802
803 /* *INDENT-OFF* */
804 VLIB_REGISTER_NODE (nat44_ed_hairpin_src_node) = {
805   .name = "nat44-ed-hairpin-src",
806   .vector_size = sizeof (u32),
807   .type = VLIB_NODE_TYPE_INTERNAL,
808   .n_next_nodes = SNAT_HAIRPIN_SRC_N_NEXT,
809   .next_nodes = {
810      [SNAT_HAIRPIN_SRC_NEXT_DROP] = "error-drop",
811      [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT] = "nat44-ed-in2out-output",
812      [SNAT_HAIRPIN_SRC_NEXT_INTERFACE_OUTPUT] = "interface-output",
813      [SNAT_HAIRPIN_SRC_NEXT_SNAT_IN2OUT_WH] = "nat44-in2out-output-worker-handoff",
814   },
815 };
816 /* *INDENT-ON* */
817
818 /*
819  * fd.io coding-style-patch-verification: ON
820  *
821  * Local Variables:
822  * eval: (c-set-style "gnu")
823  * End:
824  */