nat: remove non-error error counters
[vpp.git] / src / plugins / nat / det44 / det44_in2out.c
1 /*
2  * Copyright (c) 2020 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 /**
17  * @file
18  * @brief Deterministic NAT (CGN) inside to outside translation
19  */
20
21 #include <vlib/vlib.h>
22 #include <vnet/vnet.h>
23 #include <vnet/ip/ip.h>
24 #include <vnet/fib/ip4_fib.h>
25 #include <vppinfra/error.h>
26 #include <vppinfra/elog.h>
27
28 #include <nat/det44/det44.h>
29 #include <nat/det44/det44_inlines.h>
30
31 #include <nat/lib/lib.h>
32 #include <nat/lib/inlines.h>
33 #include <nat/lib/nat_inlines.h>
34
35 typedef enum
36 {
37   DET44_IN2OUT_NEXT_LOOKUP,
38   DET44_IN2OUT_NEXT_DROP,
39   DET44_IN2OUT_NEXT_ICMP_ERROR,
40   DET44_IN2OUT_N_NEXT,
41 } det44_in2out_next_t;
42
43 typedef struct
44 {
45   u32 sw_if_index;
46   u32 next_index;
47   u32 session_index;
48 } det44_in2out_trace_t;
49
50 #define foreach_det44_in2out_error                 \
51 _(UNSUPPORTED_PROTOCOL, "Unsupported protocol")    \
52 _(NO_TRANSLATION, "No translation")                \
53 _(BAD_ICMP_TYPE, "unsupported ICMP type")          \
54 _(OUT_OF_PORTS, "Out of ports")                    \
55 _(IN2OUT_PACKETS, "Good in2out packets processed")
56
57 typedef enum
58 {
59 #define _(sym,str) DET44_IN2OUT_ERROR_##sym,
60   foreach_det44_in2out_error
61 #undef _
62     DET44_IN2OUT_N_ERROR,
63 } det44_in2out_error_t;
64
65 static char *det44_in2out_error_strings[] = {
66 #define _(sym,string) string,
67   foreach_det44_in2out_error
68 #undef _
69 };
70
71 static u8 *
72 format_det44_in2out_trace (u8 * s, va_list * args)
73 {
74   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
75   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
76   det44_in2out_trace_t *t = va_arg (*args, det44_in2out_trace_t *);
77
78   s = format (s, "DET44_IN2OUT: sw_if_index %d, next index %d, session %d",
79               t->sw_if_index, t->next_index, t->session_index);
80
81   return s;
82 }
83
84 #ifndef CLIB_MARCH_VARIANT
85 /**
86  * Get address and port values to be used for ICMP packet translation
87  * and create session if needed
88  *
89  * @param[in,out] node           NAT node runtime
90  * @param[in] thread_index       thread index
91  * @param[in,out] b0             buffer containing packet to be translated
92  * @param[in,out] ip0            ip header
93  * @param[out] p_proto           protocol used for matching
94  * @param[out] p_value           address and port after NAT translation
95  * @param[out] p_dont_translate  if packet should not be translated
96  * @param d                      optional parameter
97  * @param e                      optional parameter
98  */
99 u32
100 icmp_match_in2out_det (vlib_node_runtime_t * node,
101                        u32 thread_index, vlib_buffer_t * b0,
102                        ip4_header_t * ip0, ip4_address_t * addr,
103                        u16 * port, u32 * fib_index,
104                        nat_protocol_t * proto, void *d, void *e,
105                        u8 * dont_translate)
106 {
107   det44_main_t *dm = &det44_main;
108   vlib_main_t *vm = vlib_get_main ();
109   icmp46_header_t *icmp0;
110   u32 sw_if_index0;
111   u32 rx_fib_index0;
112   nat_protocol_t protocol;
113   snat_det_out_key_t key0;
114   u32 next0 = ~0;
115   icmp_echo_header_t *echo0, *inner_echo0 = 0;
116   ip4_header_t *inner_ip0;
117   void *l4_header = 0;
118   icmp46_header_t *inner_icmp0;
119   snat_det_map_t *mp0 = 0;
120   ip4_address_t new_addr0;
121   u16 lo_port0, i0;
122   snat_det_session_t *ses0 = 0;
123   ip4_address_t in_addr;
124   u16 in_port;
125   *dont_translate = 0;
126
127   icmp0 = (icmp46_header_t *) ip4_next_header (ip0);
128   echo0 = (icmp_echo_header_t *) (icmp0 + 1);
129   sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
130   rx_fib_index0 = ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
131
132   if (!icmp_type_is_error_message
133       (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags))
134     {
135       protocol = NAT_PROTOCOL_ICMP;
136       in_addr = ip0->src_address;
137       in_port = vnet_buffer (b0)->ip.reass.l4_src_port;
138     }
139   else
140     {
141       /* if error message, then it's not fragmented and we can access it */
142       inner_ip0 = (ip4_header_t *) (echo0 + 1);
143       l4_header = ip4_next_header (inner_ip0);
144       protocol = ip_proto_to_nat_proto (inner_ip0->protocol);
145       in_addr = inner_ip0->dst_address;
146       switch (protocol)
147         {
148         case NAT_PROTOCOL_ICMP:
149           inner_icmp0 = (icmp46_header_t *) l4_header;
150           inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);
151           in_port = inner_echo0->identifier;
152           break;
153         case NAT_PROTOCOL_UDP:
154         case NAT_PROTOCOL_TCP:
155           in_port = ((tcp_udp_header_t *) l4_header)->dst_port;
156           break;
157         default:
158           b0->error = node->errors[DET44_IN2OUT_ERROR_UNSUPPORTED_PROTOCOL];
159           next0 = DET44_IN2OUT_NEXT_DROP;
160           goto out;
161         }
162     }
163
164   mp0 = snat_det_map_by_user (&in_addr);
165   if (PREDICT_FALSE (!mp0))
166     {
167       if (PREDICT_FALSE (det44_translate (node, sw_if_index0, ip0,
168                                           IP_PROTOCOL_ICMP, rx_fib_index0)))
169         {
170           *dont_translate = 1;
171           goto out;
172         }
173       next0 = DET44_IN2OUT_NEXT_DROP;
174       b0->error = node->errors[DET44_IN2OUT_ERROR_NO_TRANSLATION];
175       goto out;
176     }
177
178   snat_det_forward (mp0, &in_addr, &new_addr0, &lo_port0);
179
180   key0.ext_host_addr = ip0->dst_address;
181   key0.ext_host_port = 0;
182
183   ses0 = snat_det_find_ses_by_in (mp0, &in_addr, in_port, key0);
184   if (PREDICT_FALSE (!ses0))
185     {
186       if (PREDICT_FALSE (det44_translate (node, sw_if_index0, ip0,
187                                           IP_PROTOCOL_ICMP, rx_fib_index0)))
188         {
189           *dont_translate = 1;
190           goto out;
191         }
192       if (icmp0->type != ICMP4_echo_request)
193         {
194           b0->error = node->errors[DET44_IN2OUT_ERROR_BAD_ICMP_TYPE];
195           next0 = DET44_IN2OUT_NEXT_DROP;
196           goto out;
197         }
198       for (i0 = 0; i0 < mp0->ports_per_host; i0++)
199         {
200           key0.out_port = clib_host_to_net_u16 (lo_port0 +
201                                                 ((i0 +
202                                                   clib_net_to_host_u16
203                                                   (echo0->identifier)) %
204                                                  mp0->ports_per_host));
205
206           if (snat_det_get_ses_by_out (mp0, &in_addr, key0.as_u64))
207             continue;
208
209           ses0 =
210             snat_det_ses_create (thread_index, mp0,
211                                  &in_addr, echo0->identifier, &key0);
212           break;
213         }
214       if (PREDICT_FALSE (!ses0))
215         {
216           next0 = DET44_IN2OUT_NEXT_DROP;
217           b0->error = node->errors[DET44_IN2OUT_ERROR_OUT_OF_PORTS];
218           goto out;
219         }
220     }
221
222   if (PREDICT_FALSE
223       (vnet_buffer (b0)->ip.reass.icmp_type_or_tcp_flags != ICMP4_echo_request
224        && !icmp_type_is_error_message (vnet_buffer (b0)->ip.
225                                        reass.icmp_type_or_tcp_flags)))
226     {
227       b0->error = node->errors[DET44_IN2OUT_ERROR_BAD_ICMP_TYPE];
228       next0 = DET44_IN2OUT_NEXT_DROP;
229       goto out;
230     }
231
232   u32 now = (u32) vlib_time_now (vm);
233
234   ses0->state = DET44_SESSION_ICMP_ACTIVE;
235   ses0->expire = now + dm->timeouts.icmp;
236
237 out:
238   *proto = protocol;
239   if (ses0)
240     {
241       *addr = new_addr0;
242       *fib_index = dm->outside_fib_index;
243       *port = ses0->out.out_port;
244     }
245   if (d)
246     *(snat_det_session_t **) d = ses0;
247   if (e)
248     *(snat_det_map_t **) e = mp0;
249   return next0;
250 }
251 #endif
252
253 #ifndef CLIB_MARCH_VARIANT
254 u32
255 det44_icmp_in2out (vlib_buffer_t * b0,
256                    ip4_header_t * ip0,
257                    icmp46_header_t * icmp0,
258                    u32 sw_if_index0,
259                    u32 rx_fib_index0,
260                    vlib_node_runtime_t * node,
261                    u32 next0, u32 thread_index, void *d, void *e)
262 {
263   vlib_main_t *vm = vlib_get_main ();
264   u16 old_id0, new_id0, port, checksum0, old_checksum0, new_checksum0;
265   u32 new_addr0, old_addr0, next0_tmp, fib_index;
266   icmp_echo_header_t *echo0, *inner_echo0;
267   icmp46_header_t *inner_icmp0;
268   ip4_header_t *inner_ip0;
269   ip4_address_t addr;
270   void *l4_header;
271   u8 dont_translate;
272   ip_csum_t sum0;
273   nat_protocol_t protocol;
274
275   echo0 = (icmp_echo_header_t *) (icmp0 + 1);
276   next0_tmp = icmp_match_in2out_det (node, thread_index, b0, ip0,
277                                      &addr, &port, &fib_index, &protocol,
278                                      d, e, &dont_translate);
279   if (next0_tmp != ~0)
280     next0 = next0_tmp;
281   if (next0 == DET44_IN2OUT_NEXT_DROP || dont_translate)
282     goto out;
283
284   if (PREDICT_TRUE (!ip4_is_fragment (ip0)))
285     {
286       sum0 =
287         ip_incremental_checksum_buffer (vm, b0,
288                                         (u8 *) icmp0 -
289                                         (u8 *) vlib_buffer_get_current (b0),
290                                         ntohs (ip0->length) -
291                                         ip4_header_bytes (ip0), 0);
292       checksum0 = ~ip_csum_fold (sum0);
293       if (PREDICT_FALSE (checksum0 != 0 && checksum0 != 0xffff))
294         {
295           next0 = DET44_IN2OUT_NEXT_DROP;
296           goto out;
297         }
298     }
299
300   old_addr0 = ip0->src_address.as_u32;
301   new_addr0 = ip0->src_address.as_u32 = addr.as_u32;
302
303   sum0 = ip0->checksum;
304   sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
305                          src_address /* changed member */ );
306   ip0->checksum = ip_csum_fold (sum0);
307
308   if (!vnet_buffer (b0)->ip.reass.is_non_first_fragment)
309     {
310       if (icmp0->checksum == 0)
311         icmp0->checksum = 0xffff;
312
313       if (!icmp_type_is_error_message (icmp0->type))
314         {
315           new_id0 = port;
316           if (PREDICT_FALSE (new_id0 != echo0->identifier))
317             {
318               old_id0 = echo0->identifier;
319               new_id0 = port;
320               echo0->identifier = new_id0;
321
322               sum0 = icmp0->checksum;
323               sum0 =
324                 ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
325                                 identifier);
326               icmp0->checksum = ip_csum_fold (sum0);
327             }
328         }
329       else
330         {
331           inner_ip0 = (ip4_header_t *) (echo0 + 1);
332           l4_header = ip4_next_header (inner_ip0);
333
334           if (!ip4_header_checksum_is_valid (inner_ip0))
335             {
336               next0 = DET44_IN2OUT_NEXT_DROP;
337               goto out;
338             }
339
340           /* update inner destination IP address */
341           old_addr0 = inner_ip0->dst_address.as_u32;
342           inner_ip0->dst_address = addr;
343           new_addr0 = inner_ip0->dst_address.as_u32;
344           sum0 = icmp0->checksum;
345           sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
346                                  dst_address /* changed member */ );
347           icmp0->checksum = ip_csum_fold (sum0);
348
349           /* update inner IP header checksum */
350           old_checksum0 = inner_ip0->checksum;
351           sum0 = inner_ip0->checksum;
352           sum0 = ip_csum_update (sum0, old_addr0, new_addr0, ip4_header_t,
353                                  dst_address /* changed member */ );
354           inner_ip0->checksum = ip_csum_fold (sum0);
355           new_checksum0 = inner_ip0->checksum;
356           sum0 = icmp0->checksum;
357           sum0 =
358             ip_csum_update (sum0, old_checksum0, new_checksum0, ip4_header_t,
359                             checksum);
360           icmp0->checksum = ip_csum_fold (sum0);
361
362           switch (protocol)
363             {
364             case NAT_PROTOCOL_ICMP:
365               inner_icmp0 = (icmp46_header_t *) l4_header;
366               inner_echo0 = (icmp_echo_header_t *) (inner_icmp0 + 1);
367
368               old_id0 = inner_echo0->identifier;
369               new_id0 = port;
370               inner_echo0->identifier = new_id0;
371
372               sum0 = icmp0->checksum;
373               sum0 =
374                 ip_csum_update (sum0, old_id0, new_id0, icmp_echo_header_t,
375                                 identifier);
376               icmp0->checksum = ip_csum_fold (sum0);
377               break;
378             case NAT_PROTOCOL_UDP:
379             case NAT_PROTOCOL_TCP:
380               old_id0 = ((tcp_udp_header_t *) l4_header)->dst_port;
381               new_id0 = port;
382               ((tcp_udp_header_t *) l4_header)->dst_port = new_id0;
383
384               sum0 = icmp0->checksum;
385               sum0 = ip_csum_update (sum0, old_id0, new_id0, tcp_udp_header_t,
386                                      dst_port);
387               icmp0->checksum = ip_csum_fold (sum0);
388               break;
389             default:
390               ASSERT (0);
391             }
392         }
393     }
394
395   if (vnet_buffer (b0)->sw_if_index[VLIB_TX] == ~0)
396     vnet_buffer (b0)->sw_if_index[VLIB_TX] = fib_index;
397 out:
398   return next0;
399 }
400 #endif
401
402 VLIB_NODE_FN (det44_in2out_node) (vlib_main_t * vm,
403                                   vlib_node_runtime_t * node,
404                                   vlib_frame_t * frame)
405 {
406   u32 n_left_from, *from, *to_next;
407   det44_in2out_next_t next_index;
408   u32 pkts_processed = 0;
409   det44_main_t *dm = &det44_main;
410   u32 now = (u32) vlib_time_now (vm);
411   u32 thread_index = vm->thread_index;
412
413   from = vlib_frame_vector_args (frame);
414   n_left_from = frame->n_vectors;
415   next_index = node->cached_next_index;
416
417   while (n_left_from > 0)
418     {
419       u32 n_left_to_next;
420
421       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
422
423       while (n_left_from >= 4 && n_left_to_next >= 2)
424         {
425           u32 bi0, bi1;
426           vlib_buffer_t *b0, *b1;
427           u32 next0, next1;
428           u32 sw_if_index0, sw_if_index1;
429           ip4_header_t *ip0, *ip1;
430           ip_csum_t sum0, sum1;
431           ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1;
432           u16 old_port0, new_port0, lo_port0, i0;
433           u16 old_port1, new_port1, lo_port1, i1;
434           udp_header_t *udp0, *udp1;
435           tcp_header_t *tcp0, *tcp1;
436           u32 proto0, proto1;
437           snat_det_out_key_t key0, key1;
438           snat_det_map_t *mp0, *mp1;
439           snat_det_session_t *ses0 = 0, *ses1 = 0;
440           u32 rx_fib_index0, rx_fib_index1;
441           icmp46_header_t *icmp0, *icmp1;
442
443           /* Prefetch next iteration. */
444           {
445             vlib_buffer_t *p2, *p3;
446
447             p2 = vlib_get_buffer (vm, from[2]);
448             p3 = vlib_get_buffer (vm, from[3]);
449
450             vlib_prefetch_buffer_header (p2, LOAD);
451             vlib_prefetch_buffer_header (p3, LOAD);
452
453             CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
454             CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD);
455           }
456
457           /* speculatively enqueue b0 and b1 to the current next frame */
458           to_next[0] = bi0 = from[0];
459           to_next[1] = bi1 = from[1];
460           from += 2;
461           to_next += 2;
462           n_left_from -= 2;
463           n_left_to_next -= 2;
464
465           b0 = vlib_get_buffer (vm, bi0);
466           b1 = vlib_get_buffer (vm, bi1);
467
468           next0 = DET44_IN2OUT_NEXT_LOOKUP;
469           next1 = DET44_IN2OUT_NEXT_LOOKUP;
470
471           ip0 = vlib_buffer_get_current (b0);
472           udp0 = ip4_next_header (ip0);
473           tcp0 = (tcp_header_t *) udp0;
474
475           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
476
477           if (PREDICT_FALSE (ip0->ttl == 1))
478             {
479               vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
480               icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
481                                            ICMP4_time_exceeded_ttl_exceeded_in_transit,
482                                            0);
483               next0 = DET44_IN2OUT_NEXT_ICMP_ERROR;
484               goto trace0;
485             }
486
487           proto0 = ip_proto_to_nat_proto (ip0->protocol);
488
489           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
490             {
491               rx_fib_index0 =
492                 ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
493               icmp0 = (icmp46_header_t *) udp0;
494
495               // TODO:
496               next0 = det44_icmp_in2out (b0, ip0, icmp0, sw_if_index0,
497                                          rx_fib_index0, node, next0,
498                                          thread_index, &ses0, &mp0);
499               goto trace0;
500             }
501
502           mp0 = snat_det_map_by_user (&ip0->src_address);
503           if (PREDICT_FALSE (!mp0))
504             {
505               det44_log_info ("no match for internal host %U",
506                               format_ip4_address, &ip0->src_address);
507               next0 = DET44_IN2OUT_NEXT_DROP;
508               b0->error = node->errors[DET44_IN2OUT_ERROR_NO_TRANSLATION];
509               goto trace0;
510             }
511
512           snat_det_forward (mp0, &ip0->src_address, &new_addr0, &lo_port0);
513
514           key0.ext_host_addr = ip0->dst_address;
515           key0.ext_host_port = tcp0->dst;
516
517           ses0 =
518             snat_det_find_ses_by_in (mp0, &ip0->src_address, tcp0->src, key0);
519           if (PREDICT_FALSE (!ses0))
520             {
521               for (i0 = 0; i0 < mp0->ports_per_host; i0++)
522                 {
523                   key0.out_port = clib_host_to_net_u16 (lo_port0 +
524                                                         ((i0 +
525                                                           clib_net_to_host_u16
526                                                           (tcp0->src)) %
527                                                          mp0->
528                                                          ports_per_host));
529
530                   if (snat_det_get_ses_by_out
531                       (mp0, &ip0->src_address, key0.as_u64))
532                     continue;
533
534                   ses0 =
535                     snat_det_ses_create (thread_index, mp0, &ip0->src_address,
536                                          tcp0->src, &key0);
537                   break;
538                 }
539               if (PREDICT_FALSE (!ses0))
540                 {
541                   /* too many sessions for user, send ICMP error packet */
542                   vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
543                   icmp4_error_set_vnet_buffer (b0,
544                                                ICMP4_destination_unreachable,
545                                                ICMP4_destination_unreachable_destination_unreachable_host,
546                                                0);
547                   next0 = DET44_IN2OUT_NEXT_ICMP_ERROR;
548                   goto trace0;
549                 }
550             }
551
552           old_port0 = udp0->src_port;
553           udp0->src_port = new_port0 = ses0->out.out_port;
554
555           old_addr0.as_u32 = ip0->src_address.as_u32;
556           ip0->src_address.as_u32 = new_addr0.as_u32;
557           vnet_buffer (b0)->sw_if_index[VLIB_TX] = dm->outside_fib_index;
558
559           sum0 = ip0->checksum;
560           sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
561                                  ip4_header_t,
562                                  src_address /* changed member */ );
563           ip0->checksum = ip_csum_fold (sum0);
564
565           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
566             {
567               if (tcp0->flags & TCP_FLAG_SYN)
568                 ses0->state = DET44_SESSION_TCP_SYN_SENT;
569               else if (tcp0->flags & TCP_FLAG_ACK
570                        && ses0->state == DET44_SESSION_TCP_SYN_SENT)
571                 ses0->state = DET44_SESSION_TCP_ESTABLISHED;
572               else if (tcp0->flags & TCP_FLAG_FIN
573                        && ses0->state == DET44_SESSION_TCP_ESTABLISHED)
574                 ses0->state = DET44_SESSION_TCP_FIN_WAIT;
575               else if (tcp0->flags & TCP_FLAG_ACK
576                        && ses0->state == DET44_SESSION_TCP_FIN_WAIT)
577                 snat_det_ses_close (mp0, ses0);
578               else if (tcp0->flags & TCP_FLAG_FIN
579                        && ses0->state == DET44_SESSION_TCP_CLOSE_WAIT)
580                 ses0->state = DET44_SESSION_TCP_LAST_ACK;
581               else if (tcp0->flags == 0
582                        && ses0->state == DET44_SESSION_UNKNOWN)
583                 ses0->state = DET44_SESSION_TCP_ESTABLISHED;
584
585               sum0 = tcp0->checksum;
586               sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
587                                      ip4_header_t,
588                                      dst_address /* changed member */ );
589               sum0 = ip_csum_update (sum0, old_port0, new_port0,
590                                      ip4_header_t /* cheat */ ,
591                                      length /* changed member */ );
592               mss_clamping (dm->mss_clamping, tcp0, &sum0);
593               tcp0->checksum = ip_csum_fold (sum0);
594             }
595           else
596             {
597               ses0->state = DET44_SESSION_UDP_ACTIVE;
598
599               if (PREDICT_FALSE (udp0->checksum))
600                 {
601                   sum0 = udp0->checksum;
602                   sum0 =
603                     ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
604                                     ip4_header_t,
605                                     dst_address /* changed member */ );
606                   sum0 =
607                     ip_csum_update (sum0, old_port0, new_port0,
608                                     ip4_header_t /* cheat */ ,
609                                     length /* changed member */ );
610                   udp0->checksum = ip_csum_fold (sum0);
611                 }
612             }
613
614           switch (ses0->state)
615             {
616             case DET44_SESSION_UDP_ACTIVE:
617               ses0->expire = now + dm->timeouts.udp;
618               break;
619             case DET44_SESSION_TCP_SYN_SENT:
620             case DET44_SESSION_TCP_FIN_WAIT:
621             case DET44_SESSION_TCP_CLOSE_WAIT:
622             case DET44_SESSION_TCP_LAST_ACK:
623               ses0->expire = now + dm->timeouts.tcp.transitory;
624               break;
625             case DET44_SESSION_TCP_ESTABLISHED:
626               ses0->expire = now + dm->timeouts.tcp.established;
627               break;
628             }
629
630         trace0:
631           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
632                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
633             {
634               det44_in2out_trace_t *t =
635                 vlib_add_trace (vm, node, b0, sizeof (*t));
636               t->sw_if_index = sw_if_index0;
637               t->next_index = next0;
638               t->session_index = ~0;
639               if (ses0)
640                 t->session_index = ses0 - mp0->sessions;
641             }
642
643           pkts_processed += next0 != DET44_IN2OUT_NEXT_DROP;
644
645           ip1 = vlib_buffer_get_current (b1);
646           udp1 = ip4_next_header (ip1);
647           tcp1 = (tcp_header_t *) udp1;
648
649           sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
650
651           if (PREDICT_FALSE (ip1->ttl == 1))
652             {
653               vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
654               icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
655                                            ICMP4_time_exceeded_ttl_exceeded_in_transit,
656                                            0);
657               next1 = DET44_IN2OUT_NEXT_ICMP_ERROR;
658               goto trace1;
659             }
660
661           proto1 = ip_proto_to_nat_proto (ip1->protocol);
662
663           if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_ICMP))
664             {
665               rx_fib_index1 =
666                 ip4_fib_table_get_index_for_sw_if_index (sw_if_index1);
667               icmp1 = (icmp46_header_t *) udp1;
668
669               next1 = det44_icmp_in2out (b1, ip1, icmp1, sw_if_index1,
670                                          rx_fib_index1, node, next1,
671                                          thread_index, &ses1, &mp1);
672               goto trace1;
673             }
674
675           mp1 = snat_det_map_by_user (&ip1->src_address);
676           if (PREDICT_FALSE (!mp1))
677             {
678               det44_log_info ("no match for internal host %U",
679                               format_ip4_address, &ip0->src_address);
680               next1 = DET44_IN2OUT_NEXT_DROP;
681               b1->error = node->errors[DET44_IN2OUT_ERROR_NO_TRANSLATION];
682               goto trace1;
683             }
684
685           snat_det_forward (mp1, &ip1->src_address, &new_addr1, &lo_port1);
686
687           key1.ext_host_addr = ip1->dst_address;
688           key1.ext_host_port = tcp1->dst;
689
690           ses1 =
691             snat_det_find_ses_by_in (mp1, &ip1->src_address, tcp1->src, key1);
692           if (PREDICT_FALSE (!ses1))
693             {
694               for (i1 = 0; i1 < mp1->ports_per_host; i1++)
695                 {
696                   key1.out_port = clib_host_to_net_u16 (lo_port1 +
697                                                         ((i1 +
698                                                           clib_net_to_host_u16
699                                                           (tcp1->src)) %
700                                                          mp1->
701                                                          ports_per_host));
702
703                   if (snat_det_get_ses_by_out
704                       (mp1, &ip1->src_address, key1.as_u64))
705                     continue;
706
707                   ses1 =
708                     snat_det_ses_create (thread_index, mp1, &ip1->src_address,
709                                          tcp1->src, &key1);
710                   break;
711                 }
712               if (PREDICT_FALSE (!ses1))
713                 {
714                   /* too many sessions for user, send ICMP error packet */
715                   vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
716                   icmp4_error_set_vnet_buffer (b1,
717                                                ICMP4_destination_unreachable,
718                                                ICMP4_destination_unreachable_destination_unreachable_host,
719                                                0);
720                   next1 = DET44_IN2OUT_NEXT_ICMP_ERROR;
721                   goto trace1;
722                 }
723             }
724
725           old_port1 = udp1->src_port;
726           udp1->src_port = new_port1 = ses1->out.out_port;
727
728           old_addr1.as_u32 = ip1->src_address.as_u32;
729           ip1->src_address.as_u32 = new_addr1.as_u32;
730           vnet_buffer (b1)->sw_if_index[VLIB_TX] = dm->outside_fib_index;
731
732           sum1 = ip1->checksum;
733           sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
734                                  ip4_header_t,
735                                  src_address /* changed member */ );
736           ip1->checksum = ip_csum_fold (sum1);
737
738           if (PREDICT_TRUE (proto1 == NAT_PROTOCOL_TCP))
739             {
740               if (tcp1->flags & TCP_FLAG_SYN)
741                 ses1->state = DET44_SESSION_TCP_SYN_SENT;
742               else if (tcp1->flags & TCP_FLAG_ACK
743                        && ses1->state == DET44_SESSION_TCP_SYN_SENT)
744                 ses1->state = DET44_SESSION_TCP_ESTABLISHED;
745               else if (tcp1->flags & TCP_FLAG_FIN
746                        && ses1->state == DET44_SESSION_TCP_ESTABLISHED)
747                 ses1->state = DET44_SESSION_TCP_FIN_WAIT;
748               else if (tcp1->flags & TCP_FLAG_ACK
749                        && ses1->state == DET44_SESSION_TCP_FIN_WAIT)
750                 snat_det_ses_close (mp1, ses1);
751               else if (tcp1->flags & TCP_FLAG_FIN
752                        && ses1->state == DET44_SESSION_TCP_CLOSE_WAIT)
753                 ses1->state = DET44_SESSION_TCP_LAST_ACK;
754               else if (tcp1->flags == 0
755                        && ses1->state == DET44_SESSION_UNKNOWN)
756                 ses1->state = DET44_SESSION_TCP_ESTABLISHED;
757
758               sum1 = tcp1->checksum;
759               sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
760                                      ip4_header_t,
761                                      dst_address /* changed member */ );
762               sum1 = ip_csum_update (sum1, old_port1, new_port1,
763                                      ip4_header_t /* cheat */ ,
764                                      length /* changed member */ );
765               mss_clamping (dm->mss_clamping, tcp1, &sum1);
766               tcp1->checksum = ip_csum_fold (sum1);
767             }
768           else
769             {
770               ses1->state = DET44_SESSION_UDP_ACTIVE;
771
772               if (PREDICT_FALSE (udp1->checksum))
773                 {
774                   sum1 = udp1->checksum;
775                   sum1 =
776                     ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
777                                     ip4_header_t,
778                                     dst_address /* changed member */ );
779                   sum1 =
780                     ip_csum_update (sum1, old_port1, new_port1,
781                                     ip4_header_t /* cheat */ ,
782                                     length /* changed member */ );
783                   udp1->checksum = ip_csum_fold (sum1);
784                 }
785             }
786
787           switch (ses1->state)
788             {
789             case DET44_SESSION_UDP_ACTIVE:
790               ses1->expire = now + dm->timeouts.udp;
791               break;
792             case DET44_SESSION_TCP_SYN_SENT:
793             case DET44_SESSION_TCP_FIN_WAIT:
794             case DET44_SESSION_TCP_CLOSE_WAIT:
795             case DET44_SESSION_TCP_LAST_ACK:
796               ses1->expire = now + dm->timeouts.tcp.transitory;
797               break;
798             case DET44_SESSION_TCP_ESTABLISHED:
799               ses1->expire = now + dm->timeouts.tcp.established;
800               break;
801             }
802
803         trace1:
804           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
805                              && (b1->flags & VLIB_BUFFER_IS_TRACED)))
806             {
807               det44_in2out_trace_t *t =
808                 vlib_add_trace (vm, node, b1, sizeof (*t));
809               t->sw_if_index = sw_if_index1;
810               t->next_index = next1;
811               t->session_index = ~0;
812               if (ses1)
813                 t->session_index = ses1 - mp1->sessions;
814             }
815
816           pkts_processed += next1 != DET44_IN2OUT_NEXT_DROP;
817
818           /* verify speculative enqueues, maybe switch current next frame */
819           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
820                                            to_next, n_left_to_next,
821                                            bi0, bi1, next0, next1);
822         }
823
824       while (n_left_from > 0 && n_left_to_next > 0)
825         {
826           u32 bi0;
827           vlib_buffer_t *b0;
828           u32 next0;
829           u32 sw_if_index0;
830           ip4_header_t *ip0;
831           ip_csum_t sum0;
832           ip4_address_t new_addr0, old_addr0;
833           u16 old_port0, new_port0, lo_port0, i0;
834           udp_header_t *udp0;
835           tcp_header_t *tcp0;
836           u32 proto0;
837           snat_det_out_key_t key0;
838           snat_det_map_t *mp0;
839           snat_det_session_t *ses0 = 0;
840           u32 rx_fib_index0;
841           icmp46_header_t *icmp0;
842
843           /* speculatively enqueue b0 to the current next frame */
844           bi0 = from[0];
845           to_next[0] = bi0;
846           from += 1;
847           to_next += 1;
848           n_left_from -= 1;
849           n_left_to_next -= 1;
850
851           b0 = vlib_get_buffer (vm, bi0);
852           next0 = DET44_IN2OUT_NEXT_LOOKUP;
853
854           ip0 = vlib_buffer_get_current (b0);
855           udp0 = ip4_next_header (ip0);
856           tcp0 = (tcp_header_t *) udp0;
857
858           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
859
860           if (PREDICT_FALSE (ip0->ttl == 1))
861             {
862               vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
863               icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
864                                            ICMP4_time_exceeded_ttl_exceeded_in_transit,
865                                            0);
866               next0 = DET44_IN2OUT_NEXT_ICMP_ERROR;
867               goto trace00;
868             }
869
870           proto0 = ip_proto_to_nat_proto (ip0->protocol);
871
872           if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
873             {
874               rx_fib_index0 =
875                 ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
876               icmp0 = (icmp46_header_t *) udp0;
877
878               next0 = det44_icmp_in2out (b0, ip0, icmp0, sw_if_index0,
879                                          rx_fib_index0, node, next0,
880                                          thread_index, &ses0, &mp0);
881               goto trace00;
882             }
883
884           mp0 = snat_det_map_by_user (&ip0->src_address);
885           if (PREDICT_FALSE (!mp0))
886             {
887               det44_log_info ("no match for internal host %U",
888                               format_ip4_address, &ip0->src_address);
889               next0 = DET44_IN2OUT_NEXT_DROP;
890               b0->error = node->errors[DET44_IN2OUT_ERROR_NO_TRANSLATION];
891               goto trace00;
892             }
893
894           snat_det_forward (mp0, &ip0->src_address, &new_addr0, &lo_port0);
895
896           key0.ext_host_addr = ip0->dst_address;
897           key0.ext_host_port = tcp0->dst;
898
899           ses0 =
900             snat_det_find_ses_by_in (mp0, &ip0->src_address, tcp0->src, key0);
901           if (PREDICT_FALSE (!ses0))
902             {
903               for (i0 = 0; i0 < mp0->ports_per_host; i0++)
904                 {
905                   key0.out_port = clib_host_to_net_u16 (lo_port0 +
906                                                         ((i0 +
907                                                           clib_net_to_host_u16
908                                                           (tcp0->src)) %
909                                                          mp0->
910                                                          ports_per_host));
911
912                   if (snat_det_get_ses_by_out
913                       (mp0, &ip0->src_address, key0.as_u64))
914                     continue;
915
916                   ses0 =
917                     snat_det_ses_create (thread_index, mp0, &ip0->src_address,
918                                          tcp0->src, &key0);
919                   break;
920                 }
921               if (PREDICT_FALSE (!ses0))
922                 {
923                   /* too many sessions for user, send ICMP error packet */
924                   vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
925                   icmp4_error_set_vnet_buffer (b0,
926                                                ICMP4_destination_unreachable,
927                                                ICMP4_destination_unreachable_destination_unreachable_host,
928                                                0);
929                   next0 = DET44_IN2OUT_NEXT_ICMP_ERROR;
930                   goto trace00;
931                 }
932             }
933
934           old_port0 = udp0->src_port;
935           udp0->src_port = new_port0 = ses0->out.out_port;
936
937           old_addr0.as_u32 = ip0->src_address.as_u32;
938           ip0->src_address.as_u32 = new_addr0.as_u32;
939           vnet_buffer (b0)->sw_if_index[VLIB_TX] = dm->outside_fib_index;
940
941           sum0 = ip0->checksum;
942           sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
943                                  ip4_header_t,
944                                  src_address /* changed member */ );
945           ip0->checksum = ip_csum_fold (sum0);
946
947           if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
948             {
949               if (tcp0->flags & TCP_FLAG_SYN)
950                 ses0->state = DET44_SESSION_TCP_SYN_SENT;
951               else if (tcp0->flags & TCP_FLAG_ACK
952                        && ses0->state == DET44_SESSION_TCP_SYN_SENT)
953                 ses0->state = DET44_SESSION_TCP_ESTABLISHED;
954               else if (tcp0->flags & TCP_FLAG_FIN
955                        && ses0->state == DET44_SESSION_TCP_ESTABLISHED)
956                 ses0->state = DET44_SESSION_TCP_FIN_WAIT;
957               else if (tcp0->flags & TCP_FLAG_ACK
958                        && ses0->state == DET44_SESSION_TCP_FIN_WAIT)
959                 snat_det_ses_close (mp0, ses0);
960               else if (tcp0->flags & TCP_FLAG_FIN
961                        && ses0->state == DET44_SESSION_TCP_CLOSE_WAIT)
962                 ses0->state = DET44_SESSION_TCP_LAST_ACK;
963               else if (tcp0->flags == 0
964                        && ses0->state == DET44_SESSION_UNKNOWN)
965                 ses0->state = DET44_SESSION_TCP_ESTABLISHED;
966
967               sum0 = tcp0->checksum;
968               sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
969                                      ip4_header_t,
970                                      dst_address /* changed member */ );
971               sum0 = ip_csum_update (sum0, old_port0, new_port0,
972                                      ip4_header_t /* cheat */ ,
973                                      length /* changed member */ );
974               mss_clamping (dm->mss_clamping, tcp0, &sum0);
975               tcp0->checksum = ip_csum_fold (sum0);
976             }
977           else
978             {
979               ses0->state = DET44_SESSION_UDP_ACTIVE;
980
981               if (PREDICT_FALSE (udp0->checksum))
982                 {
983                   sum0 = udp0->checksum;
984                   sum0 =
985                     ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
986                                     ip4_header_t,
987                                     dst_address /* changed member */ );
988                   sum0 =
989                     ip_csum_update (sum0, old_port0, new_port0,
990                                     ip4_header_t /* cheat */ ,
991                                     length /* changed member */ );
992                   udp0->checksum = ip_csum_fold (sum0);
993                 }
994             }
995
996           switch (ses0->state)
997             {
998             case DET44_SESSION_UDP_ACTIVE:
999               ses0->expire = now + dm->timeouts.udp;
1000               break;
1001             case DET44_SESSION_TCP_SYN_SENT:
1002             case DET44_SESSION_TCP_FIN_WAIT:
1003             case DET44_SESSION_TCP_CLOSE_WAIT:
1004             case DET44_SESSION_TCP_LAST_ACK:
1005               ses0->expire = now + dm->timeouts.tcp.transitory;
1006               break;
1007             case DET44_SESSION_TCP_ESTABLISHED:
1008               ses0->expire = now + dm->timeouts.tcp.established;
1009               break;
1010             }
1011
1012         trace00:
1013           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
1014                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
1015             {
1016               det44_in2out_trace_t *t =
1017                 vlib_add_trace (vm, node, b0, sizeof (*t));
1018               t->sw_if_index = sw_if_index0;
1019               t->next_index = next0;
1020               t->session_index = ~0;
1021               if (ses0)
1022                 t->session_index = ses0 - mp0->sessions;
1023             }
1024
1025           pkts_processed += next0 != DET44_IN2OUT_NEXT_DROP;
1026
1027           /* verify speculative enqueue, maybe switch current next frame */
1028           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
1029                                            to_next, n_left_to_next,
1030                                            bi0, next0);
1031         }
1032
1033       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
1034     }
1035
1036   vlib_node_increment_counter (vm, dm->in2out_node_index,
1037                                DET44_IN2OUT_ERROR_IN2OUT_PACKETS,
1038                                pkts_processed);
1039   return frame->n_vectors;
1040 }
1041
1042 /* *INDENT-OFF* */
1043 VLIB_REGISTER_NODE (det44_in2out_node) = {
1044   .name = "det44-in2out",
1045   .vector_size = sizeof (u32),
1046   .format_trace = format_det44_in2out_trace,
1047   .type = VLIB_NODE_TYPE_INTERNAL,
1048   .n_errors = ARRAY_LEN(det44_in2out_error_strings),
1049   .error_strings = det44_in2out_error_strings,
1050   .runtime_data_bytes = sizeof (det44_runtime_t),
1051   .n_next_nodes = DET44_IN2OUT_N_NEXT,
1052   /* edit / add dispositions here */
1053   .next_nodes = {
1054     [DET44_IN2OUT_NEXT_DROP] = "error-drop",
1055     [DET44_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1056     [DET44_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1057   },
1058 };
1059 /* *INDENT-ON* */
1060
1061 /*
1062  * fd.io coding-style-patch-verification: ON
1063  *
1064  * Local Variables:
1065  * eval: (c-set-style "gnu")
1066  * End:
1067  */