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