VPP-360 Allow L2 classify to use dynamic graph arcs
[vpp.git] / vnet / vnet / l2 / l2_classify.c
1 /*
2  * Copyright (c) 2015 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  * l2_classify.c
17  */
18
19 #include <vnet/l2/l2_classify.h>
20 #include <vnet/api_errno.h>
21
22 typedef struct
23 {
24   /* per-pkt trace data */
25   u32 sw_if_index;
26   u32 next_index;
27   u32 table_index;
28   u32 session_offset;
29 } l2_classify_trace_t;
30
31 typedef struct
32 {
33   vnet_classify_main_t *vcm;
34   l2_classify_main_t *l2cm;
35 } l2_classify_runtime_t;
36
37 /* packet trace format function */
38 static u8 *
39 format_l2_classify_trace (u8 * s, va_list * args)
40 {
41   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
42   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
43   l2_classify_trace_t *t = va_arg (*args, l2_classify_trace_t *);
44
45   s = format (s, "l2-classify: sw_if_index %d, table %d, offset %x, next %d",
46               t->sw_if_index, t->table_index, t->session_offset,
47               t->next_index);
48   return s;
49 }
50
51 l2_classify_main_t l2_classify_main;
52
53 vlib_node_registration_t l2_classify_node;
54
55 #define foreach_l2_classify_error               \
56 _(MISS, "Classify misses")                      \
57 _(HIT, "Classify hits")                         \
58 _(CHAIN_HIT, "Classify hits after chain walk")  \
59 _(DROP, "L2 Classify Drops")
60
61 typedef enum
62 {
63 #define _(sym,str) L2_CLASSIFY_ERROR_##sym,
64   foreach_l2_classify_error
65 #undef _
66     L2_CLASSIFY_N_ERROR,
67 } l2_classify_error_t;
68
69 static char *l2_classify_error_strings[] = {
70 #define _(sym,string) string,
71   foreach_l2_classify_error
72 #undef _
73 };
74
75 static uword
76 l2_classify_node_fn (vlib_main_t * vm,
77                      vlib_node_runtime_t * node, vlib_frame_t * frame)
78 {
79   u32 n_left_from, *from, *to_next;
80   l2_classify_next_t next_index;
81   l2_classify_main_t *cm = &l2_classify_main;
82   vnet_classify_main_t *vcm = cm->vnet_classify_main;
83   l2_classify_runtime_t *rt = (l2_classify_runtime_t *) node->runtime_data;
84   u32 feature_bitmap;
85   u32 hits = 0;
86   u32 misses = 0;
87   u32 chain_hits = 0;
88   f64 now;
89   u32 n_next_nodes;
90
91   n_next_nodes = node->n_next_nodes;
92
93   now = vlib_time_now (vm);
94
95   n_left_from = frame->n_vectors;
96   from = vlib_frame_vector_args (frame);
97
98   /* First pass: compute hash */
99
100   while (n_left_from > 2)
101     {
102       vlib_buffer_t *b0, *b1;
103       u32 bi0, bi1;
104       ethernet_header_t *h0, *h1;
105       u32 sw_if_index0, sw_if_index1;
106       u16 type0, type1;
107       int type_index0, type_index1;
108       vnet_classify_table_t *t0, *t1;
109       u32 table_index0, table_index1;
110       u64 hash0, hash1;
111
112
113       /* prefetch next iteration */
114       {
115         vlib_buffer_t *p1, *p2;
116
117         p1 = vlib_get_buffer (vm, from[1]);
118         p2 = vlib_get_buffer (vm, from[2]);
119
120         vlib_prefetch_buffer_header (p1, STORE);
121         CLIB_PREFETCH (p1->data, CLIB_CACHE_LINE_BYTES, STORE);
122         vlib_prefetch_buffer_header (p2, STORE);
123         CLIB_PREFETCH (p2->data, CLIB_CACHE_LINE_BYTES, STORE);
124       }
125
126       bi0 = from[0];
127       b0 = vlib_get_buffer (vm, bi0);
128       h0 = vlib_buffer_get_current (b0);
129
130       bi1 = from[1];
131       b1 = vlib_get_buffer (vm, bi1);
132       h1 = vlib_buffer_get_current (b1);
133
134       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
135       vnet_buffer (b0)->l2_classify.table_index = ~0;
136
137       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
138       vnet_buffer (b1)->l2_classify.table_index = ~0;
139
140       /* Select classifier table based on ethertype */
141       type0 = clib_net_to_host_u16 (h0->type);
142       type1 = clib_net_to_host_u16 (h1->type);
143
144       type_index0 = (type0 == ETHERNET_TYPE_IP4)
145         ? L2_CLASSIFY_TABLE_IP4 : L2_CLASSIFY_TABLE_OTHER;
146       type_index0 = (type0 == ETHERNET_TYPE_IP6)
147         ? L2_CLASSIFY_TABLE_IP6 : type_index0;
148
149       type_index1 = (type1 == ETHERNET_TYPE_IP4)
150         ? L2_CLASSIFY_TABLE_IP4 : L2_CLASSIFY_TABLE_OTHER;
151       type_index1 = (type1 == ETHERNET_TYPE_IP6)
152         ? L2_CLASSIFY_TABLE_IP6 : type_index1;
153
154       vnet_buffer (b0)->l2_classify.table_index =
155         table_index0 =
156         rt->l2cm->classify_table_index_by_sw_if_index
157         [type_index0][sw_if_index0];
158
159       if (table_index0 != ~0)
160         {
161           t0 = pool_elt_at_index (vcm->tables, table_index0);
162
163           vnet_buffer (b0)->l2_classify.hash = hash0 =
164             vnet_classify_hash_packet (t0, (u8 *) h0);
165           vnet_classify_prefetch_bucket (t0, hash0);
166         }
167
168       vnet_buffer (b1)->l2_classify.table_index =
169         table_index1 =
170         rt->l2cm->classify_table_index_by_sw_if_index
171         [type_index1][sw_if_index1];
172
173       if (table_index1 != ~0)
174         {
175           t1 = pool_elt_at_index (vcm->tables, table_index1);
176
177           vnet_buffer (b1)->l2_classify.hash = hash1 =
178             vnet_classify_hash_packet (t1, (u8 *) h1);
179           vnet_classify_prefetch_bucket (t1, hash1);
180         }
181
182       from += 2;
183       n_left_from -= 2;
184     }
185
186   while (n_left_from > 0)
187     {
188       vlib_buffer_t *b0;
189       u32 bi0;
190       ethernet_header_t *h0;
191       u32 sw_if_index0;
192       u16 type0;
193       u32 type_index0;
194       vnet_classify_table_t *t0;
195       u32 table_index0;
196       u64 hash0;
197
198       bi0 = from[0];
199       b0 = vlib_get_buffer (vm, bi0);
200       h0 = vlib_buffer_get_current (b0);
201
202       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
203       vnet_buffer (b0)->l2_classify.table_index = ~0;
204
205       /* Select classifier table based on ethertype */
206       type0 = clib_net_to_host_u16 (h0->type);
207
208       type_index0 = (type0 == ETHERNET_TYPE_IP4)
209         ? L2_CLASSIFY_TABLE_IP4 : L2_CLASSIFY_TABLE_OTHER;
210       type_index0 = (type0 == ETHERNET_TYPE_IP6)
211         ? L2_CLASSIFY_TABLE_IP6 : type_index0;
212
213       vnet_buffer (b0)->l2_classify.table_index =
214         table_index0 = rt->l2cm->classify_table_index_by_sw_if_index
215         [type_index0][sw_if_index0];
216
217       if (table_index0 != ~0)
218         {
219           t0 = pool_elt_at_index (vcm->tables, table_index0);
220
221           vnet_buffer (b0)->l2_classify.hash = hash0 =
222             vnet_classify_hash_packet (t0, (u8 *) h0);
223           vnet_classify_prefetch_bucket (t0, hash0);
224         }
225       from++;
226       n_left_from--;
227     }
228
229   next_index = node->cached_next_index;
230   from = vlib_frame_vector_args (frame);
231   n_left_from = frame->n_vectors;
232
233   while (n_left_from > 0)
234     {
235       u32 n_left_to_next;
236
237       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
238
239       /* Not enough load/store slots to dual loop... */
240       while (n_left_from > 0 && n_left_to_next > 0)
241         {
242           u32 bi0;
243           vlib_buffer_t *b0;
244           u32 next0 = L2_CLASSIFY_NEXT_ETHERNET_INPUT;
245           ethernet_header_t *h0;
246           u32 table_index0;
247           u64 hash0;
248           vnet_classify_table_t *t0;
249           vnet_classify_entry_t *e0;
250
251           if (PREDICT_TRUE (n_left_from > 2))
252             {
253               vlib_buffer_t *p2 = vlib_get_buffer (vm, from[2]);
254               u64 phash2;
255               u32 table_index2;
256               vnet_classify_table_t *tp2;
257
258               /*
259                * Prefetch table entry two ahead. Buffer / data
260                * were prefetched above...
261                */
262               table_index2 = vnet_buffer (p2)->l2_classify.table_index;
263
264               if (PREDICT_TRUE (table_index2 != ~0))
265                 {
266                   tp2 = pool_elt_at_index (vcm->tables, table_index2);
267                   phash2 = vnet_buffer (p2)->l2_classify.hash;
268                   vnet_classify_prefetch_entry (tp2, phash2);
269                 }
270             }
271
272           /* speculatively enqueue b0 to the current next frame */
273           bi0 = from[0];
274           to_next[0] = bi0;
275           from += 1;
276           to_next += 1;
277           n_left_from -= 1;
278           n_left_to_next -= 1;
279
280           b0 = vlib_get_buffer (vm, bi0);
281           h0 = vlib_buffer_get_current (b0);
282           table_index0 = vnet_buffer (b0)->l2_classify.table_index;
283           e0 = 0;
284           vnet_buffer (b0)->l2_classify.opaque_index = ~0;
285
286           if (PREDICT_TRUE (table_index0 != ~0))
287             {
288               hash0 = vnet_buffer (b0)->l2_classify.hash;
289               t0 = pool_elt_at_index (vcm->tables, table_index0);
290
291               e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
292               if (e0)
293                 {
294                   vnet_buffer (b0)->l2_classify.opaque_index
295                     = e0->opaque_index;
296                   vlib_buffer_advance (b0, e0->advance);
297                   next0 = (e0->next_index < n_next_nodes) ?
298                     e0->next_index : next0;
299                   hits++;
300                 }
301               else
302                 {
303                   while (1)
304                     {
305                       if (t0->next_table_index != ~0)
306                         t0 = pool_elt_at_index (vcm->tables,
307                                                 t0->next_table_index);
308                       else
309                         {
310                           next0 = (t0->miss_next_index < n_next_nodes) ?
311                             t0->miss_next_index : next0;
312                           misses++;
313                           break;
314                         }
315
316                       hash0 = vnet_classify_hash_packet (t0, (u8 *) h0);
317                       e0 =
318                         vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
319                       if (e0)
320                         {
321                           vnet_buffer (b0)->l2_classify.opaque_index
322                             = e0->opaque_index;
323                           vlib_buffer_advance (b0, e0->advance);
324                           next0 = (e0->next_index < n_next_nodes) ?
325                             e0->next_index : next0;
326                           hits++;
327                           chain_hits++;
328                           break;
329                         }
330                     }
331                 }
332             }
333
334           if (PREDICT_FALSE (next0 == 0))
335             b0->error = node->errors[L2_CLASSIFY_ERROR_DROP];
336
337           if (PREDICT_FALSE (next0 == ~0))
338             {
339
340               // Remove ourself from the feature bitmap
341               feature_bitmap = vnet_buffer (b0)->l2.feature_bitmap
342                 & ~L2INPUT_FEAT_CLASSIFY;
343
344               // save for next feature graph nodes
345               vnet_buffer (b0)->l2.feature_bitmap = feature_bitmap;
346
347               // Determine the next node
348               next0 =
349                 feat_bitmap_get_next_node_index (cm->feat_next_node_index,
350                                                  feature_bitmap);
351             }
352
353           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
354                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
355             {
356               l2_classify_trace_t *t =
357                 vlib_add_trace (vm, node, b0, sizeof (*t));
358               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
359               t->table_index = table_index0;
360               t->next_index = next0;
361               t->session_offset = e0 ? vnet_classify_get_offset (t0, e0) : 0;
362             }
363
364           /* verify speculative enqueue, maybe switch current next frame */
365           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
366                                            to_next, n_left_to_next,
367                                            bi0, next0);
368         }
369
370       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
371     }
372
373   vlib_node_increment_counter (vm, node->node_index,
374                                L2_CLASSIFY_ERROR_MISS, misses);
375   vlib_node_increment_counter (vm, node->node_index,
376                                L2_CLASSIFY_ERROR_HIT, hits);
377   vlib_node_increment_counter (vm, node->node_index,
378                                L2_CLASSIFY_ERROR_CHAIN_HIT, chain_hits);
379   return frame->n_vectors;
380 }
381
382 /* *INDENT-OFF* */
383 VLIB_REGISTER_NODE (l2_classify_node) = {
384   .function = l2_classify_node_fn,
385   .name = "l2-classify",
386   .vector_size = sizeof (u32),
387   .format_trace = format_l2_classify_trace,
388   .type = VLIB_NODE_TYPE_INTERNAL,
389
390   .n_errors = ARRAY_LEN(l2_classify_error_strings),
391   .error_strings = l2_classify_error_strings,
392
393   .runtime_data_bytes = sizeof (l2_classify_runtime_t),
394
395   .n_next_nodes = L2_CLASSIFY_N_NEXT,
396
397   /* edit / add dispositions here */
398   .next_nodes = {
399     [L2_CLASSIFY_NEXT_DROP]  = "error-drop",
400     [L2_CLASSIFY_NEXT_ETHERNET_INPUT] = "ethernet-input-not-l2",
401     [L2_CLASSIFY_NEXT_IP4_INPUT] = "ip4-input",
402     [L2_CLASSIFY_NEXT_IP6_INPUT] = "ip6-input",
403     [L2_CLASSIFY_NEXT_LI] = "li-hit",
404   },
405 };
406 /* *INDENT-ON* */
407
408 VLIB_NODE_FUNCTION_MULTIARCH (l2_classify_node, l2_classify_node_fn)
409      clib_error_t *l2_classify_init (vlib_main_t * vm)
410 {
411   l2_classify_main_t *cm = &l2_classify_main;
412   l2_classify_runtime_t *rt;
413
414   rt = vlib_node_get_runtime_data (vm, l2_classify_node.index);
415
416   cm->vlib_main = vm;
417   cm->vnet_main = vnet_get_main ();
418   cm->vnet_classify_main = &vnet_classify_main;
419
420   /* Initialize the feature next-node indexes */
421   feat_bitmap_init_next_nodes (vm,
422                                l2_classify_node.index,
423                                L2INPUT_N_FEAT,
424                                l2input_get_feat_names (),
425                                cm->feat_next_node_index);
426   rt->l2cm = cm;
427   rt->vcm = cm->vnet_classify_main;
428
429   return 0;
430 }
431
432 VLIB_INIT_FUNCTION (l2_classify_init);
433
434
435 void
436 vnet_l2_classify_enable_disable (u32 sw_if_index, int enable_disable)
437 {
438   vlib_main_t *vm = vlib_get_main ();
439   vnet_main_t *vnm = vnet_get_main ();
440
441   if (enable_disable)
442     set_int_l2_mode (vm, vnm, MODE_L2_CLASSIFY, sw_if_index, 0, 0, 0, 0);
443   else
444     set_int_l2_mode (vm, vnm, MODE_L3, sw_if_index, 0, 0, 0, 0);
445 }
446
447 int
448 vnet_l2_classify_set_tables (u32 sw_if_index,
449                              u32 ip4_table_index,
450                              u32 ip6_table_index, u32 other_table_index)
451 {
452   l2_classify_main_t *cm = &l2_classify_main;
453   vnet_classify_main_t *vcm = cm->vnet_classify_main;
454
455   /* Assume that we've validated sw_if_index in the API layer */
456
457   if (ip4_table_index != ~0 &&
458       pool_is_free_index (vcm->tables, ip4_table_index))
459     return VNET_API_ERROR_NO_SUCH_TABLE;
460
461   if (ip6_table_index != ~0 &&
462       pool_is_free_index (vcm->tables, ip6_table_index))
463     return VNET_API_ERROR_NO_SUCH_TABLE2;
464
465   if (other_table_index != ~0 &&
466       pool_is_free_index (vcm->tables, other_table_index))
467     return VNET_API_ERROR_NO_SUCH_TABLE3;
468
469   vec_validate
470     (cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP4],
471      sw_if_index);
472
473   vec_validate
474     (cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP6],
475      sw_if_index);
476
477   vec_validate
478     (cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_OTHER],
479      sw_if_index);
480
481   cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP4]
482     [sw_if_index] = ip4_table_index;
483
484   cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_IP6]
485     [sw_if_index] = ip6_table_index;
486
487   cm->classify_table_index_by_sw_if_index[L2_CLASSIFY_TABLE_OTHER]
488     [sw_if_index] = other_table_index;
489
490   return 0;
491 }
492
493 static clib_error_t *
494 int_l2_classify_command_fn (vlib_main_t * vm,
495                             unformat_input_t * input,
496                             vlib_cli_command_t * cmd)
497 {
498   vnet_main_t *vnm = vnet_get_main ();
499   u32 sw_if_index = ~0;
500   u32 ip4_table_index = ~0;
501   u32 ip6_table_index = ~0;
502   u32 other_table_index = ~0;
503   int rv;
504
505   while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
506     {
507       if (unformat (input, "intfc %U", unformat_vnet_sw_interface,
508                     vnm, &sw_if_index))
509         ;
510       else if (unformat (input, "ip4-table %d", &ip4_table_index))
511         ;
512       else if (unformat (input, "ip6-table %d", &ip6_table_index))
513         ;
514       else if (unformat (input, "other-table %d", &other_table_index))
515         ;
516       else
517         break;
518     }
519
520   if (sw_if_index == ~0)
521     return clib_error_return (0, "interface must be specified");
522
523
524   if (ip4_table_index == ~0 && ip6_table_index == ~0
525       && other_table_index == ~0)
526     {
527       vlib_cli_output (vm, "L2 classification disabled");
528       vnet_l2_classify_enable_disable (sw_if_index, 0 /* enable */ );
529       return 0;
530     }
531
532   rv = vnet_l2_classify_set_tables (sw_if_index, ip4_table_index,
533                                     ip6_table_index, other_table_index);
534   switch (rv)
535     {
536     case 0:
537       vnet_l2_classify_enable_disable (sw_if_index, 1 /* enable */ );
538       break;
539
540     default:
541       return clib_error_return (0, "vnet_l2_classify_set_tables: %d", rv);
542       break;
543     }
544
545   return 0;
546 }
547
548 /* *INDENT-OFF* */
549 VLIB_CLI_COMMAND (int_l2_classify_cli, static) = {
550   .path = "set interface l2 classify",
551   .short_help =
552   "set interface l2 classify intfc <int> [ip4-table <n>]\n"
553   "  [ip6-table <n>] [other-table <n>]",
554   .function = int_l2_classify_command_fn,
555 };
556 /* *INDENT-ON* */
557
558
559
560 /*
561  * fd.io coding-style-patch-verification: ON
562  *
563  * Local Variables:
564  * eval: (c-set-style "gnu")
565  * End:
566  */