21b9393a22257ade8c68e65a5a49141345030441
[vpp.git] / src / vnet / policer / node_funcs.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 #include <stdint.h>
17
18 #include <vlib/vlib.h>
19 #include <vnet/vnet.h>
20 #include <vnet/policer/policer.h>
21 #include <vnet/policer/police_inlines.h>
22 #include <vnet/ip/ip.h>
23 #include <vnet/classify/policer_classify.h>
24 #include <vnet/classify/vnet_classify.h>
25 #include <vnet/l2/feat_bitmap.h>
26 #include <vnet/l2/l2_input.h>
27
28
29 /* Dispatch functions meant to be instantiated elsewhere */
30
31 typedef struct
32 {
33   u32 next_index;
34   u32 sw_if_index;
35   u32 policer_index;
36 } vnet_policer_trace_t;
37
38 /* packet trace format function */
39 static u8 *
40 format_policer_trace (u8 * s, va_list * args)
41 {
42   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
43   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
44   vnet_policer_trace_t *t = va_arg (*args, vnet_policer_trace_t *);
45
46   s = format (s, "VNET_POLICER: sw_if_index %d policer_index %d next %d",
47               t->sw_if_index, t->policer_index, t->next_index);
48   return s;
49 }
50
51 #define foreach_vnet_policer_error              \
52 _(TRANSMIT, "Packets Transmitted")              \
53 _(DROP, "Packets Dropped")
54
55 typedef enum
56 {
57 #define _(sym,str) VNET_POLICER_ERROR_##sym,
58   foreach_vnet_policer_error
59 #undef _
60     VNET_POLICER_N_ERROR,
61 } vnet_policer_error_t;
62
63 static char *vnet_policer_error_strings[] = {
64 #define _(sym,string) string,
65   foreach_vnet_policer_error
66 #undef _
67 };
68
69 static inline uword
70 vnet_policer_inline (vlib_main_t *vm, vlib_node_runtime_t *node,
71                      vlib_frame_t *frame)
72 {
73   u32 n_left_from, *from, *to_next;
74   vnet_policer_next_t next_index;
75   vnet_policer_main_t *pm = &vnet_policer_main;
76   u64 time_in_policer_periods;
77   u32 transmitted = 0;
78
79   time_in_policer_periods =
80     clib_cpu_time_now () >> POLICER_TICKS_PER_PERIOD_SHIFT;
81
82   from = vlib_frame_vector_args (frame);
83   n_left_from = frame->n_vectors;
84   next_index = node->cached_next_index;
85
86   while (n_left_from > 0)
87     {
88       u32 n_left_to_next;
89
90       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
91
92       while (n_left_from >= 4 && n_left_to_next >= 2)
93         {
94           u32 bi0, bi1;
95           vlib_buffer_t *b0, *b1;
96           u32 next0, next1;
97           u32 sw_if_index0, sw_if_index1;
98           u32 pi0 = 0, pi1 = 0;
99           u8 act0, act1;
100
101           /* Prefetch next iteration. */
102           {
103             vlib_buffer_t *b2, *b3;
104
105             b2 = vlib_get_buffer (vm, from[2]);
106             b3 = vlib_get_buffer (vm, from[3]);
107
108             vlib_prefetch_buffer_header (b2, LOAD);
109             vlib_prefetch_buffer_header (b3, LOAD);
110           }
111
112           /* speculatively enqueue b0 and b1 to the current next frame */
113           to_next[0] = bi0 = from[0];
114           to_next[1] = bi1 = from[1];
115           from += 2;
116           to_next += 2;
117           n_left_from -= 2;
118           n_left_to_next -= 2;
119
120           b0 = vlib_get_buffer (vm, bi0);
121           b1 = vlib_get_buffer (vm, bi1);
122
123           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
124           sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
125
126           pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
127           pi1 = pm->policer_index_by_sw_if_index[sw_if_index1];
128
129           act0 = vnet_policer_police (vm, b0, pi0, time_in_policer_periods,
130                                       POLICE_CONFORM /* no chaining */, true);
131
132           act1 = vnet_policer_police (vm, b1, pi1, time_in_policer_periods,
133                                       POLICE_CONFORM /* no chaining */, true);
134
135           if (PREDICT_FALSE (act0 == QOS_ACTION_HANDOFF))
136             {
137               next0 = VNET_POLICER_NEXT_HANDOFF;
138               vnet_buffer (b0)->policer.index = pi0;
139             }
140           else if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
141             {
142               next0 = VNET_POLICER_NEXT_DROP;
143               b0->error = node->errors[VNET_POLICER_ERROR_DROP];
144             }
145           else /* transmit or mark-and-transmit action */
146             {
147               transmitted++;
148               vnet_feature_next (&next0, b0);
149             }
150
151           if (PREDICT_FALSE (act1 == QOS_ACTION_HANDOFF))
152             {
153               next1 = VNET_POLICER_NEXT_HANDOFF;
154               vnet_buffer (b1)->policer.index = pi1;
155             }
156           else if (PREDICT_FALSE (act1 == QOS_ACTION_DROP)) /* drop action */
157             {
158               next1 = VNET_POLICER_NEXT_DROP;
159               b1->error = node->errors[VNET_POLICER_ERROR_DROP];
160             }
161           else /* transmit or mark-and-transmit action */
162             {
163               transmitted++;
164               vnet_feature_next (&next1, b1);
165             }
166
167           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)))
168             {
169               if (b0->flags & VLIB_BUFFER_IS_TRACED)
170                 {
171                   vnet_policer_trace_t *t =
172                     vlib_add_trace (vm, node, b0, sizeof (*t));
173                   t->sw_if_index = sw_if_index0;
174                   t->next_index = next0;
175                 }
176               if (b1->flags & VLIB_BUFFER_IS_TRACED)
177                 {
178                   vnet_policer_trace_t *t =
179                     vlib_add_trace (vm, node, b1, sizeof (*t));
180                   t->sw_if_index = sw_if_index1;
181                   t->next_index = next1;
182                 }
183             }
184
185           /* verify speculative enqueues, maybe switch current next frame */
186           vlib_validate_buffer_enqueue_x2 (vm, node, next_index,
187                                            to_next, n_left_to_next,
188                                            bi0, bi1, next0, next1);
189         }
190
191       while (n_left_from > 0 && n_left_to_next > 0)
192         {
193           u32 bi0;
194           vlib_buffer_t *b0;
195           u32 next0;
196           u32 sw_if_index0;
197           u32 pi0 = 0;
198           u8 act0;
199
200           bi0 = from[0];
201           to_next[0] = bi0;
202           from += 1;
203           to_next += 1;
204           n_left_from -= 1;
205           n_left_to_next -= 1;
206
207           b0 = vlib_get_buffer (vm, bi0);
208
209           sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
210
211           pi0 = pm->policer_index_by_sw_if_index[sw_if_index0];
212
213           act0 = vnet_policer_police (vm, b0, pi0, time_in_policer_periods,
214                                       POLICE_CONFORM /* no chaining */, true);
215
216           if (PREDICT_FALSE (act0 == QOS_ACTION_HANDOFF))
217             {
218               next0 = VNET_POLICER_NEXT_HANDOFF;
219               vnet_buffer (b0)->policer.index = pi0;
220             }
221           else if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
222             {
223               next0 = VNET_POLICER_NEXT_DROP;
224               b0->error = node->errors[VNET_POLICER_ERROR_DROP];
225             }
226           else /* transmit or mark-and-transmit action */
227             {
228               transmitted++;
229               vnet_feature_next (&next0, b0);
230             }
231
232           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
233                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
234             {
235               vnet_policer_trace_t *t =
236                 vlib_add_trace (vm, node, b0, sizeof (*t));
237               t->sw_if_index = sw_if_index0;
238               t->next_index = next0;
239               t->policer_index = pi0;
240             }
241
242           /* verify speculative enqueue, maybe switch current next frame */
243           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
244                                            to_next, n_left_to_next,
245                                            bi0, next0);
246         }
247
248       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
249     }
250
251   vlib_node_increment_counter (vm, node->node_index,
252                                VNET_POLICER_ERROR_TRANSMIT, transmitted);
253   return frame->n_vectors;
254 }
255
256 VLIB_NODE_FN (policer_input_node)
257 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
258 {
259   return vnet_policer_inline (vm, node, frame);
260 }
261
262 VLIB_REGISTER_NODE (policer_input_node) = {
263   .name = "policer-input",
264   .vector_size = sizeof (u32),
265   .format_trace = format_policer_trace,
266   .type = VLIB_NODE_TYPE_INTERNAL,
267   .n_errors = ARRAY_LEN(vnet_policer_error_strings),
268   .error_strings = vnet_policer_error_strings,
269   .n_next_nodes = VNET_POLICER_N_NEXT,
270   .next_nodes = {
271                  [VNET_POLICER_NEXT_DROP] = "error-drop",
272                  [VNET_POLICER_NEXT_HANDOFF] = "policer-input-handoff",
273                  },
274 };
275
276 VNET_FEATURE_INIT (policer_input_node, static) = {
277   .arc_name = "device-input",
278   .node_name = "policer-input",
279   .runs_before = VNET_FEATURES ("ethernet-input"),
280 };
281
282 static char *policer_input_handoff_error_strings[] = { "congestion drop" };
283
284 VLIB_NODE_FN (policer_input_handoff_node)
285 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
286 {
287   return policer_handoff (vm, node, frame, vnet_policer_main.fq_index, ~0);
288 }
289
290 VLIB_REGISTER_NODE (policer_input_handoff_node) = {
291   .name = "policer-input-handoff",
292   .vector_size = sizeof (u32),
293   .format_trace = format_policer_handoff_trace,
294   .type = VLIB_NODE_TYPE_INTERNAL,
295   .n_errors = ARRAY_LEN(policer_input_handoff_error_strings),
296   .error_strings = policer_input_handoff_error_strings,
297
298   .n_next_nodes = 1,
299   .next_nodes = {
300     [0] = "error-drop",
301   },
302 };
303
304 typedef struct
305 {
306   u32 sw_if_index;
307   u32 next_index;
308   u32 table_index;
309   u32 offset;
310   u32 policer_index;
311 } policer_classify_trace_t;
312
313 static u8 *
314 format_policer_classify_trace (u8 * s, va_list * args)
315 {
316   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
317   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
318   policer_classify_trace_t *t = va_arg (*args, policer_classify_trace_t *);
319
320   s = format (s, "POLICER_CLASSIFY: sw_if_index %d next %d table %d offset %d"
321               " policer_index %d",
322               t->sw_if_index, t->next_index, t->table_index, t->offset,
323               t->policer_index);
324   return s;
325 }
326
327 #define foreach_policer_classify_error                 \
328 _(MISS, "Policer classify misses")                     \
329 _(HIT, "Policer classify hits")                        \
330 _(CHAIN_HIT, "Policer classify hits after chain walk") \
331 _(DROP, "Policer classify action drop")
332
333 typedef enum
334 {
335 #define _(sym,str) POLICER_CLASSIFY_ERROR_##sym,
336   foreach_policer_classify_error
337 #undef _
338     POLICER_CLASSIFY_N_ERROR,
339 } policer_classify_error_t;
340
341 static char *policer_classify_error_strings[] = {
342 #define _(sym,string) string,
343   foreach_policer_classify_error
344 #undef _
345 };
346
347 static inline uword
348 policer_classify_inline (vlib_main_t * vm,
349                          vlib_node_runtime_t * node,
350                          vlib_frame_t * frame,
351                          policer_classify_table_id_t tid)
352 {
353   u32 n_left_from, *from, *to_next;
354   policer_classify_next_index_t next_index;
355   policer_classify_main_t *pcm = &policer_classify_main;
356   vnet_classify_main_t *vcm = pcm->vnet_classify_main;
357   f64 now = vlib_time_now (vm);
358   u32 hits = 0;
359   u32 misses = 0;
360   u32 chain_hits = 0;
361   u32 n_next_nodes;
362   u64 time_in_policer_periods;
363
364   time_in_policer_periods =
365     clib_cpu_time_now () >> POLICER_TICKS_PER_PERIOD_SHIFT;
366
367   n_next_nodes = node->n_next_nodes;
368
369   from = vlib_frame_vector_args (frame);
370   n_left_from = frame->n_vectors;
371
372   /* First pass: compute hashes */
373   while (n_left_from > 2)
374     {
375       vlib_buffer_t *b0, *b1;
376       u32 bi0, bi1;
377       u8 *h0, *h1;
378       u32 sw_if_index0, sw_if_index1;
379       u32 table_index0, table_index1;
380       vnet_classify_table_t *t0, *t1;
381
382       /* Prefetch next iteration */
383       {
384         vlib_buffer_t *p1, *p2;
385
386         p1 = vlib_get_buffer (vm, from[1]);
387         p2 = vlib_get_buffer (vm, from[2]);
388
389         vlib_prefetch_buffer_header (p1, STORE);
390         clib_prefetch_store (p1->data);
391         vlib_prefetch_buffer_header (p2, STORE);
392         clib_prefetch_store (p2->data);
393       }
394
395       bi0 = from[0];
396       b0 = vlib_get_buffer (vm, bi0);
397       h0 = b0->data;
398
399       bi1 = from[1];
400       b1 = vlib_get_buffer (vm, bi1);
401       h1 = b1->data;
402
403       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
404       table_index0 =
405         pcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
406
407       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
408       table_index1 =
409         pcm->classify_table_index_by_sw_if_index[tid][sw_if_index1];
410
411       t0 = pool_elt_at_index (vcm->tables, table_index0);
412
413       t1 = pool_elt_at_index (vcm->tables, table_index1);
414
415       vnet_buffer (b0)->l2_classify.hash =
416         vnet_classify_hash_packet (t0, (u8 *) h0);
417
418       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
419
420       vnet_buffer (b1)->l2_classify.hash =
421         vnet_classify_hash_packet (t1, (u8 *) h1);
422
423       vnet_classify_prefetch_bucket (t1, vnet_buffer (b1)->l2_classify.hash);
424
425       vnet_buffer (b0)->l2_classify.table_index = table_index0;
426
427       vnet_buffer (b1)->l2_classify.table_index = table_index1;
428
429       from += 2;
430       n_left_from -= 2;
431     }
432
433   while (n_left_from > 0)
434     {
435       vlib_buffer_t *b0;
436       u32 bi0;
437       u8 *h0;
438       u32 sw_if_index0;
439       u32 table_index0;
440       vnet_classify_table_t *t0;
441
442       bi0 = from[0];
443       b0 = vlib_get_buffer (vm, bi0);
444       h0 = b0->data;
445
446       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
447       table_index0 =
448         pcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
449
450       t0 = pool_elt_at_index (vcm->tables, table_index0);
451       vnet_buffer (b0)->l2_classify.hash =
452         vnet_classify_hash_packet (t0, (u8 *) h0);
453
454       vnet_buffer (b0)->l2_classify.table_index = table_index0;
455       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
456
457       from++;
458       n_left_from--;
459     }
460
461   next_index = node->cached_next_index;
462   from = vlib_frame_vector_args (frame);
463   n_left_from = frame->n_vectors;
464
465   while (n_left_from > 0)
466     {
467       u32 n_left_to_next;
468
469       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
470
471       /* Not enough load/store slots to dual loop... */
472       while (n_left_from > 0 && n_left_to_next > 0)
473         {
474           u32 bi0;
475           vlib_buffer_t *b0;
476           u32 next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
477           u32 table_index0;
478           vnet_classify_table_t *t0;
479           vnet_classify_entry_t *e0;
480           u64 hash0;
481           u8 *h0;
482           u8 act0;
483
484           /* Stride 3 seems to work best */
485           if (PREDICT_TRUE (n_left_from > 3))
486             {
487               vlib_buffer_t *p1 = vlib_get_buffer (vm, from[3]);
488               vnet_classify_table_t *tp1;
489               u32 table_index1;
490               u64 phash1;
491
492               table_index1 = vnet_buffer (p1)->l2_classify.table_index;
493
494               if (PREDICT_TRUE (table_index1 != ~0))
495                 {
496                   tp1 = pool_elt_at_index (vcm->tables, table_index1);
497                   phash1 = vnet_buffer (p1)->l2_classify.hash;
498                   vnet_classify_prefetch_entry (tp1, phash1);
499                 }
500             }
501
502           /* Speculatively enqueue b0 to the current next frame */
503           bi0 = from[0];
504           to_next[0] = bi0;
505           from += 1;
506           to_next += 1;
507           n_left_from -= 1;
508           n_left_to_next -= 1;
509
510           b0 = vlib_get_buffer (vm, bi0);
511           h0 = b0->data;
512           table_index0 = vnet_buffer (b0)->l2_classify.table_index;
513           e0 = 0;
514           t0 = 0;
515
516           if (tid == POLICER_CLASSIFY_TABLE_L2)
517             {
518               /* Feature bitmap update and determine the next node */
519               next0 = vnet_l2_feature_next (b0, pcm->feat_next_node_index,
520                                             L2INPUT_FEAT_POLICER_CLAS);
521             }
522           else
523             vnet_get_config_data (pcm->vnet_config_main[tid],
524                                   &b0->current_config_index, &next0,
525                                   /* # bytes of config data */ 0);
526
527           vnet_buffer (b0)->l2_classify.opaque_index = ~0;
528
529           if (PREDICT_TRUE (table_index0 != ~0))
530             {
531               hash0 = vnet_buffer (b0)->l2_classify.hash;
532               t0 = pool_elt_at_index (vcm->tables, table_index0);
533               e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
534
535               if (e0)
536                 {
537                   act0 = vnet_policer_police (vm, b0, e0->next_index,
538                                               time_in_policer_periods,
539                                               e0->opaque_index, false);
540                   if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
541                     {
542                       next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
543                       b0->error = node->errors[POLICER_CLASSIFY_ERROR_DROP];
544                     }
545                   hits++;
546                 }
547               else
548                 {
549                   while (1)
550                     {
551                       if (PREDICT_TRUE (t0->next_table_index != ~0))
552                         {
553                           t0 = pool_elt_at_index (vcm->tables,
554                                                   t0->next_table_index);
555                         }
556                       else
557                         {
558                           next0 = (t0->miss_next_index < n_next_nodes) ?
559                             t0->miss_next_index : next0;
560                           misses++;
561                           break;
562                         }
563
564                       hash0 = vnet_classify_hash_packet (t0, (u8 *) h0);
565                       e0 =
566                         vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
567                       if (e0)
568                         {
569                           act0 = vnet_policer_police (vm, b0, e0->next_index,
570                                                       time_in_policer_periods,
571                                                       e0->opaque_index, false);
572                           if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
573                             {
574                               next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
575                               b0->error =
576                                 node->errors[POLICER_CLASSIFY_ERROR_DROP];
577                             }
578                           hits++;
579                           chain_hits++;
580                           break;
581                         }
582                     }
583                 }
584             }
585           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
586                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
587             {
588               policer_classify_trace_t *t =
589                 vlib_add_trace (vm, node, b0, sizeof (*t));
590               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
591               t->next_index = next0;
592               t->table_index = t0 ? t0 - vcm->tables : ~0;
593               t->offset = (e0 && t0) ? vnet_classify_get_offset (t0, e0) : ~0;
594               t->policer_index = e0 ? e0->next_index : ~0;
595             }
596
597           /* Verify speculative enqueue, maybe switch current next frame */
598           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
599                                            n_left_to_next, bi0, next0);
600         }
601
602       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
603     }
604
605   vlib_node_increment_counter (vm, node->node_index,
606                                POLICER_CLASSIFY_ERROR_MISS, misses);
607   vlib_node_increment_counter (vm, node->node_index,
608                                POLICER_CLASSIFY_ERROR_HIT, hits);
609   vlib_node_increment_counter (vm, node->node_index,
610                                POLICER_CLASSIFY_ERROR_CHAIN_HIT, chain_hits);
611
612   return frame->n_vectors;
613 }
614
615 VLIB_NODE_FN (ip4_policer_classify_node) (vlib_main_t * vm,
616                                           vlib_node_runtime_t * node,
617                                           vlib_frame_t * frame)
618 {
619   return policer_classify_inline (vm, node, frame,
620                                   POLICER_CLASSIFY_TABLE_IP4);
621 }
622
623 /* *INDENT-OFF* */
624 VLIB_REGISTER_NODE (ip4_policer_classify_node) = {
625   .name = "ip4-policer-classify",
626   .vector_size = sizeof (u32),
627   .format_trace = format_policer_classify_trace,
628   .n_errors = ARRAY_LEN(policer_classify_error_strings),
629   .error_strings = policer_classify_error_strings,
630   .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
631   .next_nodes = {
632     [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
633   },
634 };
635 /* *INDENT-ON* */
636
637 VLIB_NODE_FN (ip6_policer_classify_node) (vlib_main_t * vm,
638                                           vlib_node_runtime_t * node,
639                                           vlib_frame_t * frame)
640 {
641   return policer_classify_inline (vm, node, frame,
642                                   POLICER_CLASSIFY_TABLE_IP6);
643 }
644
645 /* *INDENT-OFF* */
646 VLIB_REGISTER_NODE (ip6_policer_classify_node) = {
647   .name = "ip6-policer-classify",
648   .vector_size = sizeof (u32),
649   .format_trace = format_policer_classify_trace,
650   .n_errors = ARRAY_LEN(policer_classify_error_strings),
651   .error_strings = policer_classify_error_strings,
652   .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
653   .next_nodes = {
654     [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
655   },
656 };
657 /* *INDENT-ON* */
658
659 VLIB_NODE_FN (l2_policer_classify_node) (vlib_main_t * vm,
660                                          vlib_node_runtime_t * node,
661                                          vlib_frame_t * frame)
662 {
663   return policer_classify_inline (vm, node, frame, POLICER_CLASSIFY_TABLE_L2);
664 }
665
666 /* *INDENT-OFF* */
667 VLIB_REGISTER_NODE (l2_policer_classify_node) = {
668   .name = "l2-policer-classify",
669   .vector_size = sizeof (u32),
670   .format_trace = format_policer_classify_trace,
671   .n_errors = ARRAY_LEN (policer_classify_error_strings),
672   .error_strings = policer_classify_error_strings,
673   .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
674   .next_nodes = {
675     [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
676   },
677 };
678 /* *INDENT-ON* */
679
680 #ifndef CLIB_MARCH_VARIANT
681 static clib_error_t *
682 policer_classify_init (vlib_main_t * vm)
683 {
684   policer_classify_main_t *pcm = &policer_classify_main;
685
686   pcm->vlib_main = vm;
687   pcm->vnet_main = vnet_get_main ();
688   pcm->vnet_classify_main = &vnet_classify_main;
689
690   /* Initialize L2 feature next-node indexes */
691   feat_bitmap_init_next_nodes (vm,
692                                l2_policer_classify_node.index,
693                                L2INPUT_N_FEAT,
694                                l2input_get_feat_names (),
695                                pcm->feat_next_node_index);
696
697   return 0;
698 }
699
700 VLIB_INIT_FUNCTION (policer_classify_init);
701 #endif /* CLIB_MARCH_VARIANT */
702
703 /*
704  * fd.io coding-style-patch-verification: ON
705  *
706  * Local Variables:
707  * eval: (c-set-style "gnu")
708  * End:
709  */