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