nat: static mappings in flow hash
[vpp.git] / src / plugins / nat / nat44-ei / nat44_ei_in2out.c
1 /*
2  * Copyright (c) 2016 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 EI inside to outside network translation
18  */
19
20 #include <vlib/vlib.h>
21
22 #include <vnet/vnet.h>
23 #include <vnet/ip/ip.h>
24 #include <vnet/ethernet/ethernet.h>
25 #include <vnet/udp/udp_local.h>
26 #include <vnet/fib/ip4_fib.h>
27
28 #include <vppinfra/hash.h>
29 #include <vppinfra/error.h>
30
31 #include <nat/lib/log.h>
32 #include <nat/lib/nat_syslog.h>
33 #include <nat/lib/ipfix_logging.h>
34 #include <nat/lib/nat_inlines.h>
35 #include <nat/nat44-ei/nat44_ei_inlines.h>
36 #include <nat/nat44-ei/nat44_ei.h>
37 #include <nat/nat44-ei/nat44_ei_hairpinning.h>
38
39 typedef struct
40 {
41   u32 sw_if_index;
42   u32 next_index;
43   u32 session_index;
44   u32 is_slow_path;
45   u32 is_hairpinning;
46 } nat44_ei_in2out_trace_t;
47
48 /* packet trace format function */
49 static u8 *
50 format_nat44_ei_in2out_trace (u8 *s, va_list *args)
51 {
52   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
53   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
54   nat44_ei_in2out_trace_t *t = va_arg (*args, nat44_ei_in2out_trace_t *);
55   char *tag;
56
57   tag = t->is_slow_path ? "NAT44_IN2OUT_SLOW_PATH" : "NAT44_IN2OUT_FAST_PATH";
58
59   s = format (s, "%s: sw_if_index %d, next index %d, session %d", tag,
60               t->sw_if_index, t->next_index, t->session_index);
61   if (t->is_hairpinning)
62     {
63       s = format (s, ", with-hairpinning");
64     }
65
66   return s;
67 }
68
69 static u8 *
70 format_nat44_ei_in2out_fast_trace (u8 *s, va_list *args)
71 {
72   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
73   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
74   nat44_ei_in2out_trace_t *t = va_arg (*args, nat44_ei_in2out_trace_t *);
75
76   s = format (s, "NAT44_IN2OUT_FAST: sw_if_index %d, next index %d",
77               t->sw_if_index, t->next_index);
78
79   return s;
80 }
81
82 #define foreach_nat44_ei_in2out_error                                         \
83   _ (UNSUPPORTED_PROTOCOL, "unsupported protocol")                            \
84   _ (OUT_OF_PORTS, "out of ports")                                            \
85   _ (BAD_OUTSIDE_FIB, "outside VRF ID not found")                             \
86   _ (BAD_ICMP_TYPE, "unsupported ICMP type")                                  \
87   _ (NO_TRANSLATION, "no translation")                                        \
88   _ (MAX_SESSIONS_EXCEEDED, "maximum sessions exceeded")                      \
89   _ (CANNOT_CREATE_USER, "cannot create NAT user")
90
91 typedef enum
92 {
93 #define _(sym, str) NAT44_EI_IN2OUT_ERROR_##sym,
94   foreach_nat44_ei_in2out_error
95 #undef _
96     NAT44_EI_IN2OUT_N_ERROR,
97 } nat44_ei_in2out_error_t;
98
99 static char *nat44_ei_in2out_error_strings[] = {
100 #define _(sym,string) string,
101   foreach_nat44_ei_in2out_error
102 #undef _
103 };
104
105 typedef enum
106 {
107   NAT44_EI_IN2OUT_NEXT_LOOKUP,
108   NAT44_EI_IN2OUT_NEXT_DROP,
109   NAT44_EI_IN2OUT_NEXT_ICMP_ERROR,
110   NAT44_EI_IN2OUT_NEXT_SLOW_PATH,
111   NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF,
112   NAT44_EI_IN2OUT_N_NEXT,
113 } nat44_ei_in2out_next_t;
114
115 typedef enum
116 {
117   NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP,
118   NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP,
119   NAT44_EI_IN2OUT_HAIRPINNING_FINISH_N_NEXT,
120 } nat44_ei_in2out_hairpinnig_finish_next_t;
121
122 static inline int
123 nat44_ei_not_translate_fast (vlib_node_runtime_t *node, u32 sw_if_index0,
124                              ip4_header_t *ip0, u32 proto0, u32 rx_fib_index0)
125 {
126   nat44_ei_main_t *nm = &nat44_ei_main;
127
128   if (nm->out2in_dpo)
129     return 0;
130
131   fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
132   nat44_ei_outside_fib_t *outside_fib;
133   fib_prefix_t pfx = {
134     .fp_proto = FIB_PROTOCOL_IP4,
135     .fp_len = 32,
136     .fp_addr = {
137                 .ip4.as_u32 = ip0->dst_address.as_u32,
138                 }
139     ,
140   };
141
142   /* Don't NAT packet aimed at the intfc address */
143   if (PREDICT_FALSE (nat44_ei_is_interface_addr (
144         nm->ip4_main, node, sw_if_index0, ip0->dst_address.as_u32)))
145     return 1;
146
147   fei = fib_table_lookup (rx_fib_index0, &pfx);
148   if (FIB_NODE_INDEX_INVALID != fei)
149     {
150       u32 sw_if_index = fib_entry_get_resolving_interface (fei);
151       if (sw_if_index == ~0)
152         {
153           vec_foreach (outside_fib, nm->outside_fibs)
154             {
155               fei = fib_table_lookup (outside_fib->fib_index, &pfx);
156               if (FIB_NODE_INDEX_INVALID != fei)
157                 {
158                   sw_if_index = fib_entry_get_resolving_interface (fei);
159                   if (sw_if_index != ~0)
160                     break;
161                 }
162             }
163         }
164       if (sw_if_index == ~0)
165         return 1;
166
167       nat44_ei_interface_t *i;
168       pool_foreach (i, nm->interfaces)
169         {
170           /* NAT packet aimed at outside interface */
171           if ((nat44_ei_interface_is_outside (i)) &&
172               (sw_if_index == i->sw_if_index))
173             return 0;
174         }
175     }
176
177   return 1;
178 }
179
180 static inline int
181 nat44_ei_not_translate (nat44_ei_main_t *nm, vlib_node_runtime_t *node,
182                         u32 sw_if_index0, ip4_header_t *ip0, u32 proto0,
183                         u32 rx_fib_index0, u32 thread_index)
184 {
185   udp_header_t *udp0 = ip4_next_header (ip0);
186   clib_bihash_kv_8_8_t kv0, value0;
187
188   init_nat_k (&kv0, ip0->dst_address, udp0->dst_port, nm->outside_fib_index,
189               proto0);
190
191   /* NAT packet aimed at external address if */
192   /* has active sessions */
193   if (clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0))
194     {
195       /* or is static mappings */
196       ip4_address_t placeholder_addr;
197       u16 placeholder_port;
198       u32 placeholder_fib_index;
199       if (!nat44_ei_static_mapping_match (ip0->dst_address, udp0->dst_port,
200                                           nm->outside_fib_index, proto0,
201                                           &placeholder_addr, &placeholder_port,
202                                           &placeholder_fib_index, 1, 0, 0))
203         return 0;
204     }
205   else
206     return 0;
207
208   if (nm->forwarding_enabled)
209     return 1;
210
211   return nat44_ei_not_translate_fast (node, sw_if_index0, ip0, proto0,
212                                       rx_fib_index0);
213 }
214
215 static inline int
216 nat44_ei_not_translate_output_feature (nat44_ei_main_t *nm, ip4_header_t *ip0,
217                                        u32 proto0, u16 src_port, u16 dst_port,
218                                        u32 thread_index, u32 sw_if_index)
219 {
220   clib_bihash_kv_8_8_t kv0, value0;
221   nat44_ei_interface_t *i;
222
223   /* src NAT check */
224   init_nat_k (&kv0, ip0->src_address, src_port,
225               ip4_fib_table_get_index_for_sw_if_index (sw_if_index), proto0);
226
227   if (!clib_bihash_search_8_8 (&nm->out2in, &kv0, &value0))
228     return 1;
229
230   /* dst NAT check */
231   init_nat_k (&kv0, ip0->dst_address, dst_port,
232               ip4_fib_table_get_index_for_sw_if_index (sw_if_index), proto0);
233   if (!clib_bihash_search_8_8 (&nm->in2out, &kv0, &value0))
234     {
235       /* hairpinning */
236       pool_foreach (i, nm->output_feature_interfaces)
237         {
238           if ((nat44_ei_interface_is_inside (i)) &&
239               (sw_if_index == i->sw_if_index))
240             return 0;
241         }
242       return 1;
243     }
244
245   return 0;
246 }
247
248 #ifndef CLIB_MARCH_VARIANT
249 int
250 nat44_i2o_is_idle_session_cb (clib_bihash_kv_8_8_t * kv, void *arg)
251 {
252   nat44_ei_main_t *nm = &nat44_ei_main;
253   nat44_ei_is_idle_session_ctx_t *ctx = arg;
254   nat44_ei_session_t *s;
255   u64 sess_timeout_time;
256   nat44_ei_main_per_thread_data_t *tnm =
257     vec_elt_at_index (nm->per_thread_data, ctx->thread_index);
258   clib_bihash_kv_8_8_t s_kv;
259
260   if (ctx->thread_index != nat_value_get_thread_index (kv))
261     {
262       return 0;
263     }
264
265   s = pool_elt_at_index (tnm->sessions, nat_value_get_session_index (kv));
266   sess_timeout_time = s->last_heard + (f64) nat_session_get_timeout (
267                                         &nm->timeouts, s->nat_proto, s->state);
268   if (ctx->now >= sess_timeout_time)
269     {
270       init_nat_o2i_k (&s_kv, s);
271       if (clib_bihash_add_del_8_8 (&nm->out2in, &s_kv, 0))
272         nat_elog_warn (nm, "out2in key del failed");
273
274       nat_ipfix_logging_nat44_ses_delete (
275         ctx->thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
276         nat_proto_to_ip_proto (s->nat_proto), s->in2out.port, s->out2in.port,
277         s->in2out.fib_index);
278
279       nat_syslog_nat44_apmdel (s->user_index, s->in2out.fib_index,
280                                &s->in2out.addr, s->in2out.port,
281                                &s->out2in.addr, s->out2in.port, s->nat_proto);
282
283       nat_ha_sdel (&s->out2in.addr, s->out2in.port, &s->ext_host_addr,
284                    s->ext_host_port, s->nat_proto, s->out2in.fib_index,
285                    ctx->thread_index);
286
287       if (!nat44_ei_is_session_static (s))
288         nat44_ei_free_outside_address_and_port (
289           nm->addresses, ctx->thread_index, &s->out2in.addr, s->out2in.port,
290           s->nat_proto);
291
292       nat44_ei_delete_session (nm, s, ctx->thread_index);
293       return 1;
294     }
295
296   return 0;
297 }
298 #endif
299
300 static u32
301 slow_path (nat44_ei_main_t *nm, vlib_buffer_t *b0, ip4_header_t *ip0,
302            ip4_address_t i2o_addr, u16 i2o_port, u32 rx_fib_index0,
303            nat_protocol_t nat_proto, nat44_ei_session_t **sessionp,
304            vlib_node_runtime_t *node, u32 next0, u32 thread_index, f64 now)
305 {
306   nat44_ei_user_t *u;
307   nat44_ei_session_t *s = 0;
308   clib_bihash_kv_8_8_t kv0;
309   u8 is_sm = 0;
310   nat44_ei_outside_fib_t *outside_fib;
311   fib_node_index_t fei = FIB_NODE_INDEX_INVALID;
312   u8 identity_nat;
313   fib_prefix_t pfx = {
314     .fp_proto = FIB_PROTOCOL_IP4,
315     .fp_len = 32,
316     .fp_addr = {
317                 .ip4.as_u32 = ip0->dst_address.as_u32,
318                 },
319   };
320   nat44_ei_is_idle_session_ctx_t ctx0;
321   ip4_address_t sm_addr;
322   u16 sm_port;
323   u32 sm_fib_index;
324
325   if (PREDICT_FALSE (nat44_ei_maximum_sessions_exceeded (nm, thread_index)))
326     {
327       b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_MAX_SESSIONS_EXCEEDED];
328       nat_ipfix_logging_max_sessions (thread_index,
329                                       nm->max_translations_per_thread);
330       nat_elog_notice (nm, "maximum sessions exceeded");
331       return NAT44_EI_IN2OUT_NEXT_DROP;
332     }
333
334   /* First try to match static mapping by local address and port */
335   if (nat44_ei_static_mapping_match (i2o_addr, i2o_port, rx_fib_index0,
336                                      nat_proto, &sm_addr, &sm_port,
337                                      &sm_fib_index, 0, 0, &identity_nat))
338     {
339       /* Try to create dynamic translation */
340       if (nm->alloc_addr_and_port (
341             nm->addresses, rx_fib_index0, thread_index, nat_proto,
342             ip0->src_address, &sm_addr, &sm_port, nm->port_per_thread,
343             nm->per_thread_data[thread_index].snat_thread_index))
344         {
345           b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_OUT_OF_PORTS];
346           return NAT44_EI_IN2OUT_NEXT_DROP;
347         }
348     }
349   else
350     {
351       if (PREDICT_FALSE (identity_nat))
352         {
353           *sessionp = s;
354           return next0;
355         }
356
357       is_sm = 1;
358     }
359
360   u = nat44_ei_user_get_or_create (nm, &ip0->src_address, rx_fib_index0,
361                                    thread_index);
362   if (!u)
363     {
364       b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_CANNOT_CREATE_USER];
365       return NAT44_EI_IN2OUT_NEXT_DROP;
366     }
367
368   s = nat44_ei_session_alloc_or_recycle (nm, u, thread_index, now);
369   if (!s)
370     {
371       nat44_ei_delete_user_with_no_session (nm, u, thread_index);
372       nat_elog_warn (nm, "create NAT session failed");
373       return NAT44_EI_IN2OUT_NEXT_DROP;
374     }
375
376   if (is_sm)
377     s->flags |= NAT44_EI_SESSION_FLAG_STATIC_MAPPING;
378   nat44_ei_user_session_increment (nm, u, is_sm);
379   s->in2out.addr = i2o_addr;
380   s->in2out.port = i2o_port;
381   s->in2out.fib_index = rx_fib_index0;
382   s->nat_proto = nat_proto;
383   s->out2in.addr = sm_addr;
384   s->out2in.port = sm_port;
385   s->out2in.fib_index = nm->outside_fib_index;
386   switch (vec_len (nm->outside_fibs))
387     {
388     case 0:
389       s->out2in.fib_index = nm->outside_fib_index;
390       break;
391     case 1:
392       s->out2in.fib_index = nm->outside_fibs[0].fib_index;
393       break;
394     default:
395       vec_foreach (outside_fib, nm->outside_fibs)
396         {
397           fei = fib_table_lookup (outside_fib->fib_index, &pfx);
398           if (FIB_NODE_INDEX_INVALID != fei)
399             {
400               if (fib_entry_get_resolving_interface (fei) != ~0)
401                 {
402                   s->out2in.fib_index = outside_fib->fib_index;
403                   break;
404                 }
405             }
406         }
407       break;
408     }
409   s->ext_host_addr.as_u32 = ip0->dst_address.as_u32;
410   s->ext_host_port = vnet_buffer (b0)->ip.reass.l4_dst_port;
411   *sessionp = s;
412
413   /* Add to translation hashes */
414   ctx0.now = now;
415   ctx0.thread_index = thread_index;
416   init_nat_i2o_kv (&kv0, s, thread_index,
417                    s - nm->per_thread_data[thread_index].sessions);
418   if (clib_bihash_add_or_overwrite_stale_8_8 (
419         &nm->in2out, &kv0, nat44_i2o_is_idle_session_cb, &ctx0))
420     nat_elog_notice (nm, "in2out key add failed");
421
422   init_nat_o2i_kv (&kv0, s, thread_index,
423                    s - nm->per_thread_data[thread_index].sessions);
424   if (clib_bihash_add_or_overwrite_stale_8_8 (
425         &nm->out2in, &kv0, nat44_o2i_is_idle_session_cb, &ctx0))
426     nat_elog_notice (nm, "out2in key add failed");
427
428   /* log NAT event */
429   nat_ipfix_logging_nat44_ses_create (
430     thread_index, s->in2out.addr.as_u32, s->out2in.addr.as_u32,
431     nat_proto_to_ip_proto (s->nat_proto), s->in2out.port, s->out2in.port,
432     s->in2out.fib_index);
433
434   nat_syslog_nat44_apmadd (s->user_index, s->in2out.fib_index, &s->in2out.addr,
435                            s->in2out.port, &s->out2in.addr, s->out2in.port,
436                            s->nat_proto);
437
438   nat_ha_sadd (&s->in2out.addr, s->in2out.port, &s->out2in.addr,
439                s->out2in.port, &s->ext_host_addr, s->ext_host_port,
440                &s->ext_host_nat_addr, s->ext_host_nat_port, s->nat_proto,
441                s->in2out.fib_index, s->flags, thread_index, 0);
442
443   return next0;
444 }
445
446 #ifndef CLIB_MARCH_VARIANT
447 static_always_inline nat44_ei_in2out_error_t
448 icmp_get_key (vlib_buffer_t *b, ip4_header_t *ip0, ip4_address_t *addr,
449               u16 *port, nat_protocol_t *nat_proto)
450 {
451   icmp46_header_t *icmp0;
452   icmp_echo_header_t *echo0, *inner_echo0 = 0;
453   ip4_header_t *inner_ip0 = 0;
454   void *l4_header = 0;
455   icmp46_header_t *inner_icmp0;
456
457   icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
458   echo0 = (icmp_echo_header_t *) (icmp0 + 1);
459
460   if (!icmp_type_is_error_message
461       (vnet_buffer (b)->ip.reass.icmp_type_or_tcp_flags))
462     {
463       *nat_proto = NAT_PROTOCOL_ICMP;
464       *addr = ip0->src_address;
465       *port = vnet_buffer (b)->ip.reass.l4_src_port;
466     }
467   else
468     {
469       inner_ip0 = (ip4_header_t *) (echo0 + 1);
470       l4_header = ip4_next_header (inner_ip0);
471       *nat_proto = ip_proto_to_nat_proto (inner_ip0->protocol);
472       *addr = inner_ip0->dst_address;
473       switch (*nat_proto)
474         {
475         case NAT_PROTOCOL_ICMP:
476           inner_icmp0 = (icmp46_header_t *) l4_header;
477           inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);
478           *port = inner_echo0->identifier;
479           break;
480         case NAT_PROTOCOL_UDP:
481         case NAT_PROTOCOL_TCP:
482           *port = ((tcp_udp_header_t *) l4_header)->dst_port;
483           break;
484         default:
485           return NAT44_EI_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL;
486         }
487     }
488   return -1;                    /* success */
489 }
490
491 /**
492  * Get address and port values to be used for ICMP packet translation
493  * and create session if needed
494  *
495  * @param[in,out] nm             NAT main
496  * @param[in,out] node           NAT node runtime
497  * @param[in] thread_index       thread index
498  * @param[in,out] b0             buffer containing packet to be translated
499  * @param[in,out] ip0            ip header
500  * @param[out] p_proto           protocol used for matching
501  * @param[out] p_value           address and port after NAT translation
502  * @param[out] p_dont_translate  if packet should not be translated
503  * @param d                      optional parameter
504  * @param e                      optional parameter
505  */
506 u32
507 nat44_ei_icmp_match_in2out_slow (vlib_node_runtime_t *node, u32 thread_index,
508                                  vlib_buffer_t *b0, ip4_header_t *ip0,
509                                  ip4_address_t *addr, u16 *port,
510                                  u32 *fib_index, nat_protocol_t *proto,
511                                  nat44_ei_session_t **p_s0, u8 *dont_translate)
512 {
513   nat44_ei_main_t *nm = &nat44_ei_main;
514   nat44_ei_main_per_thread_data_t *tnm = &nm->per_thread_data[thread_index];
515   u32 sw_if_index0;
516   nat44_ei_session_t *s0 = 0;
517   clib_bihash_kv_8_8_t kv0, value0;
518   u32 next0 = ~0;
519   int err;
520   vlib_main_t *vm = vlib_get_main ();
521   *dont_translate = 0;
522
523   sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
524   *fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
525
526   err = icmp_get_key (b0, ip0, addr, port, proto);
527   if (err != -1)
528     {
529       b0->error = node->errors[err];
530       next0 = NAT44_EI_IN2OUT_NEXT_DROP;
531       goto out;
532     }
533
534   init_nat_k (&kv0, *addr, *port, *fib_index, *proto);
535   if (clib_bihash_search_8_8 (&nm->in2out, &kv0, &value0))
536     {
537       if (vnet_buffer (b0)->sw_if_index[VLIB_TX] != ~0)
538         {
539           if (PREDICT_FALSE (nat44_ei_not_translate_output_feature (
540                 nm, ip0, *proto, *port, *port, thread_index, sw_if_index0)))
541             {
542               *dont_translate = 1;
543               goto out;
544             }
545         }
546       else
547         {
548           if (PREDICT_FALSE (nat44_ei_not_translate (
549                 nm, node, sw_if_index0, ip0, NAT_PROTOCOL_ICMP, *fib_index,
550                 thread_index)))
551             {
552               *dont_translate = 1;
553               goto out;
554             }
555         }
556
557       if (PREDICT_FALSE
558           (icmp_type_is_error_message
559            (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags)))
560         {
561           b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_BAD_ICMP_TYPE];
562           next0 = NAT44_EI_IN2OUT_NEXT_DROP;
563           goto out;
564         }
565
566       next0 = slow_path (nm, b0, ip0, *addr, *port, *fib_index, *proto, &s0,
567                          node, next0, thread_index, vlib_time_now (vm));
568
569       if (PREDICT_FALSE (next0 == NAT44_EI_IN2OUT_NEXT_DROP))
570         goto out;
571
572       if (!s0)
573         {
574           *dont_translate = 1;
575           goto out;
576         }
577     }
578   else
579     {
580       if (PREDICT_FALSE
581           (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags !=
582            ICMP4_echo_request
583            && vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags !=
584            ICMP4_echo_reply
585            && !icmp_type_is_error_message (vnet_buffer (b0)->ip.
586                                            reass.icmp_type_or_tcp_flags)))
587         {
588           b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_BAD_ICMP_TYPE];
589           next0 = NAT44_EI_IN2OUT_NEXT_DROP;
590           goto out;
591         }
592
593       s0 = pool_elt_at_index (tnm->sessions,
594                               nat_value_get_session_index (&value0));
595     }
596
597 out:
598   if (s0)
599     {
600       *addr = s0->out2in.addr;
601       *port = s0->out2in.port;
602       *fib_index = s0->out2in.fib_index;
603     }
604   if (p_s0)
605     *p_s0 = s0;
606   return next0;
607 }
608 #endif
609
610 #ifndef CLIB_MARCH_VARIANT
611 u32
612 nat44_ei_icmp_match_in2out_fast (vlib_node_runtime_t *node, u32 thread_index,
613                                  vlib_buffer_t *b0, ip4_header_t *ip0,
614                                  ip4_address_t *addr, u16 *port,
615                                  u32 *fib_index, nat_protocol_t *proto,
616                                  nat44_ei_session_t **s0, u8 *dont_translate)
617 {
618   u32 sw_if_index0;
619   u8 is_addr_only;
620   u32 next0 = ~0;
621   int err;
622   *dont_translate = 0;
623
624   sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
625   *fib_index = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
626
627   err = icmp_get_key (b0, ip0, addr, port, proto);
628   if (err != -1)
629     {
630       b0->error = node->errors[err];
631       next0 = NAT44_EI_IN2OUT_NEXT_DROP;
632       goto out;
633     }
634
635   ip4_address_t sm_addr;
636   u16 sm_port;
637   u32 sm_fib_index;
638
639   if (nat44_ei_static_mapping_match (*addr, *port, *fib_index, *proto,
640                                      &sm_addr, &sm_port, &sm_fib_index, 0,
641                                      &is_addr_only, 0))
642     {
643       if (PREDICT_FALSE (nat44_ei_not_translate_fast (
644             node, sw_if_index0, ip0, IP_PROTOCOL_ICMP, *fib_index)))
645         {
646           *dont_translate = 1;
647           goto out;
648         }
649
650       if (icmp_type_is_error_message
651           (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags))
652         {
653           next0 = NAT44_EI_IN2OUT_NEXT_DROP;
654           goto out;
655         }
656
657       b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_NO_TRANSLATION];
658       next0 = NAT44_EI_IN2OUT_NEXT_DROP;
659       goto out;
660     }
661
662   if (PREDICT_FALSE
663       (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != ICMP4_echo_request
664        && (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags !=
665            ICMP4_echo_reply || !is_addr_only)
666        && !icmp_type_is_error_message (vnet_buffer (b0)->ip.
667                                        reass.icmp_type_or_tcp_flags)))
668     {
669       b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_BAD_ICMP_TYPE];
670       next0 = NAT44_EI_IN2OUT_NEXT_DROP;
671       goto out;
672     }
673
674 out:
675   return next0;
676 }
677 #endif
678
679 u32 nat44_ei_icmp_in2out (vlib_buffer_t *b0, ip4_header_t *ip0,
680                           icmp46_header_t *icmp0, u32 sw_if_index0,
681                           u32 rx_fib_index0, vlib_node_runtime_t *node,
682                           u32 next0, u32 thread_index,
683                           nat44_ei_session_t **p_s0);
684
685 #ifndef CLIB_MARCH_VARIANT
686 u32
687 nat44_ei_icmp_in2out (vlib_buffer_t *b0, ip4_header_t *ip0,
688                       icmp46_header_t *icmp0, u32 sw_if_index0,
689                       u32 rx_fib_index0, vlib_node_runtime_t *node, u32 next0,
690                       u32 thread_index, nat44_ei_session_t **p_s0)
691 {
692   nat44_ei_main_t *nm = &nat44_ei_main;
693   vlib_main_t *vm = vlib_get_main ();
694   ip4_address_t addr;
695   u16 port;
696   u32 fib_index;
697   nat_protocol_t proto;
698   icmp_echo_header_t *echo0, *inner_echo0 = 0;
699   ip4_header_t *inner_ip0;
700   void *l4_header = 0;
701   icmp46_header_t *inner_icmp0;
702   u8 dont_translate;
703   u32 new_addr0, old_addr0;
704   u16 old_id0, new_id0;
705   u16 old_checksum0, new_checksum0;
706   ip_csum_t sum0;
707   u16 checksum0;
708   u32 next0_tmp;
709   u32 required_thread_index = thread_index;
710
711   echo0 = (icmp_echo_header_t *) (icmp0 + 1);
712
713   if (PREDICT_TRUE (nm->pat))
714     {
715       next0_tmp = nat44_ei_icmp_match_in2out_slow (
716         node, thread_index, b0, ip0, &addr, &port, &fib_index, &proto, p_s0,
717         &dont_translate);
718     }
719   else
720     {
721       next0_tmp = nat44_ei_icmp_match_in2out_fast (
722         node, thread_index, b0, ip0, &addr, &port, &fib_index, &proto, p_s0,
723         &dont_translate);
724     }
725
726   if (next0_tmp != ~0)
727     next0 = next0_tmp;
728   if (next0 == NAT44_EI_IN2OUT_NEXT_DROP || dont_translate)
729     goto out;
730
731   if (PREDICT_TRUE (!ip4_is_fragment (ip0)))
732     {
733       sum0 =
734         ip_incremental_checksum_buffer (vm, b0,
735                                         (u8 *) icmp0 -
736                                         (u8 *) vlib_buffer_get_current (b0),
737                                         ntohs (ip0->length) -
738                                         ip4_header_bytes (ip0), 0);
739       checksum0 = ~ip_csum_fold (sum0);
740       if (PREDICT_FALSE (checksum0 != 0 && checksum0 != 0xffff))
741         {
742           next0 = NAT44_EI_IN2OUT_NEXT_DROP;
743           goto out;
744         }
745     }
746
747   old_addr0 = ip0->src_address.as_u32;
748   new_addr0 = ip0->src_address.as_u32 = addr.as_u32;
749
750   sum0 = ip0->checksum;
751   sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
752                          src_address /* changed member */ );
753   ip0->checksum = ip_csum_fold (sum0);
754
755   if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
756     {
757       if (icmp0->checksum == 0)
758         icmp0->checksum = 0xffff;
759
760       if (!icmp_type_is_error_message (icmp0->type))
761         {
762           new_id0 = port;
763           if (PREDICT_FALSE (new_id0 != echo0->identifier))
764             {
765               old_id0 = echo0->identifier;
766               new_id0 = port;
767               echo0->identifier = new_id0;
768
769               sum0 = icmp0->checksum;
770               sum0 =
771                 ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
772                                 identifier);
773               icmp0->checksum = ip_csum_fold (sum0);
774             }
775         }
776       else
777         {
778           inner_ip0 = (ip4_header_t *) (echo0 + 1);
779           l4_header = ip4_next_header (inner_ip0);
780
781           if (!ip4_header_checksum_is_valid (inner_ip0))
782             {
783               next0 = NAT44_EI_IN2OUT_NEXT_DROP;
784               goto out;
785             }
786
787           /* update inner destination IP address */
788           old_addr0 = inner_ip0->dst_address.as_u32;
789           inner_ip0->dst_address = addr;
790           new_addr0 = inner_ip0->dst_address.as_u32;
791           sum0 = icmp0->checksum;
792           sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
793                                  dst_address /* changed member */ );
794           icmp0->checksum = ip_csum_fold (sum0);
795
796           /* update inner IP header checksum */
797           old_checksum0 = inner_ip0->checksum;
798           sum0 = inner_ip0->checksum;
799           sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
800                                  dst_address /* changed member */ );
801           inner_ip0->checksum = ip_csum_fold (sum0);
802           new_checksum0 = inner_ip0->checksum;
803           sum0 = icmp0->checksum;
804           sum0 =
805             ip_csum_update (sum0, old_checksum0, new_checksum0, ip4_header_t,
806                             checksum);
807           icmp0->checksum = ip_csum_fold (sum0);
808
809           switch (proto)
810             {
811             case NAT_PROTOCOL_ICMP:
812               inner_icmp0 = (icmp46_header_t *) l4_header;
813               inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);
814
815               old_id0 = inner_echo0->identifier;
816               new_id0 = port;
817               inner_echo0->identifier = new_id0;
818
819               sum0 = icmp0->checksum;
820               sum0 =
821                 ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
822                                 identifier);
823               icmp0->checksum = ip_csum_fold (sum0);
824               break;
825             case NAT_PROTOCOL_UDP:
826             case NAT_PROTOCOL_TCP:
827               old_id0 = ((tcp_udp_header_t *) l4_header)->dst_port;
828               new_id0 = port;
829               ((tcp_udp_header_t *) l4_header)->dst_port = new_id0;
830
831               sum0 = icmp0->checksum;
832               sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t,
833                                      dst_port);
834               icmp0->checksum = ip_csum_fold (sum0);
835               break;
836             default:
837               ASSERT (0);
838             }
839         }
840     }
841
842   if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0)
843     {
844       if (0 != nat44_ei_icmp_hairpinning (nm, b0, thread_index, ip0, icmp0,
845                                           &required_thread_index))
846         vnet_buffer (b0)->sw_if_index[VLIB_TX] = fib_index;
847       if (thread_index != required_thread_index)
848         {
849           vnet_buffer (b0)->snat.required_thread_index = required_thread_index;
850           next0 = NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF;
851         }
852     }
853
854 out:
855   return next0;
856 }
857 #endif
858
859 static_always_inline u32
860 nat44_ei_icmp_in2out_slow_path (nat44_ei_main_t *nm, vlib_buffer_t *b0,
861                                 ip4_header_t *ip0, icmp46_header_t *icmp0,
862                                 u32 sw_if_index0, u32 rx_fib_index0,
863                                 vlib_node_runtime_t *node, u32 next0, f64 now,
864                                 u32 thread_index, nat44_ei_session_t **p_s0)
865 {
866   vlib_main_t *vm = vlib_get_main ();
867
868   next0 = nat44_ei_icmp_in2out (b0, ip0, icmp0, sw_if_index0, rx_fib_index0,
869                                 node, next0, thread_index, p_s0);
870   nat44_ei_session_t *s0 = *p_s0;
871   if (PREDICT_TRUE (next0 != NAT44_EI_IN2OUT_NEXT_DROP && s0))
872     {
873       /* Accounting */
874       nat44_ei_session_update_counters (
875         s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index);
876       /* Per-user LRU list maintenance */
877       nat44_ei_session_update_lru (nm, s0, thread_index);
878     }
879   return next0;
880 }
881
882 static int
883 nat_in2out_sm_unknown_proto (nat44_ei_main_t *nm, vlib_buffer_t *b,
884                              ip4_header_t *ip, u32 rx_fib_index)
885 {
886   clib_bihash_kv_8_8_t kv, value;
887   nat44_ei_static_mapping_t *m;
888   u32 old_addr, new_addr;
889   ip_csum_t sum;
890
891   init_nat_k (&kv, ip->src_address, 0, rx_fib_index, 0);
892   if (clib_bihash_search_8_8 (&nm->static_mapping_by_local, &kv, &value))
893     return 1;
894
895   m = pool_elt_at_index (nm->static_mappings, value.value);
896
897   old_addr = ip->src_address.as_u32;
898   new_addr = ip->src_address.as_u32 = m->external_addr.as_u32;
899   sum = ip->checksum;
900   sum = ip_csum_update (sum, old_addr, new_addr, ip4_header_t, src_address);
901   ip->checksum = ip_csum_fold (sum);
902
903
904   /* Hairpinning */
905   if (vnet_buffer (b)->sw_if_index[VLIB_TX] == ~0)
906     {
907       vnet_buffer (b)->sw_if_index[VLIB_TX] = m->fib_index;
908       nat44_ei_hairpinning_sm_unknown_proto (nm, b, ip);
909     }
910
911   return 0;
912 }
913
914 static inline uword
915 nat44_ei_in2out_node_fn_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
916                                 vlib_frame_t *frame, int is_slow_path,
917                                 int is_output_feature)
918 {
919   u32 n_left_from, *from;
920   nat44_ei_main_t *nm = &nat44_ei_main;
921   f64 now = vlib_time_now (vm);
922   u32 thread_index = vm->thread_index;
923
924   from = vlib_frame_vector_args (frame);
925   n_left_from = frame->n_vectors;
926
927   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
928   u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
929   vlib_get_buffers (vm, from, b, n_left_from);
930
931   while (n_left_from >= 2)
932     {
933       vlib_buffer_t *b0, *b1;
934       u32 next0, next1;
935       u32 rx_sw_if_index0, rx_sw_if_index1;
936       u32 tx_sw_if_index0, tx_sw_if_index1;
937       u32 cntr_sw_if_index0, cntr_sw_if_index1;
938       ip4_header_t *ip0, *ip1;
939       ip_csum_t sum0, sum1;
940       u32 new_addr0, old_addr0, new_addr1, old_addr1;
941       u16 old_port0, new_port0, old_port1, new_port1;
942       udp_header_t *udp0, *udp1;
943       tcp_header_t *tcp0, *tcp1;
944       icmp46_header_t *icmp0, *icmp1;
945       u32 rx_fib_index0, rx_fib_index1;
946       u32 proto0, proto1;
947       nat44_ei_session_t *s0 = 0, *s1 = 0;
948       clib_bihash_kv_8_8_t kv0, value0, kv1, value1;
949       u32 iph_offset0 = 0, iph_offset1 = 0;
950
951       b0 = *b;
952       b++;
953       b1 = *b;
954       b++;
955
956       /* Prefetch next iteration. */
957       if (PREDICT_TRUE (n_left_from >= 4))
958         {
959           vlib_buffer_t *p2, *p3;
960
961           p2 = *b;
962           p3 = *(b + 1);
963
964           vlib_prefetch_buffer_header (p2, LOAD);
965           vlib_prefetch_buffer_header (p3, LOAD);
966
967           clib_prefetch_load (p2->data);
968           clib_prefetch_load (p3->data);
969         }
970
971       if (is_output_feature)
972         iph_offset0 = vnet_buffer (b0)->ip.reass.save_rewrite_length;
973
974       ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
975                               iph_offset0);
976
977       udp0 = ip4_next_header (ip0);
978       tcp0 = (tcp_header_t *) udp0;
979       icmp0 = (icmp46_header_t *) udp0;
980
981       rx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
982       tx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
983       cntr_sw_if_index0 =
984         is_output_feature ? tx_sw_if_index0 : rx_sw_if_index0;
985       rx_fib_index0 =
986         vec_elt (nm->ip4_main->fib_index_by_sw_if_index, rx_sw_if_index0);
987
988       next0 = next1 = NAT44_EI_IN2OUT_NEXT_LOOKUP;
989
990       if (PREDICT_FALSE (ip0->ttl == 1))
991         {
992           vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
993           icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
994                                        ICMP4_time_exceeded_ttl_exceeded_in_transit,
995                                        0);
996           next0 = NAT44_EI_IN2OUT_NEXT_ICMP_ERROR;
997           goto trace00;
998         }
999
1000       proto0 = ip_proto_to_nat_proto (ip0->protocol);
1001
1002       /* Next configured feature, probably ip4-lookup */
1003       if (is_slow_path)
1004         {
1005           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER))
1006             {
1007               if (nat_in2out_sm_unknown_proto (nm, b0, ip0, rx_fib_index0))
1008                 {
1009                   next0 = NAT44_EI_IN2OUT_NEXT_DROP;
1010                   b0->error =
1011                     node->errors[NAT44_EI_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
1012                 }
1013               vlib_increment_simple_counter (
1014                 is_slow_path ? &nm->counters.slowpath.in2out.other :
1015                                &nm->counters.fastpath.in2out.other,
1016                 thread_index, cntr_sw_if_index0, 1);
1017               goto trace00;
1018             }
1019
1020           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
1021             {
1022               next0 = nat44_ei_icmp_in2out_slow_path (
1023                 nm, b0, ip0, icmp0, rx_sw_if_index0, rx_fib_index0, node,
1024                 next0, now, thread_index, &s0);
1025               vlib_increment_simple_counter (
1026                 is_slow_path ? &nm->counters.slowpath.in2out.icmp :
1027                                &nm->counters.fastpath.in2out.icmp,
1028                 thread_index, cntr_sw_if_index0, 1);
1029               goto trace00;
1030             }
1031         }
1032       else
1033         {
1034           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER))
1035             {
1036               next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1037               goto trace00;
1038             }
1039
1040           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
1041             {
1042               next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1043               goto trace00;
1044             }
1045         }
1046
1047       init_nat_k (&kv0, ip0->src_address,
1048                   vnet_buffer (b0)->ip.reass.l4_src_port, rx_fib_index0,
1049                   proto0);
1050       if (PREDICT_FALSE (clib_bihash_search_8_8 (&nm->in2out, &kv0, &value0) !=
1051                          0))
1052         {
1053           if (is_slow_path)
1054             {
1055               if (is_output_feature)
1056                 {
1057                   if (PREDICT_FALSE (nat44_ei_not_translate_output_feature (
1058                         nm, ip0, proto0,
1059                         vnet_buffer (b0)->ip.reass.l4_src_port,
1060                         vnet_buffer (b0)->ip.reass.l4_dst_port, thread_index,
1061                         rx_sw_if_index0)))
1062                     goto trace00;
1063
1064                   /*
1065                    * Send DHCP packets to the ipv4 stack, or we won't
1066                    * be able to use dhcp client on the outside interface
1067                    */
1068                   if (PREDICT_FALSE
1069                       (proto0 == NAT_PROTOCOL_UDP
1070                        && (vnet_buffer (b0)->ip.reass.l4_dst_port ==
1071                            clib_host_to_net_u16
1072                            (UDP_DST_PORT_dhcp_to_server))
1073                        && ip0->dst_address.as_u32 == 0xffffffff))
1074                     goto trace00;
1075                 }
1076               else
1077                 {
1078                   if (PREDICT_FALSE (nat44_ei_not_translate (
1079                         nm, node, rx_sw_if_index0, ip0, proto0, rx_fib_index0,
1080                         thread_index)))
1081                     goto trace00;
1082                 }
1083
1084               next0 = slow_path (nm, b0, ip0, ip0->src_address,
1085                                  vnet_buffer (b0)->ip.reass.l4_src_port,
1086                                  rx_fib_index0, proto0, &s0, node, next0,
1087                                  thread_index, now);
1088               if (PREDICT_FALSE (next0 == NAT44_EI_IN2OUT_NEXT_DROP))
1089                 goto trace00;
1090
1091               if (PREDICT_FALSE (!s0))
1092                 goto trace00;
1093             }
1094           else
1095             {
1096               next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1097               goto trace00;
1098             }
1099         }
1100       else
1101         s0 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions,
1102                                 nat_value_get_session_index (&value0));
1103
1104       b0->flags |= VNET_BUFFER_F_IS_NATED;
1105
1106       old_addr0 = ip0->src_address.as_u32;
1107       ip0->src_address = s0->out2in.addr;
1108       new_addr0 = ip0->src_address.as_u32;
1109       if (!is_output_feature)
1110         vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
1111
1112       sum0 = ip0->checksum;
1113       sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1114                              ip4_header_t, src_address /* changed member */ );
1115       ip0->checksum = ip_csum_fold (sum0);
1116
1117
1118       if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
1119         {
1120           if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
1121             {
1122               old_port0 = vnet_buffer (b0)->ip.reass.l4_src_port;
1123               new_port0 = udp0->src_port = s0->out2in.port;
1124               sum0 = tcp0->checksum;
1125               sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1126                                      ip4_header_t,
1127                                      dst_address /* changed member */ );
1128               sum0 = ip_csum_update (sum0, old_port0, new_port0,
1129                                      ip4_header_t /* cheat */ ,
1130                                      length /* changed member */ );
1131               mss_clamping (nm->mss_clamping, tcp0, &sum0);
1132               tcp0->checksum = ip_csum_fold (sum0);
1133             }
1134           vlib_increment_simple_counter (is_slow_path ?
1135                                            &nm->counters.slowpath.in2out.tcp :
1136                                            &nm->counters.fastpath.in2out.tcp,
1137                                          thread_index, cntr_sw_if_index0, 1);
1138         }
1139       else
1140         {
1141           if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
1142             {
1143               udp0->src_port = s0->out2in.port;
1144               if (PREDICT_FALSE (udp0->checksum))
1145                 {
1146                   old_port0 = vnet_buffer (b0)->ip.reass.l4_src_port;
1147                   new_port0 = udp0->src_port;
1148                   sum0 = udp0->checksum;
1149                   sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t, dst_address  /* changed member */
1150                     );
1151                   sum0 =
1152                     ip_csum_update (sum0, old_port0, new_port0,
1153                                     ip4_header_t /* cheat */ ,
1154                                     length /* changed member */ );
1155                   udp0->checksum = ip_csum_fold (sum0);
1156                 }
1157             }
1158           vlib_increment_simple_counter (is_slow_path ?
1159                                            &nm->counters.slowpath.in2out.udp :
1160                                            &nm->counters.fastpath.in2out.udp,
1161                                          thread_index, cntr_sw_if_index0, 1);
1162         }
1163
1164       /* Accounting */
1165       nat44_ei_session_update_counters (
1166         s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index);
1167       /* Per-user LRU list maintenance */
1168       nat44_ei_session_update_lru (nm, s0, thread_index);
1169     trace00:
1170
1171       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1172                          && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1173         {
1174           nat44_ei_in2out_trace_t *t =
1175             vlib_add_trace (vm, node, b0, sizeof (*t));
1176           t->is_slow_path = is_slow_path;
1177           t->sw_if_index = rx_sw_if_index0;
1178           t->next_index = next0;
1179           t->session_index = ~0;
1180           if (s0)
1181             t->session_index = s0 - nm->per_thread_data[thread_index].sessions;
1182         }
1183
1184       if (next0 == NAT44_EI_IN2OUT_NEXT_DROP)
1185         {
1186           vlib_increment_simple_counter (
1187             is_slow_path ? &nm->counters.slowpath.in2out.drops :
1188                            &nm->counters.fastpath.in2out.drops,
1189             thread_index, cntr_sw_if_index0, 1);
1190         }
1191
1192       if (is_output_feature)
1193         iph_offset1 = vnet_buffer (b1)->ip.reass.save_rewrite_length;
1194
1195       ip1 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b1) +
1196                               iph_offset1);
1197
1198       udp1 = ip4_next_header (ip1);
1199       tcp1 = (tcp_header_t *) udp1;
1200       icmp1 = (icmp46_header_t *) udp1;
1201
1202       rx_sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
1203       tx_sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_TX];
1204       cntr_sw_if_index1 =
1205         is_output_feature ? tx_sw_if_index1 : rx_sw_if_index1;
1206       rx_fib_index1 =
1207         vec_elt (nm->ip4_main->fib_index_by_sw_if_index, rx_sw_if_index1);
1208
1209       if (PREDICT_FALSE (ip1->ttl == 1))
1210         {
1211           vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1212           icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
1213                                        ICMP4_time_exceeded_ttl_exceeded_in_transit,
1214                                        0);
1215           next1 = NAT44_EI_IN2OUT_NEXT_ICMP_ERROR;
1216           goto trace01;
1217         }
1218
1219       proto1 = ip_proto_to_nat_proto (ip1->protocol);
1220
1221       /* Next configured feature, probably ip4-lookup */
1222       if (is_slow_path)
1223         {
1224           if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_OTHER))
1225             {
1226               if (nat_in2out_sm_unknown_proto (nm, b1, ip1, rx_fib_index1))
1227                 {
1228                   next1 = NAT44_EI_IN2OUT_NEXT_DROP;
1229                   b1->error =
1230                     node->errors[NAT44_EI_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
1231                 }
1232               vlib_increment_simple_counter (
1233                 is_slow_path ? &nm->counters.slowpath.in2out.other :
1234                                &nm->counters.fastpath.in2out.other,
1235                 thread_index, cntr_sw_if_index1, 1);
1236               goto trace01;
1237             }
1238
1239           if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_ICMP))
1240             {
1241               next1 = nat44_ei_icmp_in2out_slow_path (
1242                 nm, b1, ip1, icmp1, rx_sw_if_index1, rx_fib_index1, node,
1243                 next1, now, thread_index, &s1);
1244               vlib_increment_simple_counter (
1245                 is_slow_path ? &nm->counters.slowpath.in2out.icmp :
1246                                &nm->counters.fastpath.in2out.icmp,
1247                 thread_index, cntr_sw_if_index1, 1);
1248               goto trace01;
1249             }
1250         }
1251       else
1252         {
1253           if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_OTHER))
1254             {
1255               next1 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1256               goto trace01;
1257             }
1258
1259           if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_ICMP))
1260             {
1261               next1 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1262               goto trace01;
1263             }
1264         }
1265
1266       init_nat_k (&kv1, ip1->src_address,
1267                   vnet_buffer (b1)->ip.reass.l4_src_port, rx_fib_index1,
1268                   proto1);
1269       if (PREDICT_FALSE (clib_bihash_search_8_8 (&nm->in2out, &kv1, &value1) !=
1270                          0))
1271         {
1272           if (is_slow_path)
1273             {
1274               if (is_output_feature)
1275                 {
1276                   if (PREDICT_FALSE (nat44_ei_not_translate_output_feature (
1277                         nm, ip1, proto1,
1278                         vnet_buffer (b1)->ip.reass.l4_src_port,
1279                         vnet_buffer (b1)->ip.reass.l4_dst_port, thread_index,
1280                         rx_sw_if_index1)))
1281                     goto trace01;
1282
1283                   /*
1284                    * Send DHCP packets to the ipv4 stack, or we won't
1285                    * be able to use dhcp client on the outside interface
1286                    */
1287                   if (PREDICT_FALSE
1288                       (proto1 == NAT_PROTOCOL_UDP
1289                        && (vnet_buffer (b1)->ip.reass.l4_dst_port ==
1290                            clib_host_to_net_u16
1291                            (UDP_DST_PORT_dhcp_to_server))
1292                        && ip1->dst_address.as_u32 == 0xffffffff))
1293                     goto trace01;
1294                 }
1295               else
1296                 {
1297                   if (PREDICT_FALSE (nat44_ei_not_translate (
1298                         nm, node, rx_sw_if_index1, ip1, proto1, rx_fib_index1,
1299                         thread_index)))
1300                     goto trace01;
1301                 }
1302
1303               next1 = slow_path (nm, b1, ip1, ip1->src_address,
1304                                  vnet_buffer (b1)->ip.reass.l4_src_port,
1305                                  rx_fib_index1, proto1, &s1, node, next1,
1306                                  thread_index, now);
1307               if (PREDICT_FALSE (next1 == NAT44_EI_IN2OUT_NEXT_DROP))
1308                 goto trace01;
1309
1310               if (PREDICT_FALSE (!s1))
1311                 goto trace01;
1312             }
1313           else
1314             {
1315               next1 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1316               goto trace01;
1317             }
1318         }
1319       else
1320         s1 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions,
1321                                 nat_value_get_session_index (&value1));
1322
1323       b1->flags |= VNET_BUFFER_F_IS_NATED;
1324
1325       old_addr1 = ip1->src_address.as_u32;
1326       ip1->src_address = s1->out2in.addr;
1327       new_addr1 = ip1->src_address.as_u32;
1328       if (!is_output_feature)
1329         vnet_buffer (b1)->sw_if_index[VLIB_TX] = s1->out2in.fib_index;
1330
1331       sum1 = ip1->checksum;
1332       sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
1333                              ip4_header_t, src_address /* changed member */ );
1334       ip1->checksum = ip_csum_fold (sum1);
1335
1336       if (PREDICT_TRUE (proto1 == NAT_PROTOCOL_TCP))
1337         {
1338           if (!vnet_buffer (b1)->ip.reass.is_non_first_fragment)
1339             {
1340               old_port1 = vnet_buffer (b1)->ip.reass.l4_src_port;
1341               new_port1 = udp1->src_port = s1->out2in.port;
1342               sum1 = tcp1->checksum;
1343               sum1 = ip_csum_update (sum1, old_addr1, new_addr1,
1344                                      ip4_header_t,
1345                                      dst_address /* changed member */ );
1346               sum1 = ip_csum_update (sum1, old_port1, new_port1,
1347                                      ip4_header_t /* cheat */ ,
1348                                      length /* changed member */ );
1349               mss_clamping (nm->mss_clamping, tcp1, &sum1);
1350               tcp1->checksum = ip_csum_fold (sum1);
1351             }
1352           vlib_increment_simple_counter (is_slow_path ?
1353                                            &nm->counters.slowpath.in2out.tcp :
1354                                            &nm->counters.fastpath.in2out.tcp,
1355                                          thread_index, cntr_sw_if_index1, 1);
1356         }
1357       else
1358         {
1359           if (!vnet_buffer (b1)->ip.reass.is_non_first_fragment)
1360             {
1361               udp1->src_port = s1->out2in.port;
1362               if (PREDICT_FALSE (udp1->checksum))
1363                 {
1364                   old_port1 = vnet_buffer (b1)->ip.reass.l4_src_port;
1365                   new_port1 = udp1->src_port;
1366                   sum1 = udp1->checksum;
1367                   sum1 = ip_csum_update (sum1, old_addr1, new_addr1, ip4_header_t, dst_address  /* changed member */
1368                     );
1369                   sum1 =
1370                     ip_csum_update (sum1, old_port1, new_port1,
1371                                     ip4_header_t /* cheat */ ,
1372                                     length /* changed member */ );
1373                   udp1->checksum = ip_csum_fold (sum1);
1374                 }
1375             }
1376           vlib_increment_simple_counter (is_slow_path ?
1377                                            &nm->counters.slowpath.in2out.udp :
1378                                            &nm->counters.fastpath.in2out.udp,
1379                                          thread_index, cntr_sw_if_index1, 1);
1380         }
1381
1382       /* Accounting */
1383       nat44_ei_session_update_counters (
1384         s1, now, vlib_buffer_length_in_chain (vm, b1), thread_index);
1385       /* Per-user LRU list maintenance */
1386       nat44_ei_session_update_lru (nm, s1, thread_index);
1387     trace01:
1388
1389       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1390                          && (b1->flags & VLIB_BUFFER_IS_TRACED)))
1391         {
1392           nat44_ei_in2out_trace_t *t =
1393             vlib_add_trace (vm, node, b1, sizeof (*t));
1394           t->sw_if_index = rx_sw_if_index1;
1395           t->next_index = next1;
1396           t->session_index = ~0;
1397           if (s1)
1398             t->session_index = s1 - nm->per_thread_data[thread_index].sessions;
1399         }
1400
1401       if (next1 == NAT44_EI_IN2OUT_NEXT_DROP)
1402         {
1403           vlib_increment_simple_counter (
1404             is_slow_path ? &nm->counters.slowpath.in2out.drops :
1405                            &nm->counters.fastpath.in2out.drops,
1406             thread_index, cntr_sw_if_index1, 1);
1407         }
1408
1409       n_left_from -= 2;
1410       next[0] = next0;
1411       next[1] = next1;
1412       next += 2;
1413     }
1414
1415   while (n_left_from > 0)
1416     {
1417       vlib_buffer_t *b0;
1418       u32 next0;
1419       u32 rx_sw_if_index0;
1420       u32 tx_sw_if_index0;
1421       u32 cntr_sw_if_index0;
1422       ip4_header_t *ip0;
1423       ip_csum_t sum0;
1424       u32 new_addr0, old_addr0;
1425       u16 old_port0, new_port0;
1426       udp_header_t *udp0;
1427       tcp_header_t *tcp0;
1428       icmp46_header_t *icmp0;
1429       u32 rx_fib_index0;
1430       u32 proto0;
1431       nat44_ei_session_t *s0 = 0;
1432       clib_bihash_kv_8_8_t kv0, value0;
1433       u32 iph_offset0 = 0;
1434
1435       b0 = *b;
1436       b++;
1437       next0 = NAT44_EI_IN2OUT_NEXT_LOOKUP;
1438
1439       if (is_output_feature)
1440         iph_offset0 = vnet_buffer (b0)->ip.reass.save_rewrite_length;
1441
1442       ip0 = (ip4_header_t *) ((u8 *) vlib_buffer_get_current (b0) +
1443                               iph_offset0);
1444
1445       udp0 = ip4_next_header (ip0);
1446       tcp0 = (tcp_header_t *) udp0;
1447       icmp0 = (icmp46_header_t *) udp0;
1448
1449       rx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1450       tx_sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_TX];
1451       cntr_sw_if_index0 =
1452         is_output_feature ? tx_sw_if_index0 : rx_sw_if_index0;
1453       rx_fib_index0 =
1454         vec_elt (nm->ip4_main->fib_index_by_sw_if_index, rx_sw_if_index0);
1455
1456       if (PREDICT_FALSE (ip0->ttl == 1))
1457         {
1458           vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1459           icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1460                                        ICMP4_time_exceeded_ttl_exceeded_in_transit,
1461                                        0);
1462           next0 = NAT44_EI_IN2OUT_NEXT_ICMP_ERROR;
1463           goto trace0;
1464         }
1465
1466       proto0 = ip_proto_to_nat_proto (ip0->protocol);
1467
1468       /* Next configured feature, probably ip4-lookup */
1469       if (is_slow_path)
1470         {
1471           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER))
1472             {
1473               if (nat_in2out_sm_unknown_proto (nm, b0, ip0, rx_fib_index0))
1474                 {
1475                   next0 = NAT44_EI_IN2OUT_NEXT_DROP;
1476                   b0->error =
1477                     node->errors[NAT44_EI_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
1478                 }
1479               vlib_increment_simple_counter (
1480                 is_slow_path ? &nm->counters.slowpath.in2out.other :
1481                                &nm->counters.fastpath.in2out.other,
1482                 thread_index, cntr_sw_if_index0, 1);
1483               goto trace0;
1484             }
1485
1486           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
1487             {
1488               next0 = nat44_ei_icmp_in2out_slow_path (
1489                 nm, b0, ip0, icmp0, rx_sw_if_index0, rx_fib_index0, node,
1490                 next0, now, thread_index, &s0);
1491               vlib_increment_simple_counter (
1492                 is_slow_path ? &nm->counters.slowpath.in2out.icmp :
1493                                &nm->counters.fastpath.in2out.icmp,
1494                 thread_index, cntr_sw_if_index0, 1);
1495               goto trace0;
1496             }
1497         }
1498       else
1499         {
1500           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER))
1501             {
1502               next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1503               goto trace0;
1504             }
1505
1506           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
1507             {
1508               next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1509               goto trace0;
1510             }
1511         }
1512
1513       init_nat_k (&kv0, ip0->src_address,
1514                   vnet_buffer (b0)->ip.reass.l4_src_port, rx_fib_index0,
1515                   proto0);
1516
1517       if (clib_bihash_search_8_8 (&nm->in2out, &kv0, &value0))
1518         {
1519           if (is_slow_path)
1520             {
1521               if (is_output_feature)
1522                 {
1523                   if (PREDICT_FALSE (nat44_ei_not_translate_output_feature (
1524                         nm, ip0, proto0,
1525                         vnet_buffer (b0)->ip.reass.l4_src_port,
1526                         vnet_buffer (b0)->ip.reass.l4_dst_port, thread_index,
1527                         rx_sw_if_index0)))
1528                     goto trace0;
1529
1530                   /*
1531                    * Send DHCP packets to the ipv4 stack, or we won't
1532                    * be able to use dhcp client on the outside interface
1533                    */
1534                   if (PREDICT_FALSE
1535                       (proto0 == NAT_PROTOCOL_UDP
1536                        && (vnet_buffer (b0)->ip.reass.l4_dst_port ==
1537                            clib_host_to_net_u16
1538                            (UDP_DST_PORT_dhcp_to_server))
1539                        && ip0->dst_address.as_u32 == 0xffffffff))
1540                     goto trace0;
1541                 }
1542               else
1543                 {
1544                   if (PREDICT_FALSE (nat44_ei_not_translate (
1545                         nm, node, rx_sw_if_index0, ip0, proto0, rx_fib_index0,
1546                         thread_index)))
1547                     goto trace0;
1548                 }
1549
1550               next0 = slow_path (nm, b0, ip0, ip0->src_address,
1551                                  vnet_buffer (b0)->ip.reass.l4_src_port,
1552                                  rx_fib_index0, proto0, &s0, node, next0,
1553                                  thread_index, now);
1554
1555               if (PREDICT_FALSE (next0 == NAT44_EI_IN2OUT_NEXT_DROP))
1556                 goto trace0;
1557
1558               if (PREDICT_FALSE (!s0))
1559                 goto trace0;
1560             }
1561           else
1562             {
1563               next0 = NAT44_EI_IN2OUT_NEXT_SLOW_PATH;
1564               goto trace0;
1565             }
1566         }
1567       else
1568         s0 = pool_elt_at_index (nm->per_thread_data[thread_index].sessions,
1569                                 nat_value_get_session_index (&value0));
1570
1571       b0->flags |= VNET_BUFFER_F_IS_NATED;
1572
1573       old_addr0 = ip0->src_address.as_u32;
1574       ip0->src_address = s0->out2in.addr;
1575       new_addr0 = ip0->src_address.as_u32;
1576       if (!is_output_feature)
1577         vnet_buffer (b0)->sw_if_index[VLIB_TX] = s0->out2in.fib_index;
1578
1579       sum0 = ip0->checksum;
1580       sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1581                              ip4_header_t, src_address /* changed member */ );
1582       ip0->checksum = ip_csum_fold (sum0);
1583
1584       if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
1585         {
1586           if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
1587             {
1588               old_port0 = vnet_buffer (b0)->ip.reass.l4_src_port;
1589               new_port0 = udp0->src_port = s0->out2in.port;
1590               sum0 = tcp0->checksum;
1591               sum0 =
1592                 ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
1593                                 dst_address /* changed member */ );
1594               sum0 =
1595                 ip_csum_update (sum0, old_port0, new_port0,
1596                                 ip4_header_t /* cheat */ ,
1597                                 length /* changed member */ );
1598               mss_clamping (nm->mss_clamping, tcp0, &sum0);
1599               tcp0->checksum = ip_csum_fold (sum0);
1600             }
1601           vlib_increment_simple_counter (is_slow_path ?
1602                                            &nm->counters.slowpath.in2out.tcp :
1603                                            &nm->counters.fastpath.in2out.tcp,
1604                                          thread_index, cntr_sw_if_index0, 1);
1605         }
1606       else
1607         {
1608           if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
1609             {
1610               udp0->src_port = s0->out2in.port;
1611               if (PREDICT_FALSE (udp0->checksum))
1612                 {
1613                   old_port0 = vnet_buffer (b0)->ip.reass.l4_src_port;
1614                   new_port0 = udp0->src_port;
1615                   sum0 = udp0->checksum;
1616                   sum0 =
1617                     ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
1618                                     dst_address /* changed member */ );
1619                   sum0 =
1620                     ip_csum_update (sum0, old_port0, new_port0,
1621                                     ip4_header_t /* cheat */ ,
1622                                     length /* changed member */ );
1623                   udp0->checksum = ip_csum_fold (sum0);
1624                 }
1625             }
1626           vlib_increment_simple_counter (is_slow_path ?
1627                                            &nm->counters.slowpath.in2out.udp :
1628                                            &nm->counters.fastpath.in2out.udp,
1629                                          thread_index, cntr_sw_if_index0, 1);
1630         }
1631
1632       /* Accounting */
1633       nat44_ei_session_update_counters (
1634         s0, now, vlib_buffer_length_in_chain (vm, b0), thread_index);
1635       /* Per-user LRU list maintenance */
1636       nat44_ei_session_update_lru (nm, s0, thread_index);
1637
1638     trace0:
1639       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1640                          && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1641         {
1642           nat44_ei_in2out_trace_t *t =
1643             vlib_add_trace (vm, node, b0, sizeof (*t));
1644           t->is_slow_path = is_slow_path;
1645           t->sw_if_index = rx_sw_if_index0;
1646           t->next_index = next0;
1647           t->session_index = ~0;
1648           if (s0)
1649             t->session_index = s0 - nm->per_thread_data[thread_index].sessions;
1650         }
1651
1652       if (next0 == NAT44_EI_IN2OUT_NEXT_DROP)
1653         {
1654           vlib_increment_simple_counter (
1655             is_slow_path ? &nm->counters.slowpath.in2out.drops :
1656                            &nm->counters.fastpath.in2out.drops,
1657             thread_index, cntr_sw_if_index0, 1);
1658         }
1659
1660       n_left_from--;
1661       next[0] = next0;
1662       next++;
1663     }
1664
1665   vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts,
1666                                frame->n_vectors);
1667   return frame->n_vectors;
1668 }
1669
1670 VLIB_NODE_FN (nat44_ei_in2out_node)
1671 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
1672 {
1673   return nat44_ei_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */,
1674                                          0);
1675 }
1676
1677 VLIB_REGISTER_NODE (nat44_ei_in2out_node) = {
1678   .name = "nat44-ei-in2out",
1679   .vector_size = sizeof (u32),
1680   .format_trace = format_nat44_ei_in2out_trace,
1681   .type = VLIB_NODE_TYPE_INTERNAL,
1682
1683   .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings),
1684   .error_strings = nat44_ei_in2out_error_strings,
1685
1686   .runtime_data_bytes = sizeof (nat44_ei_runtime_t),
1687
1688   .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT,
1689
1690   /* edit / add dispositions here */
1691   .next_nodes = {
1692     [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop",
1693     [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1694     [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-slowpath",
1695     [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1696     [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-ip4-lookup",
1697   },
1698 };
1699
1700 VLIB_NODE_FN (nat44_ei_in2out_output_node)
1701 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
1702 {
1703   return nat44_ei_in2out_node_fn_inline (vm, node, frame, 0 /* is_slow_path */,
1704                                          1);
1705 }
1706
1707 VLIB_REGISTER_NODE (nat44_ei_in2out_output_node) = {
1708   .name = "nat44-ei-in2out-output",
1709   .vector_size = sizeof (u32),
1710   .format_trace = format_nat44_ei_in2out_trace,
1711   .type = VLIB_NODE_TYPE_INTERNAL,
1712
1713   .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings),
1714   .error_strings = nat44_ei_in2out_error_strings,
1715
1716   .runtime_data_bytes = sizeof (nat44_ei_runtime_t),
1717
1718   .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT,
1719
1720   /* edit / add dispositions here */
1721   .next_nodes = {
1722     [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop",
1723     [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "interface-output",
1724     [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-output-slowpath",
1725     [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1726     [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-interface-output",
1727   },
1728 };
1729
1730 VLIB_NODE_FN (nat44_ei_in2out_slowpath_node)
1731 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
1732 {
1733   return nat44_ei_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */,
1734                                          0);
1735 }
1736
1737 VLIB_REGISTER_NODE (nat44_ei_in2out_slowpath_node) = {
1738   .name = "nat44-ei-in2out-slowpath",
1739   .vector_size = sizeof (u32),
1740   .format_trace = format_nat44_ei_in2out_trace,
1741   .type = VLIB_NODE_TYPE_INTERNAL,
1742
1743   .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings),
1744   .error_strings = nat44_ei_in2out_error_strings,
1745
1746   .runtime_data_bytes = sizeof (nat44_ei_runtime_t),
1747
1748   .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT,
1749
1750   /* edit / add dispositions here */
1751   .next_nodes = {
1752     [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop",
1753     [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1754     [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-slowpath",
1755     [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1756     [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-ip4-lookup",
1757   },
1758 };
1759
1760 VLIB_NODE_FN (nat44_ei_in2out_output_slowpath_node)
1761 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
1762 {
1763   return nat44_ei_in2out_node_fn_inline (vm, node, frame, 1 /* is_slow_path */,
1764                                          1);
1765 }
1766
1767 VLIB_REGISTER_NODE (nat44_ei_in2out_output_slowpath_node) = {
1768   .name = "nat44-ei-in2out-output-slowpath",
1769   .vector_size = sizeof (u32),
1770   .format_trace = format_nat44_ei_in2out_trace,
1771   .type = VLIB_NODE_TYPE_INTERNAL,
1772
1773   .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings),
1774   .error_strings = nat44_ei_in2out_error_strings,
1775
1776   .runtime_data_bytes = sizeof (nat44_ei_runtime_t),
1777
1778   .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT,
1779
1780   /* edit / add dispositions here */
1781   .next_nodes = {
1782     [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop",
1783     [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "interface-output",
1784     [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-output-slowpath",
1785     [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1786     [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-interface-output",
1787   },
1788 };
1789
1790 VLIB_NODE_FN (nat44_ei_in2out_fast_node)
1791 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
1792 {
1793   u32 n_left_from, *from, *to_next;
1794   u32 thread_index = vm->thread_index;
1795   nat44_ei_in2out_next_t next_index;
1796   nat44_ei_main_t *nm = &nat44_ei_main;
1797   int is_hairpinning = 0;
1798
1799   from = vlib_frame_vector_args (frame);
1800   n_left_from = frame->n_vectors;
1801   next_index = node->cached_next_index;
1802
1803   while (n_left_from > 0)
1804     {
1805       u32 n_left_to_next;
1806
1807       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
1808
1809       while (n_left_from > 0 && n_left_to_next > 0)
1810         {
1811           u32 bi0;
1812           vlib_buffer_t *b0;
1813           u32 next0;
1814           u32 sw_if_index0;
1815           ip4_header_t *ip0;
1816           ip_csum_t sum0;
1817           u32 new_addr0, old_addr0;
1818           u16 old_port0, new_port0;
1819           udp_header_t *udp0;
1820           tcp_header_t *tcp0;
1821           icmp46_header_t *icmp0;
1822           u32 proto0;
1823           u32 rx_fib_index0;
1824           ip4_address_t sm0_addr;
1825           u16 sm0_port;
1826           u32 sm0_fib_index;
1827           u32 required_thread_index = thread_index;
1828
1829           /* speculatively enqueue b0 to the current next frame */
1830           bi0 = from[0];
1831           to_next[0] = bi0;
1832           from += 1;
1833           to_next += 1;
1834           n_left_from -= 1;
1835           n_left_to_next -= 1;
1836
1837           b0 = vlib_get_buffer (vm, bi0);
1838           next0 = NAT44_EI_IN2OUT_NEXT_LOOKUP;
1839
1840           ip0 = vlib_buffer_get_current (b0);
1841           udp0 = ip4_next_header (ip0);
1842           tcp0 = (tcp_header_t *) udp0;
1843           icmp0 = (icmp46_header_t *) udp0;
1844
1845           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
1846           rx_fib_index0 =
1847             ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
1848
1849           if (PREDICT_FALSE (ip0->ttl == 1))
1850             {
1851               vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
1852               icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
1853                                            ICMP4_time_exceeded_ttl_exceeded_in_transit,
1854                                            0);
1855               next0 = NAT44_EI_IN2OUT_NEXT_ICMP_ERROR;
1856               goto trace0;
1857             }
1858
1859           proto0 = ip_proto_to_nat_proto (ip0->protocol);
1860
1861           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_OTHER))
1862             goto trace0;
1863
1864           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
1865             {
1866               next0 = nat44_ei_icmp_in2out (b0, ip0, icmp0, sw_if_index0,
1867                                             rx_fib_index0, node, next0, ~0, 0);
1868               goto trace0;
1869             }
1870
1871           if (nat44_ei_static_mapping_match (
1872                 ip0->src_address, udp0->src_port, rx_fib_index0, proto0,
1873                 &sm0_addr, &sm0_port, &sm0_fib_index, 0, 0, 0))
1874             {
1875               b0->error = node->errors[NAT44_EI_IN2OUT_ERROR_NO_TRANSLATION];
1876               next0 = NAT44_EI_IN2OUT_NEXT_DROP;
1877               goto trace0;
1878             }
1879
1880           new_addr0 = sm0_addr.as_u32;
1881           new_port0 = sm0_port;
1882           vnet_buffer (b0)->sw_if_index[VLIB_TX] = sm0_fib_index;
1883           old_addr0 = ip0->src_address.as_u32;
1884           ip0->src_address.as_u32 = new_addr0;
1885
1886           sum0 = ip0->checksum;
1887           sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1888                                  ip4_header_t,
1889                                  src_address /* changed member */ );
1890           ip0->checksum = ip_csum_fold (sum0);
1891
1892           if (PREDICT_FALSE (new_port0 != udp0->dst_port))
1893             {
1894               old_port0 = udp0->src_port;
1895               udp0->src_port = new_port0;
1896
1897               if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
1898                 {
1899                   sum0 = tcp0->checksum;
1900                   sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1901                                          ip4_header_t,
1902                                          dst_address /* changed member */ );
1903                   sum0 = ip_csum_update (sum0, old_port0, new_port0,
1904                                          ip4_header_t /* cheat */ ,
1905                                          length /* changed member */ );
1906                   mss_clamping (nm->mss_clamping, tcp0, &sum0);
1907                   tcp0->checksum = ip_csum_fold (sum0);
1908                 }
1909               else if (udp0->checksum)
1910                 {
1911                   sum0 = udp0->checksum;
1912                   sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1913                                          ip4_header_t,
1914                                          dst_address /* changed member */ );
1915                   sum0 = ip_csum_update (sum0, old_port0, new_port0,
1916                                          ip4_header_t /* cheat */ ,
1917                                          length /* changed member */ );
1918                   udp0->checksum = ip_csum_fold (sum0);
1919                 }
1920             }
1921           else
1922             {
1923               if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
1924                 {
1925                   sum0 = tcp0->checksum;
1926                   sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1927                                          ip4_header_t,
1928                                          dst_address /* changed member */ );
1929                   mss_clamping (nm->mss_clamping, tcp0, &sum0);
1930                   tcp0->checksum = ip_csum_fold (sum0);
1931                 }
1932               else if (udp0->checksum)
1933                 {
1934                   sum0 = udp0->checksum;
1935                   sum0 = ip_csum_update (sum0, old_addr0, new_addr0,
1936                                          ip4_header_t,
1937                                          dst_address /* changed member */ );
1938                   udp0->checksum = ip_csum_fold (sum0);
1939                 }
1940             }
1941
1942           /* Hairpinning */
1943           is_hairpinning = nat44_ei_hairpinning (
1944             vm, node, nm, thread_index, b0, ip0, udp0, tcp0, proto0,
1945             0 /* do_trace */, &required_thread_index);
1946
1947           if (thread_index != required_thread_index)
1948             {
1949               vnet_buffer (b0)->snat.required_thread_index =
1950                 required_thread_index;
1951               next0 = NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF;
1952             }
1953
1954         trace0:
1955           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1956                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1957             {
1958               nat44_ei_in2out_trace_t *t =
1959                 vlib_add_trace (vm, node, b0, sizeof (*t));
1960               t->sw_if_index = sw_if_index0;
1961               t->next_index = next0;
1962               t->is_hairpinning = is_hairpinning;
1963             }
1964
1965           if (next0 != NAT44_EI_IN2OUT_NEXT_DROP)
1966             {
1967
1968               vlib_increment_simple_counter (
1969                 &nm->counters.fastpath.in2out.other, sw_if_index0,
1970                 vm->thread_index, 1);
1971             }
1972
1973           /* verify speculative enqueue, maybe switch current next frame */
1974           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1975                                            to_next, n_left_to_next,
1976                                            bi0, next0);
1977         }
1978
1979       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1980     }
1981
1982   return frame->n_vectors;
1983 }
1984
1985 VLIB_REGISTER_NODE (nat44_ei_in2out_fast_node) = {
1986   .name = "nat44-ei-in2out-fast",
1987   .vector_size = sizeof (u32),
1988   .format_trace = format_nat44_ei_in2out_fast_trace,
1989   .type = VLIB_NODE_TYPE_INTERNAL,
1990
1991   .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings),
1992   .error_strings = nat44_ei_in2out_error_strings,
1993
1994   .runtime_data_bytes = sizeof (nat44_ei_runtime_t),
1995
1996   .n_next_nodes = NAT44_EI_IN2OUT_N_NEXT,
1997
1998   /* edit / add dispositions here */
1999   .next_nodes = {
2000     [NAT44_EI_IN2OUT_NEXT_DROP] = "error-drop",
2001     [NAT44_EI_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
2002     [NAT44_EI_IN2OUT_NEXT_SLOW_PATH] = "nat44-ei-in2out-slowpath",
2003     [NAT44_EI_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
2004     [NAT44_EI_IN2OUT_NEXT_HAIRPINNING_HANDOFF] = "nat44-ei-in2out-hairpinning-handoff-ip4-lookup",
2005   },
2006 };
2007
2008 VLIB_NODE_FN (nat44_ei_in2out_hairpinning_handoff_ip4_lookup_node)
2009 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
2010 {
2011   return nat44_ei_hairpinning_handoff_fn_inline (
2012     vm, node, frame,
2013     nat44_ei_main.in2out_hairpinning_finish_ip4_lookup_node_fq_index);
2014 }
2015
2016 VLIB_REGISTER_NODE (nat44_ei_in2out_hairpinning_handoff_ip4_lookup_node) = {
2017   .name = "nat44-ei-in2out-hairpinning-handoff-ip4-lookup",
2018   .vector_size = sizeof (u32),
2019   .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings),
2020   .error_strings = nat44_ei_hairpinning_handoff_error_strings,
2021   .format_trace = format_nat44_ei_hairpinning_handoff_trace,
2022
2023   .n_next_nodes = 1,
2024
2025   .next_nodes = {
2026     [0] = "error-drop",
2027   },
2028 };
2029
2030 VLIB_NODE_FN (nat44_ei_in2out_hairpinning_handoff_interface_output_node)
2031 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
2032 {
2033   return nat44_ei_hairpinning_handoff_fn_inline (
2034     vm, node, frame,
2035     nat44_ei_main.in2out_hairpinning_finish_interface_output_node_fq_index);
2036 }
2037
2038 VLIB_REGISTER_NODE (nat44_ei_in2out_hairpinning_handoff_interface_output_node) = {
2039   .name = "nat44-ei-in2out-hairpinning-handoff-interface-output",
2040   .vector_size = sizeof (u32),
2041   .n_errors = ARRAY_LEN(nat44_ei_hairpinning_handoff_error_strings),
2042   .error_strings = nat44_ei_hairpinning_handoff_error_strings,
2043   .format_trace = format_nat44_ei_hairpinning_handoff_trace,
2044
2045   .n_next_nodes = 1,
2046
2047   .next_nodes = {
2048     [0] = "error-drop",
2049   },
2050 };
2051
2052 static_always_inline int
2053 nat44_ei_in2out_hairpinning_finish_inline (vlib_main_t *vm,
2054                                            vlib_node_runtime_t *node,
2055                                            vlib_frame_t *frame)
2056 {
2057   u32 n_left_from, *from, *to_next;
2058   u32 thread_index = vm->thread_index;
2059   nat44_ei_in2out_next_t next_index;
2060   nat44_ei_main_t *nm = &nat44_ei_main;
2061   int is_hairpinning = 0;
2062
2063   from = vlib_frame_vector_args (frame);
2064   n_left_from = frame->n_vectors;
2065   next_index = node->cached_next_index;
2066
2067   while (n_left_from > 0)
2068     {
2069       u32 n_left_to_next;
2070
2071       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
2072
2073       while (n_left_from > 0 && n_left_to_next > 0)
2074         {
2075           u32 bi0;
2076           vlib_buffer_t *b0;
2077           u32 next0;
2078           u32 sw_if_index0;
2079           ip4_header_t *ip0;
2080           udp_header_t *udp0;
2081           tcp_header_t *tcp0;
2082           icmp46_header_t *icmp0;
2083           u32 proto0;
2084           u32 required_thread_index = thread_index;
2085
2086           /* speculatively enqueue b0 to the current next frame */
2087           bi0 = from[0];
2088           to_next[0] = bi0;
2089           from += 1;
2090           to_next += 1;
2091           n_left_from -= 1;
2092           n_left_to_next -= 1;
2093
2094           b0 = vlib_get_buffer (vm, bi0);
2095           next0 = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP;
2096
2097           ip0 = vlib_buffer_get_current (b0);
2098           udp0 = ip4_next_header (ip0);
2099           tcp0 = (tcp_header_t *) udp0;
2100           icmp0 = (icmp46_header_t *) udp0;
2101
2102           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
2103           proto0 = ip_proto_to_nat_proto (ip0->protocol);
2104
2105           switch (proto0)
2106             {
2107             case NAT_PROTOCOL_TCP:
2108               // fallthrough
2109             case NAT_PROTOCOL_UDP:
2110               is_hairpinning = nat44_ei_hairpinning (
2111                 vm, node, nm, thread_index, b0, ip0, udp0, tcp0, proto0,
2112                 0 /* do_trace */, &required_thread_index);
2113               break;
2114             case NAT_PROTOCOL_ICMP:
2115               is_hairpinning = (0 == nat44_ei_icmp_hairpinning (
2116                                        nm, b0, thread_index, ip0, icmp0,
2117                                        &required_thread_index));
2118               break;
2119             case NAT_PROTOCOL_OTHER:
2120               // this should never happen
2121               next0 = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP;
2122               break;
2123             }
2124
2125           if (thread_index != required_thread_index)
2126             {
2127               // but we already did a handoff ...
2128               next0 = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP;
2129             }
2130
2131           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE) &&
2132                              (b0->flags & VLIB_BUFFER_IS_TRACED)))
2133             {
2134               nat44_ei_in2out_trace_t *t =
2135                 vlib_add_trace (vm, node, b0, sizeof (*t));
2136               t->sw_if_index = sw_if_index0;
2137               t->next_index = next0;
2138               t->is_hairpinning = is_hairpinning;
2139             }
2140
2141           if (next0 != NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP)
2142             {
2143               vlib_increment_simple_counter (
2144                 &nm->counters.fastpath.in2out.other, sw_if_index0,
2145                 vm->thread_index, 1);
2146             }
2147
2148           /* verify speculative enqueue, maybe switch current next frame */
2149           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
2150                                            n_left_to_next, bi0, next0);
2151         }
2152
2153       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
2154     }
2155
2156   return frame->n_vectors;
2157 }
2158
2159 VLIB_NODE_FN (nat44_ei_in2out_hairpinning_finish_ip4_lookup_node)
2160 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
2161 {
2162   return nat44_ei_in2out_hairpinning_finish_inline (vm, node, frame);
2163 }
2164
2165 VLIB_REGISTER_NODE (nat44_ei_in2out_hairpinning_finish_ip4_lookup_node) = {
2166   .name = "nat44-ei-in2out-hairpinning-finish-ip4-lookup",
2167   .vector_size = sizeof (u32),
2168   .format_trace = format_nat44_ei_in2out_fast_trace,
2169   .type = VLIB_NODE_TYPE_INTERNAL,
2170
2171   .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings),
2172   .error_strings = nat44_ei_in2out_error_strings,
2173
2174   .runtime_data_bytes = sizeof (nat44_ei_runtime_t),
2175
2176   .n_next_nodes = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_N_NEXT,
2177
2178   /* edit / add dispositions here */
2179   .next_nodes = {
2180     [NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP] = "error-drop",
2181     [NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP] = "ip4-lookup",
2182   },
2183 };
2184
2185 VLIB_NODE_FN (nat44_ei_in2out_hairpinning_finish_interface_output_node)
2186 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
2187 {
2188   return nat44_ei_in2out_hairpinning_finish_inline (vm, node, frame);
2189 }
2190
2191 VLIB_REGISTER_NODE (nat44_ei_in2out_hairpinning_finish_interface_output_node) = {
2192   .name = "nat44-ei-in2out-hairpinning-finish-interface-output",
2193   .vector_size = sizeof (u32),
2194   .format_trace = format_nat44_ei_in2out_fast_trace,
2195   .type = VLIB_NODE_TYPE_INTERNAL,
2196
2197   .n_errors = ARRAY_LEN(nat44_ei_in2out_error_strings),
2198   .error_strings = nat44_ei_in2out_error_strings,
2199
2200   .runtime_data_bytes = sizeof (nat44_ei_runtime_t),
2201
2202   .n_next_nodes = NAT44_EI_IN2OUT_HAIRPINNING_FINISH_N_NEXT,
2203
2204   /* edit / add dispositions here */
2205   .next_nodes = {
2206     [NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_DROP] = "error-drop",
2207     [NAT44_EI_IN2OUT_HAIRPINNING_FINISH_NEXT_LOOKUP] = "interface-output",
2208   },
2209 };
2210
2211 /*
2212  * fd.io coding-style-patch-verification: ON
2213  *
2214  * Local Variables:
2215  * eval: (c-set-style "gnu")
2216  * End:
2217  */