ipsec: IPSec protection for multi-point tunnel interfaces
[vpp.git] / src / vnet / classify / flow_classify_node.c
1 /*
2  * Copyright (c) 2016 Cisco and/or its affiliates.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at:
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include <stdint.h>
17
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/ip/ip.h>
21 #include <vnet/classify/flow_classify.h>
22 #include <vnet/classify/vnet_classify.h>
23
24 typedef struct
25 {
26   u32 sw_if_index;
27   u32 next_index;
28   u32 table_index;
29   u32 offset;
30 } flow_classify_trace_t;
31
32 static u8 *
33 format_flow_classify_trace (u8 * s, va_list * args)
34 {
35   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
36   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
37   flow_classify_trace_t *t = va_arg (*args, flow_classify_trace_t *);
38
39   s = format (s, "FLOW_CLASSIFY: sw_if_index %d next %d table %d offset %d",
40               t->sw_if_index, t->next_index, t->table_index, t->offset);
41   return s;
42 }
43
44 #define foreach_flow_classify_error                 \
45 _(MISS, "Flow classify misses")                     \
46 _(HIT, "Flow classify hits")                        \
47 _(CHAIN_HIT, "Flow classify hits after chain walk") \
48 _(DROP, "Flow classify action drop")
49
50 typedef enum
51 {
52 #define _(sym,str) FLOW_CLASSIFY_ERROR_##sym,
53   foreach_flow_classify_error
54 #undef _
55     FLOW_CLASSIFY_N_ERROR,
56 } flow_classify_error_t;
57
58 static char *flow_classify_error_strings[] = {
59 #define _(sym,string) string,
60   foreach_flow_classify_error
61 #undef _
62 };
63
64 static inline uword
65 flow_classify_inline (vlib_main_t * vm,
66                       vlib_node_runtime_t * node,
67                       vlib_frame_t * frame, flow_classify_table_id_t tid)
68 {
69   u32 n_left_from, *from, *to_next;
70   flow_classify_next_index_t next_index;
71   flow_classify_main_t *fcm = &flow_classify_main;
72   vnet_classify_main_t *vcm = fcm->vnet_classify_main;
73   f64 now = vlib_time_now (vm);
74   u32 hits = 0;
75   u32 misses = 0;
76   u32 chain_hits = 0;
77   u32 drop = 0;
78
79   from = vlib_frame_vector_args (frame);
80   n_left_from = frame->n_vectors;
81
82   /* First pass: compute hashes */
83   while (n_left_from > 2)
84     {
85       vlib_buffer_t *b0, *b1;
86       u32 bi0, bi1;
87       u8 *h0, *h1;
88       u32 sw_if_index0, sw_if_index1;
89       u32 table_index0, table_index1;
90       vnet_classify_table_t *t0, *t1;
91
92       /* Prefetch next iteration */
93       {
94         vlib_buffer_t *p1, *p2;
95
96         p1 = vlib_get_buffer (vm, from[1]);
97         p2 = vlib_get_buffer (vm, from[2]);
98
99         vlib_prefetch_buffer_header (p1, STORE);
100         CLIB_PREFETCH (p1->data, CLIB_CACHE_LINE_BYTES, STORE);
101         vlib_prefetch_buffer_header (p2, STORE);
102         CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
103       }
104
105       bi0 = from[0];
106       b0 = vlib_get_buffer (vm, bi0);
107       h0 = b0->data;
108
109       bi1 = from[1];
110       b1 = vlib_get_buffer (vm, bi1);
111       h1 = b1->data;
112
113       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
114       table_index0 =
115         fcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
116
117       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
118       table_index1 =
119         fcm->classify_table_index_by_sw_if_index[tid][sw_if_index1];
120
121       t0 = pool_elt_at_index (vcm->tables, table_index0);
122
123       t1 = pool_elt_at_index (vcm->tables, table_index1);
124
125       vnet_buffer (b0)->l2_classify.hash = vnet_classify_hash_packet (t0, h0);
126
127       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
128
129       vnet_buffer (b1)->l2_classify.hash = vnet_classify_hash_packet (t1, h1);
130
131       vnet_classify_prefetch_bucket (t1, vnet_buffer (b1)->l2_classify.hash);
132
133       vnet_buffer (b0)->l2_classify.table_index = table_index0;
134
135       vnet_buffer (b1)->l2_classify.table_index = table_index1;
136
137       from += 2;
138       n_left_from -= 2;
139     }
140
141   while (n_left_from > 0)
142     {
143       vlib_buffer_t *b0;
144       u32 bi0;
145       u8 *h0;
146       u32 sw_if_index0;
147       u32 table_index0;
148       vnet_classify_table_t *t0;
149
150       bi0 = from[0];
151       b0 = vlib_get_buffer (vm, bi0);
152       h0 = b0->data;
153
154       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
155       table_index0 =
156         fcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
157
158       t0 = pool_elt_at_index (vcm->tables, table_index0);
159       vnet_buffer (b0)->l2_classify.hash = vnet_classify_hash_packet (t0, h0);
160
161       vnet_buffer (b0)->l2_classify.table_index = table_index0;
162       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
163
164       from++;
165       n_left_from--;
166     }
167
168   next_index = node->cached_next_index;
169   from = vlib_frame_vector_args (frame);
170   n_left_from = frame->n_vectors;
171
172   while (n_left_from > 0)
173     {
174       u32 n_left_to_next;
175
176       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
177
178       /* Not enough load/store slots to dual loop... */
179       while (n_left_from > 0 && n_left_to_next > 0)
180         {
181           u32 bi0;
182           vlib_buffer_t *b0;
183           u32 next0 = FLOW_CLASSIFY_NEXT_INDEX_DROP;
184           u32 table_index0;
185           vnet_classify_table_t *t0;
186           vnet_classify_entry_t *e0;
187           u64 hash0;
188           u8 *h0;
189
190           /* Stride 3 seems to work best */
191           if (PREDICT_TRUE (n_left_from > 3))
192             {
193               vlib_buffer_t *p1 = vlib_get_buffer (vm, from[3]);
194               vnet_classify_table_t *tp1;
195               u32 table_index1;
196               u64 phash1;
197
198               table_index1 = vnet_buffer (p1)->l2_classify.table_index;
199
200               if (PREDICT_TRUE (table_index1 != ~0))
201                 {
202                   tp1 = pool_elt_at_index (vcm->tables, table_index1);
203                   phash1 = vnet_buffer (p1)->l2_classify.hash;
204                   vnet_classify_prefetch_entry (tp1, phash1);
205                 }
206             }
207
208           /* Speculatively enqueue b0 to the current next frame */
209           bi0 = from[0];
210           to_next[0] = bi0;
211           from += 1;
212           to_next += 1;
213           n_left_from -= 1;
214           n_left_to_next -= 1;
215
216           b0 = vlib_get_buffer (vm, bi0);
217           h0 = b0->data;
218           table_index0 = vnet_buffer (b0)->l2_classify.table_index;
219           e0 = 0;
220           t0 = 0;
221
222           vnet_get_config_data (fcm->vnet_config_main[tid],
223                                 &b0->current_config_index, &next0,
224                                 /* # bytes of config data */ 0);
225
226           if (PREDICT_TRUE (table_index0 != ~0))
227             {
228               hash0 = vnet_buffer (b0)->l2_classify.hash;
229               t0 = pool_elt_at_index (vcm->tables, table_index0);
230               e0 = vnet_classify_find_entry (t0, h0, hash0, now);
231               if (e0)
232                 {
233                   hits++;
234                 }
235               else
236                 {
237                   misses++;
238                   vnet_classify_add_del_session (vcm, table_index0,
239                                                  h0, ~0, 0, 0, 0, 0, 1);
240                   /* increment counter */
241                   vnet_classify_find_entry (t0, h0, hash0, now);
242                 }
243             }
244           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
245                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
246             {
247               flow_classify_trace_t *t =
248                 vlib_add_trace (vm, node, b0, sizeof (*t));
249               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
250               t->next_index = next0;
251               t->table_index = t0 ? t0 - vcm->tables : ~0;
252               t->offset = (t0 && e0) ? vnet_classify_get_offset (t0, e0) : ~0;
253             }
254
255           /* Verify speculative enqueue, maybe switch current next frame */
256           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
257                                            n_left_to_next, bi0, next0);
258         }
259
260       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
261     }
262
263   vlib_node_increment_counter (vm, node->node_index,
264                                FLOW_CLASSIFY_ERROR_MISS, misses);
265   vlib_node_increment_counter (vm, node->node_index,
266                                FLOW_CLASSIFY_ERROR_HIT, hits);
267   vlib_node_increment_counter (vm, node->node_index,
268                                FLOW_CLASSIFY_ERROR_CHAIN_HIT, chain_hits);
269   vlib_node_increment_counter (vm, node->node_index,
270                                FLOW_CLASSIFY_ERROR_DROP, drop);
271
272   return frame->n_vectors;
273 }
274
275 VLIB_NODE_FN (ip4_flow_classify_node) (vlib_main_t * vm,
276                                        vlib_node_runtime_t * node,
277                                        vlib_frame_t * frame)
278 {
279   return flow_classify_inline (vm, node, frame, FLOW_CLASSIFY_TABLE_IP4);
280 }
281
282 /* *INDENT-OFF* */
283 VLIB_REGISTER_NODE (ip4_flow_classify_node) = {
284   .name = "ip4-flow-classify",
285   .vector_size = sizeof (u32),
286   .format_trace = format_flow_classify_trace,
287   .n_errors = ARRAY_LEN(flow_classify_error_strings),
288   .error_strings = flow_classify_error_strings,
289   .n_next_nodes = FLOW_CLASSIFY_NEXT_INDEX_N_NEXT,
290   .next_nodes = {
291     [FLOW_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
292   },
293 };
294 /* *INDENT-ON* */
295
296 VLIB_NODE_FN (ip6_flow_classify_node) (vlib_main_t * vm,
297                                        vlib_node_runtime_t * node,
298                                        vlib_frame_t * frame)
299 {
300   return flow_classify_inline (vm, node, frame, FLOW_CLASSIFY_TABLE_IP6);
301 }
302
303 /* *INDENT-OFF* */
304 VLIB_REGISTER_NODE (ip6_flow_classify_node) = {
305   .name = "ip6-flow-classify",
306   .vector_size = sizeof (u32),
307   .format_trace = format_flow_classify_trace,
308   .n_errors = ARRAY_LEN(flow_classify_error_strings),
309   .error_strings = flow_classify_error_strings,
310   .n_next_nodes = FLOW_CLASSIFY_NEXT_INDEX_N_NEXT,
311   .next_nodes = {
312     [FLOW_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
313   },
314 };
315
316 /* *INDENT-ON* */
317
318
319 static clib_error_t *
320 flow_classify_init (vlib_main_t * vm)
321 {
322   flow_classify_main_t *fcm = &flow_classify_main;
323
324   fcm->vlib_main = vm;
325   fcm->vnet_main = vnet_get_main ();
326   fcm->vnet_classify_main = &vnet_classify_main;
327
328   return 0;
329 }
330
331 VLIB_INIT_FUNCTION (flow_classify_init);
332
333 /*
334  * fd.io coding-style-patch-verification: ON
335  *
336  * Local Variables:
337  * eval: (c-set-style "gnu")
338  * End:
339  */