policer: output interface policer
[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, vlib_dir_t dir)
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[dir];
124           sw_if_index1 = vnet_buffer (b1)->sw_if_index[dir];
125
126           pi0 = pm->policer_index_by_sw_if_index[dir][sw_if_index0];
127           pi1 = pm->policer_index_by_sw_if_index[dir][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[dir];
210           pi0 = pm->policer_index_by_sw_if_index[dir][sw_if_index0];
211
212           act0 = vnet_policer_police (vm, b0, pi0, time_in_policer_periods,
213                                       POLICE_CONFORM /* no chaining */, true);
214
215           if (PREDICT_FALSE (act0 == QOS_ACTION_HANDOFF))
216             {
217               next0 = VNET_POLICER_NEXT_HANDOFF;
218               vnet_buffer (b0)->policer.index = pi0;
219             }
220           else if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
221             {
222               next0 = VNET_POLICER_NEXT_DROP;
223               b0->error = node->errors[VNET_POLICER_ERROR_DROP];
224             }
225           else /* transmit or mark-and-transmit action */
226             {
227               transmitted++;
228               vnet_feature_next (&next0, b0);
229             }
230
231           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
232                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
233             {
234               vnet_policer_trace_t *t =
235                 vlib_add_trace (vm, node, b0, sizeof (*t));
236               t->sw_if_index = sw_if_index0;
237               t->next_index = next0;
238               t->policer_index = pi0;
239             }
240
241           /* verify speculative enqueue, maybe switch current next frame */
242           vlib_validate_buffer_enqueue_x1 (vm, node, next_index,
243                                            to_next, n_left_to_next,
244                                            bi0, next0);
245         }
246
247       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
248     }
249
250   vlib_node_increment_counter (vm, node->node_index,
251                                VNET_POLICER_ERROR_TRANSMIT, transmitted);
252   return frame->n_vectors;
253 }
254
255 VLIB_NODE_FN (policer_input_node)
256 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
257 {
258   return vnet_policer_inline (vm, node, frame, VLIB_RX);
259 }
260
261 VLIB_REGISTER_NODE (policer_input_node) = {
262   .name = "policer-input",
263   .vector_size = sizeof (u32),
264   .format_trace = format_policer_trace,
265   .type = VLIB_NODE_TYPE_INTERNAL,
266   .n_errors = ARRAY_LEN(vnet_policer_error_strings),
267   .error_strings = vnet_policer_error_strings,
268   .n_next_nodes = VNET_POLICER_N_NEXT,
269   .next_nodes = {
270                  [VNET_POLICER_NEXT_DROP] = "error-drop",
271                  [VNET_POLICER_NEXT_HANDOFF] = "policer-input-handoff",
272                  },
273 };
274
275 VNET_FEATURE_INIT (policer_input_node, static) = {
276   .arc_name = "device-input",
277   .node_name = "policer-input",
278   .runs_before = VNET_FEATURES ("ethernet-input"),
279 };
280
281 VLIB_NODE_FN (policer_output_node)
282 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
283 {
284   return vnet_policer_inline (vm, node, frame, VLIB_TX);
285 }
286
287 VLIB_REGISTER_NODE (policer_output_node) = {
288   .name = "policer-output",
289   .vector_size = sizeof (u32),
290   .format_trace = format_policer_trace,
291   .type = VLIB_NODE_TYPE_INTERNAL,
292   .n_errors = ARRAY_LEN(vnet_policer_error_strings),
293   .error_strings = vnet_policer_error_strings,
294   .n_next_nodes = VNET_POLICER_N_NEXT,
295   .next_nodes = {
296                  [VNET_POLICER_NEXT_DROP] = "error-drop",
297                  [VNET_POLICER_NEXT_HANDOFF] = "policer-output-handoff",
298                  },
299 };
300
301 VNET_FEATURE_INIT (policer_output_node, static) = {
302   .arc_name = "ip4-output",
303   .node_name = "policer-output",
304 };
305
306 VNET_FEATURE_INIT (policer6_output_node, static) = {
307   .arc_name = "ip6-output",
308   .node_name = "policer-output",
309 };
310
311 static char *policer_input_handoff_error_strings[] = { "congestion drop" };
312
313 VLIB_NODE_FN (policer_input_handoff_node)
314 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
315 {
316   return policer_handoff (vm, node, frame, vnet_policer_main.fq_index[VLIB_RX],
317                           ~0);
318 }
319
320 VLIB_REGISTER_NODE (policer_input_handoff_node) = {
321   .name = "policer-input-handoff",
322   .vector_size = sizeof (u32),
323   .format_trace = format_policer_handoff_trace,
324   .type = VLIB_NODE_TYPE_INTERNAL,
325   .n_errors = ARRAY_LEN(policer_input_handoff_error_strings),
326   .error_strings = policer_input_handoff_error_strings,
327
328   .n_next_nodes = 1,
329   .next_nodes = {
330     [0] = "error-drop",
331   },
332 };
333
334 VLIB_NODE_FN (policer_output_handoff_node)
335 (vlib_main_t *vm, vlib_node_runtime_t *node, vlib_frame_t *frame)
336 {
337   return policer_handoff (vm, node, frame, vnet_policer_main.fq_index[VLIB_TX],
338                           ~0);
339 }
340
341 VLIB_REGISTER_NODE (policer_output_handoff_node) = {
342   .name = "policer-output-handoff",
343   .vector_size = sizeof (u32),
344   .format_trace = format_policer_handoff_trace,
345   .type = VLIB_NODE_TYPE_INTERNAL,
346   .n_errors = ARRAY_LEN(policer_input_handoff_error_strings),
347   .error_strings = policer_input_handoff_error_strings,
348
349   .n_next_nodes = 1,
350   .next_nodes = {
351     [0] = "error-drop",
352   },
353 };
354 typedef struct
355 {
356   u32 sw_if_index;
357   u32 next_index;
358   u32 table_index;
359   u32 offset;
360   u32 policer_index;
361 } policer_classify_trace_t;
362
363 static u8 *
364 format_policer_classify_trace (u8 * s, va_list * args)
365 {
366   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
367   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
368   policer_classify_trace_t *t = va_arg (*args, policer_classify_trace_t *);
369
370   s = format (s, "POLICER_CLASSIFY: sw_if_index %d next %d table %d offset %d"
371               " policer_index %d",
372               t->sw_if_index, t->next_index, t->table_index, t->offset,
373               t->policer_index);
374   return s;
375 }
376
377 #define foreach_policer_classify_error                 \
378 _(MISS, "Policer classify misses")                     \
379 _(HIT, "Policer classify hits")                        \
380 _(CHAIN_HIT, "Policer classify hits after chain walk") \
381 _(DROP, "Policer classify action drop")
382
383 typedef enum
384 {
385 #define _(sym,str) POLICER_CLASSIFY_ERROR_##sym,
386   foreach_policer_classify_error
387 #undef _
388     POLICER_CLASSIFY_N_ERROR,
389 } policer_classify_error_t;
390
391 static char *policer_classify_error_strings[] = {
392 #define _(sym,string) string,
393   foreach_policer_classify_error
394 #undef _
395 };
396
397 static inline uword
398 policer_classify_inline (vlib_main_t * vm,
399                          vlib_node_runtime_t * node,
400                          vlib_frame_t * frame,
401                          policer_classify_table_id_t tid)
402 {
403   u32 n_left_from, *from, *to_next;
404   policer_classify_next_index_t next_index;
405   policer_classify_main_t *pcm = &policer_classify_main;
406   vnet_classify_main_t *vcm = pcm->vnet_classify_main;
407   f64 now = vlib_time_now (vm);
408   u32 hits = 0;
409   u32 misses = 0;
410   u32 chain_hits = 0;
411   u32 n_next_nodes;
412   u64 time_in_policer_periods;
413
414   time_in_policer_periods =
415     clib_cpu_time_now () >> POLICER_TICKS_PER_PERIOD_SHIFT;
416
417   n_next_nodes = node->n_next_nodes;
418
419   from = vlib_frame_vector_args (frame);
420   n_left_from = frame->n_vectors;
421
422   /* First pass: compute hashes */
423   while (n_left_from > 2)
424     {
425       vlib_buffer_t *b0, *b1;
426       u32 bi0, bi1;
427       u8 *h0, *h1;
428       u32 sw_if_index0, sw_if_index1;
429       u32 table_index0, table_index1;
430       vnet_classify_table_t *t0, *t1;
431
432       /* Prefetch next iteration */
433       {
434         vlib_buffer_t *p1, *p2;
435
436         p1 = vlib_get_buffer (vm, from[1]);
437         p2 = vlib_get_buffer (vm, from[2]);
438
439         vlib_prefetch_buffer_header (p1, STORE);
440         clib_prefetch_store (p1->data);
441         vlib_prefetch_buffer_header (p2, STORE);
442         clib_prefetch_store (p2->data);
443       }
444
445       bi0 = from[0];
446       b0 = vlib_get_buffer (vm, bi0);
447       h0 = b0->data;
448
449       bi1 = from[1];
450       b1 = vlib_get_buffer (vm, bi1);
451       h1 = b1->data;
452
453       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
454       table_index0 =
455         pcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
456
457       sw_if_index1 = vnet_buffer (b1)->sw_if_index[VLIB_RX];
458       table_index1 =
459         pcm->classify_table_index_by_sw_if_index[tid][sw_if_index1];
460
461       t0 = pool_elt_at_index (vcm->tables, table_index0);
462
463       t1 = pool_elt_at_index (vcm->tables, table_index1);
464
465       vnet_buffer (b0)->l2_classify.hash =
466         vnet_classify_hash_packet (t0, (u8 *) h0);
467
468       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
469
470       vnet_buffer (b1)->l2_classify.hash =
471         vnet_classify_hash_packet (t1, (u8 *) h1);
472
473       vnet_classify_prefetch_bucket (t1, vnet_buffer (b1)->l2_classify.hash);
474
475       vnet_buffer (b0)->l2_classify.table_index = table_index0;
476
477       vnet_buffer (b1)->l2_classify.table_index = table_index1;
478
479       from += 2;
480       n_left_from -= 2;
481     }
482
483   while (n_left_from > 0)
484     {
485       vlib_buffer_t *b0;
486       u32 bi0;
487       u8 *h0;
488       u32 sw_if_index0;
489       u32 table_index0;
490       vnet_classify_table_t *t0;
491
492       bi0 = from[0];
493       b0 = vlib_get_buffer (vm, bi0);
494       h0 = b0->data;
495
496       sw_if_index0 = vnet_buffer (b0)->sw_if_index[VLIB_RX];
497       table_index0 =
498         pcm->classify_table_index_by_sw_if_index[tid][sw_if_index0];
499
500       t0 = pool_elt_at_index (vcm->tables, table_index0);
501       vnet_buffer (b0)->l2_classify.hash =
502         vnet_classify_hash_packet (t0, (u8 *) h0);
503
504       vnet_buffer (b0)->l2_classify.table_index = table_index0;
505       vnet_classify_prefetch_bucket (t0, vnet_buffer (b0)->l2_classify.hash);
506
507       from++;
508       n_left_from--;
509     }
510
511   next_index = node->cached_next_index;
512   from = vlib_frame_vector_args (frame);
513   n_left_from = frame->n_vectors;
514
515   while (n_left_from > 0)
516     {
517       u32 n_left_to_next;
518
519       vlib_get_next_frame (vm, node, next_index, to_next, n_left_to_next);
520
521       /* Not enough load/store slots to dual loop... */
522       while (n_left_from > 0 && n_left_to_next > 0)
523         {
524           u32 bi0;
525           vlib_buffer_t *b0;
526           u32 next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
527           u32 table_index0;
528           vnet_classify_table_t *t0;
529           vnet_classify_entry_t *e0;
530           u64 hash0;
531           u8 *h0;
532           u8 act0;
533
534           /* Stride 3 seems to work best */
535           if (PREDICT_TRUE (n_left_from > 3))
536             {
537               vlib_buffer_t *p1 = vlib_get_buffer (vm, from[3]);
538               vnet_classify_table_t *tp1;
539               u32 table_index1;
540               u64 phash1;
541
542               table_index1 = vnet_buffer (p1)->l2_classify.table_index;
543
544               if (PREDICT_TRUE (table_index1 != ~0))
545                 {
546                   tp1 = pool_elt_at_index (vcm->tables, table_index1);
547                   phash1 = vnet_buffer (p1)->l2_classify.hash;
548                   vnet_classify_prefetch_entry (tp1, phash1);
549                 }
550             }
551
552           /* Speculatively enqueue b0 to the current next frame */
553           bi0 = from[0];
554           to_next[0] = bi0;
555           from += 1;
556           to_next += 1;
557           n_left_from -= 1;
558           n_left_to_next -= 1;
559
560           b0 = vlib_get_buffer (vm, bi0);
561           h0 = b0->data;
562           table_index0 = vnet_buffer (b0)->l2_classify.table_index;
563           e0 = 0;
564           t0 = 0;
565
566           if (tid == POLICER_CLASSIFY_TABLE_L2)
567             {
568               /* Feature bitmap update and determine the next node */
569               next0 = vnet_l2_feature_next (b0, pcm->feat_next_node_index,
570                                             L2INPUT_FEAT_POLICER_CLAS);
571             }
572           else
573             vnet_get_config_data (pcm->vnet_config_main[tid],
574                                   &b0->current_config_index, &next0,
575                                   /* # bytes of config data */ 0);
576
577           vnet_buffer (b0)->l2_classify.opaque_index = ~0;
578
579           if (PREDICT_TRUE (table_index0 != ~0))
580             {
581               hash0 = vnet_buffer (b0)->l2_classify.hash;
582               t0 = pool_elt_at_index (vcm->tables, table_index0);
583               e0 = vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
584
585               if (e0)
586                 {
587                   act0 = vnet_policer_police (vm, b0, e0->next_index,
588                                               time_in_policer_periods,
589                                               e0->opaque_index, false);
590                   if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
591                     {
592                       next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
593                       b0->error = node->errors[POLICER_CLASSIFY_ERROR_DROP];
594                     }
595                   hits++;
596                 }
597               else
598                 {
599                   while (1)
600                     {
601                       if (PREDICT_TRUE (t0->next_table_index != ~0))
602                         {
603                           t0 = pool_elt_at_index (vcm->tables,
604                                                   t0->next_table_index);
605                         }
606                       else
607                         {
608                           next0 = (t0->miss_next_index < n_next_nodes) ?
609                             t0->miss_next_index : next0;
610                           misses++;
611                           break;
612                         }
613
614                       hash0 = vnet_classify_hash_packet (t0, (u8 *) h0);
615                       e0 =
616                         vnet_classify_find_entry (t0, (u8 *) h0, hash0, now);
617                       if (e0)
618                         {
619                           act0 = vnet_policer_police (vm, b0, e0->next_index,
620                                                       time_in_policer_periods,
621                                                       e0->opaque_index, false);
622                           if (PREDICT_FALSE (act0 == QOS_ACTION_DROP))
623                             {
624                               next0 = POLICER_CLASSIFY_NEXT_INDEX_DROP;
625                               b0->error =
626                                 node->errors[POLICER_CLASSIFY_ERROR_DROP];
627                             }
628                           hits++;
629                           chain_hits++;
630                           break;
631                         }
632                     }
633                 }
634             }
635           if (PREDICT_FALSE ((node->flags & VLIB_NODE_FLAG_TRACE)
636                              && (b0->flags & VLIB_BUFFER_IS_TRACED)))
637             {
638               policer_classify_trace_t *t =
639                 vlib_add_trace (vm, node, b0, sizeof (*t));
640               t->sw_if_index = vnet_buffer (b0)->sw_if_index[VLIB_RX];
641               t->next_index = next0;
642               t->table_index = t0 ? t0 - vcm->tables : ~0;
643               t->offset = (e0 && t0) ? vnet_classify_get_offset (t0, e0) : ~0;
644               t->policer_index = e0 ? e0->next_index : ~0;
645             }
646
647           /* Verify speculative enqueue, maybe switch current next frame */
648           vlib_validate_buffer_enqueue_x1 (vm, node, next_index, to_next,
649                                            n_left_to_next, bi0, next0);
650         }
651
652       vlib_put_next_frame (vm, node, next_index, n_left_to_next);
653     }
654
655   vlib_node_increment_counter (vm, node->node_index,
656                                POLICER_CLASSIFY_ERROR_MISS, misses);
657   vlib_node_increment_counter (vm, node->node_index,
658                                POLICER_CLASSIFY_ERROR_HIT, hits);
659   vlib_node_increment_counter (vm, node->node_index,
660                                POLICER_CLASSIFY_ERROR_CHAIN_HIT, chain_hits);
661
662   return frame->n_vectors;
663 }
664
665 VLIB_NODE_FN (ip4_policer_classify_node) (vlib_main_t * vm,
666                                           vlib_node_runtime_t * node,
667                                           vlib_frame_t * frame)
668 {
669   return policer_classify_inline (vm, node, frame,
670                                   POLICER_CLASSIFY_TABLE_IP4);
671 }
672
673 /* *INDENT-OFF* */
674 VLIB_REGISTER_NODE (ip4_policer_classify_node) = {
675   .name = "ip4-policer-classify",
676   .vector_size = sizeof (u32),
677   .format_trace = format_policer_classify_trace,
678   .n_errors = ARRAY_LEN(policer_classify_error_strings),
679   .error_strings = policer_classify_error_strings,
680   .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
681   .next_nodes = {
682     [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
683   },
684 };
685 /* *INDENT-ON* */
686
687 VLIB_NODE_FN (ip6_policer_classify_node) (vlib_main_t * vm,
688                                           vlib_node_runtime_t * node,
689                                           vlib_frame_t * frame)
690 {
691   return policer_classify_inline (vm, node, frame,
692                                   POLICER_CLASSIFY_TABLE_IP6);
693 }
694
695 /* *INDENT-OFF* */
696 VLIB_REGISTER_NODE (ip6_policer_classify_node) = {
697   .name = "ip6-policer-classify",
698   .vector_size = sizeof (u32),
699   .format_trace = format_policer_classify_trace,
700   .n_errors = ARRAY_LEN(policer_classify_error_strings),
701   .error_strings = policer_classify_error_strings,
702   .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
703   .next_nodes = {
704     [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
705   },
706 };
707 /* *INDENT-ON* */
708
709 VLIB_NODE_FN (l2_policer_classify_node) (vlib_main_t * vm,
710                                          vlib_node_runtime_t * node,
711                                          vlib_frame_t * frame)
712 {
713   return policer_classify_inline (vm, node, frame, POLICER_CLASSIFY_TABLE_L2);
714 }
715
716 /* *INDENT-OFF* */
717 VLIB_REGISTER_NODE (l2_policer_classify_node) = {
718   .name = "l2-policer-classify",
719   .vector_size = sizeof (u32),
720   .format_trace = format_policer_classify_trace,
721   .n_errors = ARRAY_LEN (policer_classify_error_strings),
722   .error_strings = policer_classify_error_strings,
723   .n_next_nodes = POLICER_CLASSIFY_NEXT_INDEX_N_NEXT,
724   .next_nodes = {
725     [POLICER_CLASSIFY_NEXT_INDEX_DROP] = "error-drop",
726   },
727 };
728 /* *INDENT-ON* */
729
730 #ifndef CLIB_MARCH_VARIANT
731 static clib_error_t *
732 policer_classify_init (vlib_main_t * vm)
733 {
734   policer_classify_main_t *pcm = &policer_classify_main;
735
736   pcm->vlib_main = vm;
737   pcm->vnet_main = vnet_get_main ();
738   pcm->vnet_classify_main = &vnet_classify_main;
739
740   /* Initialize L2 feature next-node indexes */
741   feat_bitmap_init_next_nodes (vm,
742                                l2_policer_classify_node.index,
743                                L2INPUT_N_FEAT,
744                                l2input_get_feat_names (),
745                                pcm->feat_next_node_index);
746
747   return 0;
748 }
749
750 VLIB_INIT_FUNCTION (policer_classify_init);
751 #endif /* CLIB_MARCH_VARIANT */
752
753 /*
754  * fd.io coding-style-patch-verification: ON
755  *
756  * Local Variables:
757  * eval: (c-set-style "gnu")
758  * End:
759  */