api: clean up use of deprecated flag
[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;
407   u32 pkts_processed = 0;
408   det44_main_t *dm = &det44_main;
409   u32 now = (u32) vlib_time_now (vm);
410   u32 thread_index = vm->thread_index;
411
412   from = vlib_frame_vector_args (frame);
413   n_left_from = frame->n_vectors;
414
415   vlib_buffer_t *bufs[VLIB_FRAME_SIZE], **b = bufs;
416   u16 nexts[VLIB_FRAME_SIZE], *next = nexts;
417   vlib_get_buffers (vm, from, b, n_left_from);
418
419   while (n_left_from >= 2)
420     {
421       vlib_buffer_t *b0, *b1;
422       u32 next0, next1;
423       u32 sw_if_index0, sw_if_index1;
424       ip4_header_t *ip0, *ip1;
425       ip_csum_t sum0, sum1;
426       ip4_address_t new_addr0, old_addr0, new_addr1, old_addr1;
427       u16 old_port0, new_port0, lo_port0, i0;
428       u16 old_port1, new_port1, lo_port1, i1;
429       udp_header_t *udp0, *udp1;
430       tcp_header_t *tcp0, *tcp1;
431       u32 proto0, proto1;
432       snat_det_out_key_t key0, key1;
433       snat_det_map_t *mp0, *mp1;
434       snat_det_session_t *ses0 = 0, *ses1 = 0;
435       u32 rx_fib_index0, rx_fib_index1;
436       icmp46_header_t *icmp0, *icmp1;
437
438       b0 = *b;
439       b++;
440       b1 = *b;
441       b++;
442
443       /* Prefetch next iteration. */
444       if (PREDICT_TRUE (n_left_from >= 4))
445         {
446           vlib_buffer_t *p2, *p3;
447
448           p2 = *b;
449           p3 = *(b + 1);
450
451           vlib_prefetch_buffer_header (p2, LOAD);
452           vlib_prefetch_buffer_header (p3, LOAD);
453
454           CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, LOAD);
455           CLIB_PREFETCH (p3->data, CLIB_CACHE_LINE_BYTES, LOAD);
456         }
457
458       next0 = DET44_IN2OUT_NEXT_LOOKUP;
459       next1 = DET44_IN2OUT_NEXT_LOOKUP;
460
461       ip0 = vlib_buffer_get_current (b0);
462       udp0 = ip4_next_header (ip0);
463       tcp0 = (tcp_header_t *) udp0;
464
465       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
466
467       if (PREDICT_FALSE (ip0->ttl == 1))
468         {
469           vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
470           icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
471                                        ICMP4_time_exceeded_ttl_exceeded_in_transit,
472                                        0);
473           next0 = DET44_IN2OUT_NEXT_ICMP_ERROR;
474           goto trace0;
475         }
476
477       proto0 = ip_proto_to_nat_proto (ip0->protocol);
478
479       if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
480         {
481           rx_fib_index0 =
482             ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
483           icmp0 = (icmp46_header_t *) udp0;
484
485           // TODO:
486           next0 = det44_icmp_in2out (b0, ip0, icmp0, sw_if_index0,
487                                      rx_fib_index0, node, next0,
488                                      thread_index, &ses0, &mp0);
489           goto trace0;
490         }
491
492       mp0 = snat_det_map_by_user (&ip0->src_address);
493       if (PREDICT_FALSE (!mp0))
494         {
495           det44_log_info ("no match for internal host %U",
496                           format_ip4_address, &ip0->src_address);
497           next0 = DET44_IN2OUT_NEXT_DROP;
498           b0->error = node->errors[DET44_IN2OUT_ERROR_NO_TRANSLATION];
499           goto trace0;
500         }
501
502       snat_det_forward (mp0, &ip0->src_address, &new_addr0, &lo_port0);
503
504       key0.ext_host_addr = ip0->dst_address;
505       key0.ext_host_port = tcp0->dst;
506
507       ses0 =
508         snat_det_find_ses_by_in (mp0, &ip0->src_address, tcp0->src, key0);
509       if (PREDICT_FALSE (!ses0))
510         {
511           for (i0 = 0; i0 < mp0->ports_per_host; i0++)
512             {
513               key0.out_port = clib_host_to_net_u16 (lo_port0 +
514                                                     ((i0 +
515                                                       clib_net_to_host_u16
516                                                       (tcp0->src)) %
517                                                      mp0->ports_per_host));
518
519               if (snat_det_get_ses_by_out
520                   (mp0, &ip0->src_address, key0.as_u64))
521                 continue;
522
523               ses0 =
524                 snat_det_ses_create (thread_index, mp0, &ip0->src_address,
525                                      tcp0->src, &key0);
526               break;
527             }
528           if (PREDICT_FALSE (!ses0))
529             {
530               /* too many sessions for user, send ICMP error packet */
531               vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
532               icmp4_error_set_vnet_buffer (b0,
533                                            ICMP4_destination_unreachable,
534                                            ICMP4_destination_unreachable_destination_unreachable_host,
535                                            0);
536               next0 = DET44_IN2OUT_NEXT_ICMP_ERROR;
537               goto trace0;
538             }
539         }
540
541       old_port0 = udp0->src_port;
542       udp0->src_port = new_port0 = ses0->out.out_port;
543
544       old_addr0.as_u32 = ip0->src_address.as_u32;
545       ip0->src_address.as_u32 = new_addr0.as_u32;
546       vnet_buffer (b0)->sw_if_index[VLIB_TX] = dm->outside_fib_index;
547
548       sum0 = ip0->checksum;
549       sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
550                              ip4_header_t, src_address /* changed member */ );
551       ip0->checksum = ip_csum_fold (sum0);
552
553       if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
554         {
555           if (tcp0->flags & TCP_FLAG_SYN)
556             ses0->state = DET44_SESSION_TCP_SYN_SENT;
557           else if (tcp0->flags & TCP_FLAG_ACK
558                    && ses0->state == DET44_SESSION_TCP_SYN_SENT)
559             ses0->state = DET44_SESSION_TCP_ESTABLISHED;
560           else if (tcp0->flags & TCP_FLAG_FIN
561                    && ses0->state == DET44_SESSION_TCP_ESTABLISHED)
562             ses0->state = DET44_SESSION_TCP_FIN_WAIT;
563           else if (tcp0->flags & TCP_FLAG_ACK
564                    && ses0->state == DET44_SESSION_TCP_FIN_WAIT)
565             snat_det_ses_close (mp0, ses0);
566           else if (tcp0->flags & TCP_FLAG_FIN
567                    && ses0->state == DET44_SESSION_TCP_CLOSE_WAIT)
568             ses0->state = DET44_SESSION_TCP_LAST_ACK;
569           else if (tcp0->flags == 0 && ses0->state == DET44_SESSION_UNKNOWN)
570             ses0->state = DET44_SESSION_TCP_ESTABLISHED;
571
572           sum0 = tcp0->checksum;
573           sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
574                                  ip4_header_t,
575                                  dst_address /* changed member */ );
576           sum0 = ip_csum_update (sum0, old_port0, new_port0,
577                                  ip4_header_t /* cheat */ ,
578                                  length /* changed member */ );
579           mss_clamping (dm->mss_clamping, tcp0, &sum0);
580           tcp0->checksum = ip_csum_fold (sum0);
581         }
582       else
583         {
584           ses0->state = DET44_SESSION_UDP_ACTIVE;
585
586           if (PREDICT_FALSE (udp0->checksum))
587             {
588               sum0 = udp0->checksum;
589               sum0 =
590                 ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
591                                 ip4_header_t,
592                                 dst_address /* changed member */ );
593               sum0 =
594                 ip_csum_update (sum0, old_port0, new_port0,
595                                 ip4_header_t /* cheat */ ,
596                                 length /* changed member */ );
597               udp0->checksum = ip_csum_fold (sum0);
598             }
599         }
600
601       switch (ses0->state)
602         {
603         case DET44_SESSION_UDP_ACTIVE:
604           ses0->expire = now + dm->timeouts.udp;
605           break;
606         case DET44_SESSION_TCP_SYN_SENT:
607         case DET44_SESSION_TCP_FIN_WAIT:
608         case DET44_SESSION_TCP_CLOSE_WAIT:
609         case DET44_SESSION_TCP_LAST_ACK:
610           ses0->expire = now + dm->timeouts.tcp.transitory;
611           break;
612         case DET44_SESSION_TCP_ESTABLISHED:
613           ses0->expire = now + dm->timeouts.tcp.established;
614           break;
615         }
616
617     trace0:
618       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
619                          && (b0->flags & VLIB_BUFFER_IS_TRACED)))
620         {
621           det44_in2out_trace_t *t =
622             vlib_add_trace (vm, node, b0, sizeof (*t));
623           t->sw_if_index = sw_if_index0;
624           t->next_index = next0;
625           t->session_index = ~0;
626           if (ses0)
627             t->session_index = ses0 - mp0->sessions;
628         }
629
630       pkts_processed += next0 != DET44_IN2OUT_NEXT_DROP;
631
632       ip1 = vlib_buffer_get_current (b1);
633       udp1 = ip4_next_header (ip1);
634       tcp1 = (tcp_header_t *) udp1;
635
636       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
637
638       if (PREDICT_FALSE (ip1->ttl == 1))
639         {
640           vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
641           icmp4_error_set_vnet_buffer (b1, ICMP4_time_exceeded,
642                                        ICMP4_time_exceeded_ttl_exceeded_in_transit,
643                                        0);
644           next1 = DET44_IN2OUT_NEXT_ICMP_ERROR;
645           goto trace1;
646         }
647
648       proto1 = ip_proto_to_nat_proto (ip1->protocol);
649
650       if (PREDICT_FALSE (proto1 == NAT_PROTOCOL_ICMP))
651         {
652           rx_fib_index1 =
653             ip4_fib_table_get_index_for_sw_if_index (sw_if_index1);
654           icmp1 = (icmp46_header_t *) udp1;
655
656           next1 = det44_icmp_in2out (b1, ip1, icmp1, sw_if_index1,
657                                      rx_fib_index1, node, next1,
658                                      thread_index, &ses1, &mp1);
659           goto trace1;
660         }
661
662       mp1 = snat_det_map_by_user (&ip1->src_address);
663       if (PREDICT_FALSE (!mp1))
664         {
665           det44_log_info ("no match for internal host %U",
666                           format_ip4_address, &ip0->src_address);
667           next1 = DET44_IN2OUT_NEXT_DROP;
668           b1->error = node->errors[DET44_IN2OUT_ERROR_NO_TRANSLATION];
669           goto trace1;
670         }
671
672       snat_det_forward (mp1, &ip1->src_address, &new_addr1, &lo_port1);
673
674       key1.ext_host_addr = ip1->dst_address;
675       key1.ext_host_port = tcp1->dst;
676
677       ses1 =
678         snat_det_find_ses_by_in (mp1, &ip1->src_address, tcp1->src, key1);
679       if (PREDICT_FALSE (!ses1))
680         {
681           for (i1 = 0; i1 < mp1->ports_per_host; i1++)
682             {
683               key1.out_port = clib_host_to_net_u16 (lo_port1 +
684                                                     ((i1 +
685                                                       clib_net_to_host_u16
686                                                       (tcp1->src)) %
687                                                      mp1->ports_per_host));
688
689               if (snat_det_get_ses_by_out
690                   (mp1, &ip1->src_address, key1.as_u64))
691                 continue;
692
693               ses1 =
694                 snat_det_ses_create (thread_index, mp1, &ip1->src_address,
695                                      tcp1->src, &key1);
696               break;
697             }
698           if (PREDICT_FALSE (!ses1))
699             {
700               /* too many sessions for user, send ICMP error packet */
701               vnet_buffer (b1)->sw_if_index[VLIB_TX] = (u32) ~ 0;
702               icmp4_error_set_vnet_buffer (b1,
703                                            ICMP4_destination_unreachable,
704                                            ICMP4_destination_unreachable_destination_unreachable_host,
705                                            0);
706               next1 = DET44_IN2OUT_NEXT_ICMP_ERROR;
707               goto trace1;
708             }
709         }
710
711       old_port1 = udp1->src_port;
712       udp1->src_port = new_port1 = ses1->out.out_port;
713
714       old_addr1.as_u32 = ip1->src_address.as_u32;
715       ip1->src_address.as_u32 = new_addr1.as_u32;
716       vnet_buffer (b1)->sw_if_index[VLIB_TX] = dm->outside_fib_index;
717
718       sum1 = ip1->checksum;
719       sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
720                              ip4_header_t, src_address /* changed member */ );
721       ip1->checksum = ip_csum_fold (sum1);
722
723       if (PREDICT_TRUE (proto1 == NAT_PROTOCOL_TCP))
724         {
725           if (tcp1->flags & TCP_FLAG_SYN)
726             ses1->state = DET44_SESSION_TCP_SYN_SENT;
727           else if (tcp1->flags & TCP_FLAG_ACK
728                    && ses1->state == DET44_SESSION_TCP_SYN_SENT)
729             ses1->state = DET44_SESSION_TCP_ESTABLISHED;
730           else if (tcp1->flags & TCP_FLAG_FIN
731                    && ses1->state == DET44_SESSION_TCP_ESTABLISHED)
732             ses1->state = DET44_SESSION_TCP_FIN_WAIT;
733           else if (tcp1->flags & TCP_FLAG_ACK
734                    && ses1->state == DET44_SESSION_TCP_FIN_WAIT)
735             snat_det_ses_close (mp1, ses1);
736           else if (tcp1->flags & TCP_FLAG_FIN
737                    && ses1->state == DET44_SESSION_TCP_CLOSE_WAIT)
738             ses1->state = DET44_SESSION_TCP_LAST_ACK;
739           else if (tcp1->flags == 0 && ses1->state == DET44_SESSION_UNKNOWN)
740             ses1->state = DET44_SESSION_TCP_ESTABLISHED;
741
742           sum1 = tcp1->checksum;
743           sum1 = ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
744                                  ip4_header_t,
745                                  dst_address /* changed member */ );
746           sum1 = ip_csum_update (sum1, old_port1, new_port1,
747                                  ip4_header_t /* cheat */ ,
748                                  length /* changed member */ );
749           mss_clamping (dm->mss_clamping, tcp1, &sum1);
750           tcp1->checksum = ip_csum_fold (sum1);
751         }
752       else
753         {
754           ses1->state = DET44_SESSION_UDP_ACTIVE;
755
756           if (PREDICT_FALSE (udp1->checksum))
757             {
758               sum1 = udp1->checksum;
759               sum1 =
760                 ip_csum_update (sum1, old_addr1.as_u32, new_addr1.as_u32,
761                                 ip4_header_t,
762                                 dst_address /* changed member */ );
763               sum1 =
764                 ip_csum_update (sum1, old_port1, new_port1,
765                                 ip4_header_t /* cheat */ ,
766                                 length /* changed member */ );
767               udp1->checksum = ip_csum_fold (sum1);
768             }
769         }
770
771       switch (ses1->state)
772         {
773         case DET44_SESSION_UDP_ACTIVE:
774           ses1->expire = now + dm->timeouts.udp;
775           break;
776         case DET44_SESSION_TCP_SYN_SENT:
777         case DET44_SESSION_TCP_FIN_WAIT:
778         case DET44_SESSION_TCP_CLOSE_WAIT:
779         case DET44_SESSION_TCP_LAST_ACK:
780           ses1->expire = now + dm->timeouts.tcp.transitory;
781           break;
782         case DET44_SESSION_TCP_ESTABLISHED:
783           ses1->expire = now + dm->timeouts.tcp.established;
784           break;
785         }
786
787     trace1:
788       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
789                          && (b1->flags & VLIB_BUFFER_IS_TRACED)))
790         {
791           det44_in2out_trace_t *t =
792             vlib_add_trace (vm, node, b1, sizeof (*t));
793           t->sw_if_index = sw_if_index1;
794           t->next_index = next1;
795           t->session_index = ~0;
796           if (ses1)
797             t->session_index = ses1 - mp1->sessions;
798         }
799
800       pkts_processed += next1 != DET44_IN2OUT_NEXT_DROP;
801
802       n_left_from -= 2;
803       next[0] = next0;
804       next[1] = next1;
805       next += 2;
806     }
807
808   while (n_left_from > 0)
809     {
810       vlib_buffer_t *b0;
811       u32 next0;
812       u32 sw_if_index0;
813       ip4_header_t *ip0;
814       ip_csum_t sum0;
815       ip4_address_t new_addr0, old_addr0;
816       u16 old_port0, new_port0, lo_port0, i0;
817       udp_header_t *udp0;
818       tcp_header_t *tcp0;
819       u32 proto0;
820       snat_det_out_key_t key0;
821       snat_det_map_t *mp0;
822       snat_det_session_t *ses0 = 0;
823       u32 rx_fib_index0;
824       icmp46_header_t *icmp0;
825
826       b0 = *b;
827       b++;
828       next0 = DET44_IN2OUT_NEXT_LOOKUP;
829
830       ip0 = vlib_buffer_get_current (b0);
831       udp0 = ip4_next_header (ip0);
832       tcp0 = (tcp_header_t *) udp0;
833
834       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
835
836       if (PREDICT_FALSE (ip0->ttl == 1))
837         {
838           vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
839           icmp4_error_set_vnet_buffer (b0, ICMP4_time_exceeded,
840                                        ICMP4_time_exceeded_ttl_exceeded_in_transit,
841                                        0);
842           next0 = DET44_IN2OUT_NEXT_ICMP_ERROR;
843           goto trace00;
844         }
845
846       proto0 = ip_proto_to_nat_proto (ip0->protocol);
847
848       if (PREDICT_FALSE (proto0 == NAT_PROTOCOL_ICMP))
849         {
850           rx_fib_index0 =
851             ip4_fib_table_get_index_for_sw_if_index (sw_if_index0);
852           icmp0 = (icmp46_header_t *) udp0;
853
854           next0 = det44_icmp_in2out (b0, ip0, icmp0, sw_if_index0,
855                                      rx_fib_index0, node, next0,
856                                      thread_index, &ses0, &mp0);
857           goto trace00;
858         }
859
860       mp0 = snat_det_map_by_user (&ip0->src_address);
861       if (PREDICT_FALSE (!mp0))
862         {
863           det44_log_info ("no match for internal host %U",
864                           format_ip4_address, &ip0->src_address);
865           next0 = DET44_IN2OUT_NEXT_DROP;
866           b0->error = node->errors[DET44_IN2OUT_ERROR_NO_TRANSLATION];
867           goto trace00;
868         }
869
870       snat_det_forward (mp0, &ip0->src_address, &new_addr0, &lo_port0);
871
872       key0.ext_host_addr = ip0->dst_address;
873       key0.ext_host_port = tcp0->dst;
874
875       ses0 =
876         snat_det_find_ses_by_in (mp0, &ip0->src_address, tcp0->src, key0);
877       if (PREDICT_FALSE (!ses0))
878         {
879           for (i0 = 0; i0 < mp0->ports_per_host; i0++)
880             {
881               key0.out_port = clib_host_to_net_u16 (lo_port0 +
882                                                     ((i0 +
883                                                       clib_net_to_host_u16
884                                                       (tcp0->src)) %
885                                                      mp0->ports_per_host));
886
887               if (snat_det_get_ses_by_out
888                   (mp0, &ip0->src_address, key0.as_u64))
889                 continue;
890
891               ses0 =
892                 snat_det_ses_create (thread_index, mp0, &ip0->src_address,
893                                      tcp0->src, &key0);
894               break;
895             }
896           if (PREDICT_FALSE (!ses0))
897             {
898               /* too many sessions for user, send ICMP error packet */
899               vnet_buffer (b0)->sw_if_index[VLIB_TX] = (u32) ~ 0;
900               icmp4_error_set_vnet_buffer (b0,
901                                            ICMP4_destination_unreachable,
902                                            ICMP4_destination_unreachable_destination_unreachable_host,
903                                            0);
904               next0 = DET44_IN2OUT_NEXT_ICMP_ERROR;
905               goto trace00;
906             }
907         }
908
909       old_port0 = udp0->src_port;
910       udp0->src_port = new_port0 = ses0->out.out_port;
911
912       old_addr0.as_u32 = ip0->src_address.as_u32;
913       ip0->src_address.as_u32 = new_addr0.as_u32;
914       vnet_buffer (b0)->sw_if_index[VLIB_TX] = dm->outside_fib_index;
915
916       sum0 = ip0->checksum;
917       sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
918                              ip4_header_t, src_address /* changed member */ );
919       ip0->checksum = ip_csum_fold (sum0);
920
921       if (PREDICT_TRUE (proto0 == NAT_PROTOCOL_TCP))
922         {
923           if (tcp0->flags & TCP_FLAG_SYN)
924             ses0->state = DET44_SESSION_TCP_SYN_SENT;
925           else if (tcp0->flags & TCP_FLAG_ACK
926                    && ses0->state == DET44_SESSION_TCP_SYN_SENT)
927             ses0->state = DET44_SESSION_TCP_ESTABLISHED;
928           else if (tcp0->flags & TCP_FLAG_FIN
929                    && ses0->state == DET44_SESSION_TCP_ESTABLISHED)
930             ses0->state = DET44_SESSION_TCP_FIN_WAIT;
931           else if (tcp0->flags & TCP_FLAG_ACK
932                    && ses0->state == DET44_SESSION_TCP_FIN_WAIT)
933             snat_det_ses_close (mp0, ses0);
934           else if (tcp0->flags & TCP_FLAG_FIN
935                    && ses0->state == DET44_SESSION_TCP_CLOSE_WAIT)
936             ses0->state = DET44_SESSION_TCP_LAST_ACK;
937           else if (tcp0->flags == 0 && ses0->state == DET44_SESSION_UNKNOWN)
938             ses0->state = DET44_SESSION_TCP_ESTABLISHED;
939
940           sum0 = tcp0->checksum;
941           sum0 = ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
942                                  ip4_header_t,
943                                  dst_address /* changed member */ );
944           sum0 = ip_csum_update (sum0, old_port0, new_port0,
945                                  ip4_header_t /* cheat */ ,
946                                  length /* changed member */ );
947           mss_clamping (dm->mss_clamping, tcp0, &sum0);
948           tcp0->checksum = ip_csum_fold (sum0);
949         }
950       else
951         {
952           ses0->state = DET44_SESSION_UDP_ACTIVE;
953
954           if (PREDICT_FALSE (udp0->checksum))
955             {
956               sum0 = udp0->checksum;
957               sum0 =
958                 ip_csum_update (sum0, old_addr0.as_u32, new_addr0.as_u32,
959                                 ip4_header_t,
960                                 dst_address /* changed member */ );
961               sum0 =
962                 ip_csum_update (sum0, old_port0, new_port0,
963                                 ip4_header_t /* cheat */ ,
964                                 length /* changed member */ );
965               udp0->checksum = ip_csum_fold (sum0);
966             }
967         }
968
969       switch (ses0->state)
970         {
971         case DET44_SESSION_UDP_ACTIVE:
972           ses0->expire = now + dm->timeouts.udp;
973           break;
974         case DET44_SESSION_TCP_SYN_SENT:
975         case DET44_SESSION_TCP_FIN_WAIT:
976         case DET44_SESSION_TCP_CLOSE_WAIT:
977         case DET44_SESSION_TCP_LAST_ACK:
978           ses0->expire = now + dm->timeouts.tcp.transitory;
979           break;
980         case DET44_SESSION_TCP_ESTABLISHED:
981           ses0->expire = now + dm->timeouts.tcp.established;
982           break;
983         }
984
985     trace00:
986       if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
987                          && (b0->flags & VLIB_BUFFER_IS_TRACED)))
988         {
989           det44_in2out_trace_t *t =
990             vlib_add_trace (vm, node, b0, sizeof (*t));
991           t->sw_if_index = sw_if_index0;
992           t->next_index = next0;
993           t->session_index = ~0;
994           if (ses0)
995             t->session_index = ses0 - mp0->sessions;
996         }
997
998       pkts_processed += next0 != DET44_IN2OUT_NEXT_DROP;
999
1000       n_left_from--;
1001       next[0] = next0;
1002       next++;
1003     }
1004
1005   vlib_buffer_enqueue_to_next (vm, node, from, (u16 *) nexts,
1006                                frame->n_vectors);
1007
1008   vlib_node_increment_counter (vm, dm->in2out_node_index,
1009                                DET44_IN2OUT_ERROR_IN2OUT_PACKETS,
1010                                pkts_processed);
1011   return frame->n_vectors;
1012 }
1013
1014 /* *INDENT-OFF* */
1015 VLIB_REGISTER_NODE (det44_in2out_node) = {
1016   .name = "det44-in2out",
1017   .vector_size = sizeof (u32),
1018   .format_trace = format_det44_in2out_trace,
1019   .type = VLIB_NODE_TYPE_INTERNAL,
1020   .n_errors = ARRAY_LEN(det44_in2out_error_strings),
1021   .error_strings = det44_in2out_error_strings,
1022   .runtime_data_bytes = sizeof (det44_runtime_t),
1023   .n_next_nodes = DET44_IN2OUT_N_NEXT,
1024   /* edit / add dispositions here */
1025   .next_nodes = {
1026     [DET44_IN2OUT_NEXT_DROP] = "error-drop",
1027     [DET44_IN2OUT_NEXT_LOOKUP] = "ip4-lookup",
1028     [DET44_IN2OUT_NEXT_ICMP_ERROR] = "ip4-icmp-error",
1029   },
1030 };
1031 /* *INDENT-ON* */
1032
1033 /*
1034  * fd.io coding-style-patch-verification: ON
1035  *
1036  * Local Variables:
1037  * eval: (c-set-style "gnu")
1038  * End:
1039  */