nat: static mappings in flow hash
[vpp.git] / src / plugins / nat / nat44-ed / nat44_ed_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
24 #include <nat/nat44-ed/nat44_ed.h>
25 #include <nat/nat44-ed/nat44_ed_inlines.h>
26
27 #define foreach_nat44_classify_error                      \
28 _(NEXT_IN2OUT, "next in2out")                             \
29 _(NEXT_OUT2IN, "next out2in")                             \
30 _(FRAG_CACHED, "fragment cached")
31
32 typedef enum
33 {
34 #define _(sym,str) NAT44_CLASSIFY_ERROR_##sym,
35   foreach_nat44_classify_error
36 #undef _
37     NAT44_CLASSIFY_N_ERROR,
38 } nat44_classify_error_t;
39
40 typedef enum
41 {
42   NAT44_CLASSIFY_NEXT_IN2OUT,
43   NAT44_CLASSIFY_NEXT_OUT2IN,
44   NAT44_CLASSIFY_NEXT_DROP,
45   NAT44_CLASSIFY_N_NEXT,
46 } nat44_classify_next_t;
47
48 typedef struct
49 {
50   u8 next_in2out;
51   u8 cached;
52 } nat44_classify_trace_t;
53
54 static u8 *
55 format_nat44_classify_trace (u8 * s, va_list * args)
56 {
57   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
58   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
59   nat44_classify_trace_t *t = va_arg (*args, nat44_classify_trace_t *);
60   char *next;
61
62   if (t->cached)
63     s = format (s, "nat44-classify: fragment cached");
64   else
65     {
66       next = t->next_in2out ? "nat44-ed-in2out" : "nat44-ed-out2in";
67       s = format (s, "nat44-classify: next %s", next);
68     }
69
70   return s;
71 }
72
73 static inline uword
74 nat44_handoff_classify_node_fn_inline (vlib_main_t * vm,
75                                        vlib_node_runtime_t * node,
76                                        vlib_frame_t * frame)
77 {
78   u32 n_left_from, *from, *to_next;
79   nat44_classify_next_t next_index;
80   snat_main_t *sm = &snat_main;
81   snat_static_mapping_t *m;
82   u32 next_in2out = 0, next_out2in = 0;
83
84   from = vlib_frame_vector_args (frame);
85   n_left_from = frame->n_vectors;
86   next_index = node->cached_next_index;
87
88   while (n_left_from > 0)
89     {
90       u32 n_left_to_next;
91
92       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
93
94       while (n_left_from > 0 && n_left_to_next > 0)
95         {
96           u32 bi0;
97           vlib_buffer_t *b0;
98           u32 next0 = NAT_NEXT_IN2OUT_CLASSIFY;
99           ip4_header_t *ip0;
100           snat_address_t *ap;
101
102           /* speculatively enqueue b0 to the current next frame */
103           bi0 = from[0];
104           to_next[0] = bi0;
105           from += 1;
106           to_next += 1;
107           n_left_from -= 1;
108           n_left_to_next -= 1;
109
110           b0 = vlib_get_buffer (vm, bi0);
111           ip0 = vlib_buffer_get_current (b0);
112
113           vec_foreach (ap, sm->addresses)
114             {
115               if (ip0->dst_address.as_u32 == ap->addr.as_u32)
116                 {
117                   next0 = NAT_NEXT_OUT2IN_CLASSIFY;
118                   goto enqueue0;
119                 }
120             }
121
122           if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
123             {
124               /* try to classify the fragment based on IP header alone */
125               m = nat44_ed_sm_o2i_lookup (sm, ip0->dst_address, 0, 0, 0);
126               if (m)
127                 {
128                   if (m->local_addr.as_u32 != m->external_addr.as_u32)
129                     next0 = NAT_NEXT_OUT2IN_CLASSIFY;
130                   goto enqueue0;
131                 }
132               m = nat44_ed_sm_o2i_lookup (
133                 sm, ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port,
134                 0, ip0->protocol);
135               if (m)
136                 {
137                   if (m->local_addr.as_u32 != m->external_addr.as_u32)
138                     next0 = NAT_NEXT_OUT2IN_CLASSIFY;
139                 }
140             }
141
142         enqueue0:
143           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
144                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
145             {
146               nat44_classify_trace_t *t =
147                 vlib_add_trace (vm, node, b0, sizeof (*t));
148               t->cached = 0;
149               t->next_in2out = next0 == NAT_NEXT_IN2OUT_CLASSIFY ? 1 : 0;
150             }
151
152           next_in2out += next0 == NAT_NEXT_IN2OUT_CLASSIFY;
153           next_out2in += next0 == NAT_NEXT_OUT2IN_CLASSIFY;
154
155           /* verify speculative enqueue, maybe switch current next frame */
156           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
157                                            to_next, n_left_to_next,
158                                            bi0, next0);
159         }
160
161       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
162     }
163
164   vlib_node_increment_counter (vm, node->node_index,
165                                NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out);
166   vlib_node_increment_counter (vm, node->node_index,
167                                NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in);
168   return frame->n_vectors;
169 }
170
171 static inline uword
172 nat44_ed_classify_node_fn_inline (vlib_main_t * vm,
173                                   vlib_node_runtime_t * node,
174                                   vlib_frame_t * frame)
175 {
176   u32 n_left_from, *from, *to_next;
177   nat44_classify_next_t next_index;
178   snat_main_t *sm = &snat_main;
179   snat_static_mapping_t *m;
180   u32 next_in2out = 0, next_out2in = 0;
181
182   from = vlib_frame_vector_args (frame);
183   n_left_from = frame->n_vectors;
184   next_index = node->cached_next_index;
185
186   while (n_left_from > 0)
187     {
188       u32 n_left_to_next;
189
190       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
191
192       while (n_left_from > 0 && n_left_to_next > 0)
193         {
194           u32 bi0;
195           vlib_buffer_t *b0;
196           u32 next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
197           u32 sw_if_index0, rx_fib_index0;
198           ip4_header_t *ip0;
199           snat_address_t *ap;
200           clib_bihash_kv_16_8_t ed_kv0, ed_value0;
201
202           /* speculatively enqueue b0 to the current next frame */
203           bi0 = from[0];
204           to_next[0] = bi0;
205           from += 1;
206           to_next += 1;
207           n_left_from -= 1;
208           n_left_to_next -= 1;
209
210           b0 = vlib_get_buffer (vm, bi0);
211           ip0 = vlib_buffer_get_current (b0);
212
213           u32 arc_next;
214           vnet_feature_next (&arc_next, b0);
215           vnet_buffer2 (b0)->nat.arc_next = arc_next;
216
217           if (ip0->protocol != IP_PROTOCOL_ICMP)
218             {
219               /* process leading fragment/whole packet (with L4 header) */
220               sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
221               rx_fib_index0 =
222                 fib_table_get_index_for_sw_if_index (FIB_PROTOCOL_IP4,
223                                                      sw_if_index0);
224               init_ed_k (&ed_kv0, ip0->src_address.as_u32,
225                          vnet_buffer (b0)->ip.reass.l4_src_port,
226                          ip0->dst_address.as_u32,
227                          vnet_buffer (b0)->ip.reass.l4_dst_port, rx_fib_index0,
228                          ip0->protocol);
229               /* process whole packet */
230               if (!clib_bihash_search_16_8 (&sm->flow_hash, &ed_kv0,
231                                             &ed_value0))
232                 {
233                   ASSERT (vm->thread_index ==
234                           ed_value_get_thread_index (&ed_value0));
235                   snat_main_per_thread_data_t *tsm =
236                     &sm->per_thread_data[vm->thread_index];
237                   snat_session_t *s = pool_elt_at_index (
238                     tsm->sessions, ed_value_get_session_index (&ed_value0));
239                   clib_bihash_kv_16_8_t i2o_kv;
240                   nat_6t_flow_to_ed_k (&i2o_kv, &s->i2o);
241                   vnet_buffer2 (b0)->nat.cached_session_index =
242                     ed_value_get_session_index (&ed_value0);
243                   if (i2o_kv.key[0] == ed_kv0.key[0] &&
244                       i2o_kv.key[1] == ed_kv0.key[1])
245                     {
246                       next0 = NAT_NEXT_IN2OUT_ED_FAST_PATH;
247                     }
248                   else
249                     {
250                       next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
251                     }
252
253                   goto enqueue0;
254                 }
255               /* session doesn't exist so continue in code */
256             }
257
258           vec_foreach (ap, sm->addresses)
259             {
260               if (ip0->dst_address.as_u32 == ap->addr.as_u32)
261                 {
262                   next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
263                   goto enqueue0;
264                 }
265             }
266
267           if (PREDICT_FALSE (pool_elts (sm->static_mappings)))
268             {
269               /* try to classify the fragment based on IP header alone */
270               m = nat44_ed_sm_o2i_lookup (sm, ip0->dst_address, 0, 0, 0);
271               if (m)
272                 {
273                   if (m->local_addr.as_u32 != m->external_addr.as_u32)
274                     next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
275                   goto enqueue0;
276                 }
277               m = nat44_ed_sm_o2i_lookup (
278                 sm, ip0->dst_address, vnet_buffer (b0)->ip.reass.l4_dst_port,
279                 0, ip0->protocol);
280               if (m)
281                 {
282                   if (m->local_addr.as_u32 != m->external_addr.as_u32)
283                     next0 = NAT_NEXT_OUT2IN_ED_FAST_PATH;
284                 }
285             }
286
287         enqueue0:
288           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
289                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
290             {
291               nat44_classify_trace_t *t =
292                 vlib_add_trace (vm, node, b0, sizeof (*t));
293               t->cached = 0;
294               t->next_in2out = next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH ? 1 : 0;
295             }
296
297           next_in2out += next0 == NAT_NEXT_IN2OUT_ED_FAST_PATH;
298           next_out2in += next0 == NAT_NEXT_OUT2IN_ED_FAST_PATH;
299
300           /* verify speculative enqueue, maybe switch current next frame */
301           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
302                                            to_next, n_left_to_next,
303                                            bi0, next0);
304         }
305
306       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
307     }
308
309   vlib_node_increment_counter (vm, node->node_index,
310                                NAT44_CLASSIFY_ERROR_NEXT_IN2OUT, next_in2out);
311   vlib_node_increment_counter (vm, node->node_index,
312                                NAT44_CLASSIFY_ERROR_NEXT_OUT2IN, next_out2in);
313   return frame->n_vectors;
314 }
315
316 VLIB_NODE_FN (nat44_ed_classify_node) (vlib_main_t * vm,
317                                        vlib_node_runtime_t * node,
318                                        vlib_frame_t * frame)
319 {
320   return nat44_ed_classify_node_fn_inline (vm, node, frame);
321 }
322
323 VLIB_REGISTER_NODE (nat44_ed_classify_node) = {
324   .name = "nat44-ed-classify",
325   .vector_size = sizeof (u32),
326   .sibling_of = "nat-default",
327   .format_trace = format_nat44_classify_trace,
328   .type = VLIB_NODE_TYPE_INTERNAL,
329 };
330
331 VLIB_NODE_FN (nat44_handoff_classify_node) (vlib_main_t * vm,
332                                             vlib_node_runtime_t * node,
333                                             vlib_frame_t * frame)
334 {
335   return nat44_handoff_classify_node_fn_inline (vm, node, frame);
336 }
337
338 VLIB_REGISTER_NODE (nat44_handoff_classify_node) = {
339   .name = "nat44-handoff-classify",
340   .vector_size = sizeof (u32),
341   .sibling_of = "nat-default",
342   .format_trace = format_nat44_classify_trace,
343   .type = VLIB_NODE_TYPE_INTERNAL,
344 };
345
346 /*
347  * fd.io coding-style-patch-verification: ON
348  *
349  * Local Variables:
350  * eval: (c-set-style "gnu")
351  * End:
352  */