aa2bf73e94cd3c66d59811bd05cc43fe3600a346
[vpp.git] / src / plugins / nat / nat44_classify.c
1 /*
2  * Copyright (c) 2018 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 /**
16  * @file
17  * @brief Classify for one armed NAT44 (in+out interface)
18  */
19
20 #include <vlib/vlib.h>
21 #include <vnet/vnet.h>
22 #include <vnet/fib/ip4_fib.h>
23 #include <nat/nat.h>
24 #include <nat/nat_reass.h>
25 #include <nat/nat_inlines.h>
26
27 #define foreach_nat44_classify_error                      \
28 _(MAX_REASS, "Maximum reassemblies exceeded")             \
29 _(MAX_FRAG, "Maximum fragments per reassembly exceeded")  \
30 _(NEXT_IN2OUT, "next in2out")                             \
31 _(NEXT_OUT2IN, "next out2in")                             \
32 _(FRAG_CACHED, "fragment cached")
33
34 typedef enum
35 {
36 #define _(sym,str) NAT44_CLASSIFY_ERROR_##sym,
37   foreach_nat44_classify_error
38 #undef _
39     NAT44_CLASSIFY_N_ERROR,
40 } nat44_classify_error_t;
41
42 static char *nat44_classify_error_strings[] = {
43 #define _(sym,string) string,
44   foreach_nat44_classify_error
45 #undef _
46 };
47
48 typedef enum
49 {
50   NAT44_CLASSIFY_NEXT_IN2OUT,
51   NAT44_CLASSIFY_NEXT_OUT2IN,
52   NAT44_CLASSIFY_NEXT_DROP,
53   NAT44_CLASSIFY_N_NEXT,
54 } nat44_classify_next_t;
55
56 typedef struct
57 {
58   u8 next_in2out;
59   u8 cached;
60 } nat44_classify_trace_t;
61
62 static u8 *
63 format_nat44_classify_trace (u8 * s, va_list * args)
64 {
65   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
66   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
67   nat44_classify_trace_t *t = va_arg (*args, nat44_classify_trace_t *);
68   char *next;
69
70   if (t->cached)
71     s = format (s, "nat44-classify: fragment cached");
72   else
73     {
74       next = t->next_in2out ? "nat44-in2out" : "nat44-out2in";
75       s = format (s, "nat44-classify: next %s", next);
76     }
77
78   return s;
79 }
80
81 static inline uword
82 nat44_classify_node_fn_inline (vlib_main_t * vm,
83                                vlib_node_runtime_t * node,
84                                vlib_frame_t * frame, int is_ed)
85 {
86   u32 n_left_from, *from, *to_next;
87   nat44_classify_next_t next_index;
88   snat_main_t *sm = &snat_main;
89   snat_static_mapping_t *m;
90   u32 thread_index = vm->thread_index;
91   snat_main_per_thread_data_t *tsm = &sm->per_thread_data[thread_index];
92   u32 *fragments_to_drop = 0;
93   u32 *fragments_to_loopback = 0;
94   u32 next_in2out = 0, next_out2in = 0, frag_cached = 0;
95
96   from = vlib_frame_vector_args (frame);
97   n_left_from = frame->n_vectors;
98   next_index = node->cached_next_index;
99
100   while (n_left_from > 0)
101     {
102       u32 n_left_to_next;
103
104       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
105
106       while (n_left_from > 0 && n_left_to_next > 0)
107         {
108           u32 bi0;
109           vlib_buffer_t *b0;
110           u32 next0 = NAT44_CLASSIFY_NEXT_IN2OUT, sw_if_index0, rx_fib_index0;
111           ip4_header_t *ip0;
112           snat_address_t *ap;
113           snat_session_key_t m_key0;
114           clib_bihash_kv_8_8_t kv0, value0;
115           clib_bihash_kv_16_8_t ed_kv0, ed_value0;
116           udp_header_t *udp0;
117           nat_reass_ip4_t *reass0;
118           u8 cached0 = 0;
119
120           /* speculatively enqueue b0 to the current next frame */
121           bi0 = from[0];
122           to_next[0] = bi0;
123           from += 1;
124           to_next += 1;
125           n_left_from -= 1;
126           n_left_to_next -= 1;
127
128           b0 = vlib_get_buffer (vm, bi0);
129           ip0 = vlib_buffer_get_current (b0);
130           udp0 = ip4_next_header (ip0);
131
132           if (is_ed && ip0->protocol != IP_PROTOCOL_ICMP)
133             {
134               if (!ip4_is_fragment (ip0) || ip4_is_first_fragment (ip0))
135                 {
136                   /* process leading fragment/whole packet (with L4 header) */
137                   sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
138                   rx_fib_index0 =
139                     fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
140                                                          sw_if_index0);
141                   make_ed_kv (&ed_kv0, &ip0->src_address, &ip0->dst_address,
142                               ip0->protocol, rx_fib_index0, udp0->src_port,
143                               udp0->dst_port);
144                   if (ip4_is_fragment (ip0))
145                     {
146                       reass0 = nat_ip4_reass_find_or_create (ip0->src_address,
147                                                              ip0->dst_address,
148                                                              ip0->fragment_id,
149                                                              ip0->protocol,
150                                                              1,
151                                                              &fragments_to_drop);
152                       if (PREDICT_FALSE (!reass0))
153                         {
154                           next0 = NAT44_CLASSIFY_NEXT_DROP;
155                           b0->error =
156                             node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
157                           nat_elog_notice ("maximum reassemblies exceeded");
158                           goto enqueue0;
159                         }
160                       if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &ed_kv0,
161                                                     &ed_value0))
162                         {
163                           /* session exists so classify as IN2OUT,
164                            * save this information for future fragments and set
165                            * past fragments to be looped over and reprocessed */
166                           reass0->sess_index = ed_value0.value;
167                           reass0->classify_next =
168                             NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT;
169                           nat_ip4_reass_get_frags (reass0,
170                                                    &fragments_to_loopback);
171                           goto enqueue0;
172                         }
173                       else
174                         {
175                           /* session doesn't exist so continue in the code,
176                            * save this information for future fragments and set
177                            * past fragments to be looped over and reprocessed */
178                           reass0->flags |=
179                             NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE;
180                           nat_ip4_reass_get_frags (reass0,
181                                                    &fragments_to_loopback);
182                         }
183                     }
184                   else
185                     {
186                       /* process whole packet */
187                       if (!clib_bihash_search_16_8 (&tsm->in2out_ed, &ed_kv0,
188                                                     &ed_value0))
189                         goto enqueue0;
190                       /* session doesn't exist so continue in code */
191                     }
192                 }
193               else
194                 {
195                   /* process non-first fragment */
196                   reass0 = nat_ip4_reass_find_or_create (ip0->src_address,
197                                                          ip0->dst_address,
198                                                          ip0->fragment_id,
199                                                          ip0->protocol,
200                                                          1,
201                                                          &fragments_to_drop);
202                   if (PREDICT_FALSE (!reass0))
203                     {
204                       next0 = NAT44_CLASSIFY_NEXT_DROP;
205                       b0->error =
206                         node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
207                       nat_elog_notice ("maximum reassemblies exceeded");
208                       goto enqueue0;
209                     }
210                   /* check if first fragment has arrived */
211                   if (reass0->classify_next == NAT_REASS_IP4_CLASSIFY_NONE &&
212                       !(reass0->flags & NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE))
213                     {
214                       /* first fragment still hasn't arrived, cache this fragment */
215                       if (nat_ip4_reass_add_fragment
216                           (thread_index, reass0, bi0, &fragments_to_drop))
217                         {
218                           b0->error =
219                             node->errors[NAT44_CLASSIFY_ERROR_MAX_FRAG];
220                           nat_elog_notice
221                             ("maximum fragments per reassembly exceeded");
222                           next0 = NAT44_CLASSIFY_NEXT_DROP;
223                           goto enqueue0;
224                         }
225                       cached0 = 1;
226                       goto enqueue0;
227                     }
228                   if (reass0->classify_next ==
229                       NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT)
230                     goto enqueue0;
231                   /* flag NAT_REASS_FLAG_CLASSIFY_ED_CONTINUE is set
232                    * so keep the default next0 and continue in code to
233                    * potentially find other classification for this packet */
234                 }
235             }
236
237           /* *INDENT-OFF* */
238           vec_foreach (ap, sm->addresses)
239             {
240               if (ip0->dst_address.as_u32 == ap->addr.as_u32)
241                 {
242                   next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
243                   goto enqueue0;
244                 }
245             }
246           /* *INDENT-ON* */
247
248           if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
249             {
250               m_key0.addr = ip0->dst_address;
251               m_key0.port = 0;
252               m_key0.protocol = 0;
253               m_key0.fib_index = 0;
254               kv0.key = m_key0.as_u64;
255               /* try to classify the fragment based on IP header alone */
256               if (!clib_bihash_search_8_8 (&sm->static_mapping_by_external,
257                                            &kv0, &value0))
258                 {
259                   m = pool_elt_at_index (sm->static_mappings, value0.value);
260                   if (m->local_addr.as_u32 != m->external_addr.as_u32)
261                     next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
262                   goto enqueue0;
263                 }
264               if (!ip4_is_fragment (ip0) || ip4_is_first_fragment (ip0))
265                 {
266                   /* process leading fragment/whole packet (with L4 header) */
267                   m_key0.port = clib_net_to_host_u16 (udp0->dst_port);
268                   m_key0.protocol = ip_proto_to_snat_proto (ip0->protocol);
269                   kv0.key = m_key0.as_u64;
270                   if (!clib_bihash_search_8_8
271                       (&sm->static_mapping_by_external, &kv0, &value0))
272                     {
273                       m =
274                         pool_elt_at_index (sm->static_mappings, value0.value);
275                       if (m->local_addr.as_u32 != m->external_addr.as_u32)
276                         next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
277                     }
278                   if (ip4_is_fragment (ip0))
279                     {
280                       reass0 = nat_ip4_reass_find_or_create (ip0->src_address,
281                                                              ip0->dst_address,
282                                                              ip0->fragment_id,
283                                                              ip0->protocol,
284                                                              1,
285                                                              &fragments_to_drop);
286                       if (PREDICT_FALSE (!reass0))
287                         {
288                           next0 = NAT44_CLASSIFY_NEXT_DROP;
289                           b0->error =
290                             node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
291                           nat_elog_notice ("maximum reassemblies exceeded");
292                           goto enqueue0;
293                         }
294                       /* save classification for future fragments and set past
295                        * fragments to be looped over and reprocessed */
296                       if (next0 == NAT44_CLASSIFY_NEXT_OUT2IN)
297                         reass0->classify_next =
298                           NAT_REASS_IP4_CLASSIFY_NEXT_OUT2IN;
299                       else
300                         reass0->classify_next =
301                           NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT;
302                       nat_ip4_reass_get_frags (reass0,
303                                                &fragments_to_loopback);
304                     }
305                 }
306               else
307                 {
308                   /* process non-first fragment */
309                   reass0 = nat_ip4_reass_find_or_create (ip0->src_address,
310                                                          ip0->dst_address,
311                                                          ip0->fragment_id,
312                                                          ip0->protocol,
313                                                          1,
314                                                          &fragments_to_drop);
315                   if (PREDICT_FALSE (!reass0))
316                     {
317                       next0 = NAT44_CLASSIFY_NEXT_DROP;
318                       b0->error =
319                         node->errors[NAT44_CLASSIFY_ERROR_MAX_REASS];
320                       nat_elog_notice ("maximum reassemblies exceeded");
321                       goto enqueue0;
322                     }
323                   if (reass0->classify_next == NAT_REASS_IP4_CLASSIFY_NONE)
324                     /* first fragment still hasn't arrived */
325                     {
326                       if (nat_ip4_reass_add_fragment
327                           (thread_index, reass0, bi0, &fragments_to_drop))
328                         {
329                           b0->error =
330                             node->errors[NAT44_CLASSIFY_ERROR_MAX_FRAG];
331                           nat_elog_notice
332                             ("maximum fragments per reassembly exceeded");
333                           next0 = NAT44_CLASSIFY_NEXT_DROP;
334                           goto enqueue0;
335                         }
336                       cached0 = 1;
337                       goto enqueue0;
338                     }
339                   else if (reass0->classify_next ==
340                            NAT_REASS_IP4_CLASSIFY_NEXT_OUT2IN)
341                     next0 = NAT44_CLASSIFY_NEXT_OUT2IN;
342                   else if (reass0->classify_next ==
343                            NAT_REASS_IP4_CLASSIFY_NEXT_IN2OUT)
344                     next0 = NAT44_CLASSIFY_NEXT_IN2OUT;
345                 }
346             }
347
348         enqueue0:
349           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
350                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
351             {
352               nat44_classify_trace_t *t =
353                 vlib_add_trace (vm, node, b0, sizeof (*t));
354               t->cached = cached0;
355               if (!cached0)
356                 t->next_in2out = next0 == NAT44_CLASSIFY_NEXT_IN2OUT ? 1 : 0;
357             }
358
359           if (cached0)
360             {
361               n_left_to_next++;
362               to_next--;
363               frag_cached++;
364             }
365           else
366             {
367               next_in2out += next0 == NAT44_CLASSIFY_NEXT_IN2OUT;
368               next_out2in += next0 == NAT44_CLASSIFY_NEXT_OUT2IN;
369
370               /* verify speculative enqueue, maybe switch current next frame */
371               vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
372                                                to_next, n_left_to_next,
373                                                bi0, next0);
374             }
375
376           if (n_left_from == 0 && vec_len (fragments_to_loopback))
377             {
378               from = vlib_frame_vector_args (frame);
379               u32 len = vec_len (fragments_to_loopback);
380               if (len <= VLIB_FRAME_SIZE)
381                 {
382                   clib_memcpy_fast (from, fragments_to_loopback,
383                                     sizeof (u32) * len);
384                   n_left_from = len;
385                   vec_reset_length (fragments_to_loopback);
386                 }
387               else
388                 {
389                   clib_memcpy_fast (from, fragments_to_loopback +
390                                     (len - VLIB_FRAME_SIZE),
391                                     sizeof (u32) * VLIB_FRAME_SIZE);
392                   n_left_from = VLIB_FRAME_SIZE;
393                   _vec_len (fragments_to_loopback) = len - VLIB_FRAME_SIZE;
394                 }
395             }
396         }
397
398       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
399     }
400
401   nat_send_all_to_node (vm, fragments_to_drop, node, 0,
402                         NAT44_CLASSIFY_NEXT_DROP);
403
404   vec_free (fragments_to_drop);
405
406   vlib_node_increment_counter (vm, node->node_index,
407                                NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out);
408   vlib_node_increment_counter (vm, node->node_index,
409                                NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in);
410   vlib_node_increment_counter (vm, node->node_index,
411                                NAT44_CLASSIFY_ERROR_FRAG_CACHED, frag_cached);
412
413   return frame->n_vectors;
414 }
415
416 VLIB_NODE_FN (nat44_classify_node) (vlib_main_t * vm,
417                                     vlib_node_runtime_t * node,
418                                     vlib_frame_t * frame)
419 {
420   return nat44_classify_node_fn_inline (vm, node, frame, 0);
421 }
422
423 /* *INDENT-OFF* */
424 VLIB_REGISTER_NODE (nat44_classify_node) = {
425   .name = "nat44-classify",
426   .vector_size = sizeof (u32),
427   .format_trace = format_nat44_classify_trace,
428   .type = VLIB_NODE_TYPE_INTERNAL,
429   .n_errors = ARRAY_LEN(nat44_classify_error_strings),
430   .error_strings = nat44_classify_error_strings,
431   .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
432   .next_nodes = {
433     [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out",
434     [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in",
435     [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
436   },
437 };
438 /* *INDENT-ON* */
439
440 VLIB_NODE_FN (nat44_ed_classify_node) (vlib_main_t * vm,
441                                        vlib_node_runtime_t * node,
442                                        vlib_frame_t * frame)
443 {
444   return nat44_classify_node_fn_inline (vm, node, frame, 1);
445 }
446
447 /* *INDENT-OFF* */
448 VLIB_REGISTER_NODE (nat44_ed_classify_node) = {
449   .name = "nat44-ed-classify",
450   .vector_size = sizeof (u32),
451   .format_trace = format_nat44_classify_trace,
452   .type = VLIB_NODE_TYPE_INTERNAL,
453   .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
454   .next_nodes = {
455     [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-ed-in2out",
456     [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-ed-out2in",
457     [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
458   },
459 };
460 /* *INDENT-ON* */
461
462 VLIB_NODE_FN (nat44_det_classify_node) (vlib_main_t * vm,
463                                         vlib_node_runtime_t * node,
464                                         vlib_frame_t * frame)
465 {
466   return nat44_classify_node_fn_inline (vm, node, frame, 0);
467 }
468
469 /* *INDENT-OFF* */
470 VLIB_REGISTER_NODE (nat44_det_classify_node) = {
471   .name = "nat44-det-classify",
472   .vector_size = sizeof (u32),
473   .format_trace = format_nat44_classify_trace,
474   .type = VLIB_NODE_TYPE_INTERNAL,
475   .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
476   .next_nodes = {
477     [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-det-in2out",
478     [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-det-out2in",
479     [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
480   },
481 };
482 /* *INDENT-ON* */
483
484 VLIB_NODE_FN (nat44_handoff_classify_node) (vlib_main_t * vm,
485                                             vlib_node_runtime_t * node,
486                                             vlib_frame_t * frame)
487 {
488   return nat44_classify_node_fn_inline (vm, node, frame, 0);
489 }
490
491 /* *INDENT-OFF* */
492 VLIB_REGISTER_NODE (nat44_handoff_classify_node) = {
493   .name = "nat44-handoff-classify",
494   .vector_size = sizeof (u32),
495   .format_trace = format_nat44_classify_trace,
496   .type = VLIB_NODE_TYPE_INTERNAL,
497   .n_next_nodes = NAT44_CLASSIFY_N_NEXT,
498   .next_nodes = {
499     [NAT44_CLASSIFY_NEXT_IN2OUT] = "nat44-in2out-worker-handoff",
500     [NAT44_CLASSIFY_NEXT_OUT2IN] = "nat44-out2in-worker-handoff",
501     [NAT44_CLASSIFY_NEXT_DROP] = "error-drop",
502   },
503 };
504
505 /* *INDENT-ON* */
506
507 /*
508  * fd.io coding-style-patch-verification: ON
509  *
510  * Local Variables:
511  * eval: (c-set-style "gnu")
512  * End:
513  */