policer: add api to configure input policing
[vpp.git] / src / vnet / policer / policer.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 #include <stdint.h>
16 #include <stdbool.h>
17 #include <vnet/policer/policer.h>
18 #include <vnet/policer/police_inlines.h>
19 #include <vnet/classify/vnet_classify.h>
20 #include <vnet/ip/ip_packet.h>
21
22 vnet_policer_main_t vnet_policer_main;
23
24 u8 *
25 format_policer_handoff_trace (u8 *s, va_list *args)
26 {
27   CLIB_UNUSED (vlib_main_t * vm) = va_arg (*args, vlib_main_t *);
28   CLIB_UNUSED (vlib_node_t * node) = va_arg (*args, vlib_node_t *);
29   policer_handoff_trace_t *t = va_arg (*args, policer_handoff_trace_t *);
30
31   s = format (s, "policer %d, handoff thread %d to %d", t->policer_index,
32               t->current_worker_index, t->next_worker_index);
33
34   return s;
35 }
36
37 vlib_combined_counter_main_t policer_counters[] = {
38   {
39     .name = "Policer-Conform",
40     .stat_segment_name = "/net/policer/conform",
41   },
42   {
43     .name = "Policer-Exceed",
44     .stat_segment_name = "/net/policer/exceed",
45   },
46   {
47     .name = "Policer-Violate",
48     .stat_segment_name = "/net/policer/violate",
49   },
50 };
51
52 clib_error_t *
53 policer_add_del (vlib_main_t *vm, u8 *name, qos_pol_cfg_params_st *cfg,
54                  u32 *policer_index, u8 is_add)
55 {
56   vnet_policer_main_t *pm = &vnet_policer_main;
57   policer_t test_policer;
58   policer_t *policer;
59   uword *p;
60   u32 pi;
61   int rv;
62
63   p = hash_get_mem (pm->policer_config_by_name, name);
64
65   if (is_add == 0)
66     {
67       /* free policer config and template */
68       if (p == 0)
69         {
70           vec_free (name);
71           return clib_error_return (0, "No such policer configuration");
72         }
73       pool_put_index (pm->configs, p[0]);
74       pool_put_index (pm->policer_templates, p[0]);
75       hash_unset_mem (pm->policer_config_by_name, name);
76
77       /* free policer */
78       p = hash_get_mem (pm->policer_index_by_name, name);
79       if (p == 0)
80         {
81           vec_free (name);
82           return clib_error_return (0, "No such policer");
83         }
84       pool_put_index (pm->policers, p[0]);
85       hash_unset_mem (pm->policer_index_by_name, name);
86
87       vec_free (name);
88       return 0;
89     }
90
91   if (p != 0)
92     {
93       vec_free (name);
94       return clib_error_return (0, "Policer already exists");
95     }
96
97   /* Vet the configuration before adding it to the table */
98   rv = pol_logical_2_physical (cfg, &test_policer);
99
100   if (rv == 0)
101     {
102       policer_t *pp;
103       qos_pol_cfg_params_st *cp;
104       int i;
105
106       pool_get (pm->configs, cp);
107       pool_get (pm->policer_templates, pp);
108
109       ASSERT (cp - pm->configs == pp - pm->policer_templates);
110
111       clib_memcpy (cp, cfg, sizeof (*cp));
112       clib_memcpy (pp, &test_policer, sizeof (*pp));
113
114       hash_set_mem (pm->policer_config_by_name, name, cp - pm->configs);
115       pool_get_aligned (pm->policers, policer, CLIB_CACHE_LINE_BYTES);
116       policer[0] = pp[0];
117       pi = policer - pm->policers;
118       hash_set_mem (pm->policer_index_by_name, name, pi);
119       *policer_index = pi;
120       policer->thread_index = ~0;
121
122       for (i = 0; i < NUM_POLICE_RESULTS; i++)
123         {
124           vlib_validate_combined_counter (&policer_counters[i], pi);
125           vlib_zero_combined_counter (&policer_counters[i], pi);
126         }
127     }
128   else
129     {
130       vec_free (name);
131       return clib_error_return (0, "Config failed sanity check");
132     }
133
134   return 0;
135 }
136
137 int
138 policer_bind_worker (u8 *name, u32 worker, bool bind)
139 {
140   vnet_policer_main_t *pm = &vnet_policer_main;
141   policer_t *policer;
142   uword *p;
143
144   p = hash_get_mem (pm->policer_index_by_name, name);
145   if (p == 0)
146     {
147       return VNET_API_ERROR_NO_SUCH_ENTRY;
148     }
149
150   policer = &pm->policers[p[0]];
151
152   if (bind)
153     {
154       if (worker >= vlib_num_workers ())
155         {
156           return VNET_API_ERROR_INVALID_WORKER;
157         }
158
159       policer->thread_index = vlib_get_worker_thread_index (worker);
160     }
161   else
162     {
163       policer->thread_index = ~0;
164     }
165   return 0;
166 }
167
168 int
169 policer_input (u8 *name, u32 sw_if_index, bool apply)
170 {
171   vnet_policer_main_t *pm = &vnet_policer_main;
172   policer_t *policer;
173   u32 policer_index;
174   uword *p;
175
176   p = hash_get_mem (pm->policer_index_by_name, name);
177   if (p == 0)
178     {
179       return VNET_API_ERROR_NO_SUCH_ENTRY;
180     }
181
182   policer = &pm->policers[p[0]];
183   policer_index = policer - pm->policers;
184
185   if (apply)
186     {
187       vec_validate (pm->policer_index_by_sw_if_index, sw_if_index);
188       pm->policer_index_by_sw_if_index[sw_if_index] = policer_index;
189     }
190   else
191     {
192       pm->policer_index_by_sw_if_index[sw_if_index] = ~0;
193     }
194
195   vnet_feature_enable_disable ("device-input", "policer-input", sw_if_index,
196                                apply, 0, 0);
197   return 0;
198 }
199
200 u8 *
201 format_policer_instance (u8 * s, va_list * va)
202 {
203   policer_t *i = va_arg (*va, policer_t *);
204   uword pi = va_arg (*va, uword);
205   int result;
206   vlib_counter_t counts[NUM_POLICE_RESULTS];
207
208   for (result = 0; result < NUM_POLICE_RESULTS; result++)
209     {
210       vlib_get_combined_counter (&policer_counters[result], pi,
211                                  &counts[result]);
212     }
213
214   s = format (s, "policer at %llx: %s rate, %s color-aware\n",
215               i, i->single_rate ? "single" : "dual",
216               i->color_aware ? "is" : "not");
217   s = format (s, "cir %u tok/period, pir %u tok/period, scale %u\n",
218               i->cir_tokens_per_period, i->pir_tokens_per_period, i->scale);
219   s = format (s, "cur lim %u, cur bkt %u, ext lim %u, ext bkt %u\n",
220               i->current_limit,
221               i->current_bucket, i->extended_limit, i->extended_bucket);
222   s = format (s, "last update %llu\n", i->last_update_time);
223   s = format (s, "conform %llu packets, %llu bytes\n",
224               counts[POLICE_CONFORM].packets, counts[POLICE_CONFORM].bytes);
225   s = format (s, "exceed %llu packets, %llu bytes\n",
226               counts[POLICE_EXCEED].packets, counts[POLICE_EXCEED].bytes);
227   s = format (s, "violate %llu packets, %llu bytes\n",
228               counts[POLICE_VIOLATE].packets, counts[POLICE_VIOLATE].bytes);
229   return s;
230 }
231
232 static u8 *
233 format_policer_round_type (u8 * s, va_list * va)
234 {
235   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
236
237   if (c->rnd_type == QOS_ROUND_TO_CLOSEST)
238     s = format (s, "closest");
239   else if (c->rnd_type == QOS_ROUND_TO_UP)
240     s = format (s, "up");
241   else if (c->rnd_type == QOS_ROUND_TO_DOWN)
242     s = format (s, "down");
243   else
244     s = format (s, "ILLEGAL");
245   return s;
246 }
247
248
249 static u8 *
250 format_policer_rate_type (u8 * s, va_list * va)
251 {
252   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
253
254   if (c->rate_type == QOS_RATE_KBPS)
255     s = format (s, "kbps");
256   else if (c->rate_type == QOS_RATE_PPS)
257     s = format (s, "pps");
258   else
259     s = format (s, "ILLEGAL");
260   return s;
261 }
262
263 static u8 *
264 format_policer_type (u8 * s, va_list * va)
265 {
266   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
267
268   if (c->rfc == QOS_POLICER_TYPE_1R2C)
269     s = format (s, "1r2c");
270
271   else if (c->rfc == QOS_POLICER_TYPE_1R3C_RFC_2697)
272     s = format (s, "1r3c");
273
274   else if (c->rfc == QOS_POLICER_TYPE_2R3C_RFC_2698)
275     s = format (s, "2r3c-2698");
276
277   else if (c->rfc == QOS_POLICER_TYPE_2R3C_RFC_4115)
278     s = format (s, "2r3c-4115");
279
280   else if (c->rfc == QOS_POLICER_TYPE_2R3C_RFC_MEF5CF1)
281     s = format (s, "2r3c-mef5cf1");
282   else
283     s = format (s, "ILLEGAL");
284   return s;
285 }
286
287 static u8 *
288 format_policer_action_type (u8 * s, va_list * va)
289 {
290   qos_pol_action_params_st *a = va_arg (*va, qos_pol_action_params_st *);
291
292   if (a->action_type == QOS_ACTION_DROP)
293     s = format (s, "drop");
294   else if (a->action_type == QOS_ACTION_TRANSMIT)
295     s = format (s, "transmit");
296   else if (a->action_type == QOS_ACTION_MARK_AND_TRANSMIT)
297     s = format (s, "mark-and-transmit %U", format_ip_dscp, a->dscp);
298   else
299     s = format (s, "ILLEGAL");
300   return s;
301 }
302
303 u8 *
304 format_policer_config (u8 * s, va_list * va)
305 {
306   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
307
308   s = format (s, "type %U cir %u eir %u cb %u eb %u\n",
309               format_policer_type, c,
310               c->rb.kbps.cir_kbps,
311               c->rb.kbps.eir_kbps, c->rb.kbps.cb_bytes, c->rb.kbps.eb_bytes);
312   s = format (s, "rate type %U, round type %U\n",
313               format_policer_rate_type, c, format_policer_round_type, c);
314   s = format (s, "conform action %U, exceed action %U, violate action %U\n",
315               format_policer_action_type, &c->conform_action,
316               format_policer_action_type, &c->exceed_action,
317               format_policer_action_type, &c->violate_action);
318   return s;
319 }
320
321 static uword
322 unformat_policer_type (unformat_input_t * input, va_list * va)
323 {
324   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
325
326   if (!unformat (input, "type"))
327     return 0;
328
329   if (unformat (input, "1r2c"))
330     c->rfc = QOS_POLICER_TYPE_1R2C;
331   else if (unformat (input, "1r3c"))
332     c->rfc = QOS_POLICER_TYPE_1R3C_RFC_2697;
333   else if (unformat (input, "2r3c-2698"))
334     c->rfc = QOS_POLICER_TYPE_2R3C_RFC_2698;
335   else if (unformat (input, "2r3c-4115"))
336     c->rfc = QOS_POLICER_TYPE_2R3C_RFC_4115;
337   else if (unformat (input, "2r3c-mef5cf1"))
338     c->rfc = QOS_POLICER_TYPE_2R3C_RFC_MEF5CF1;
339   else
340     return 0;
341   return 1;
342 }
343
344 static uword
345 unformat_policer_round_type (unformat_input_t * input, va_list * va)
346 {
347   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
348
349   if (!unformat (input, "round"))
350     return 0;
351
352   if (unformat (input, "closest"))
353     c->rnd_type = QOS_ROUND_TO_CLOSEST;
354   else if (unformat (input, "up"))
355     c->rnd_type = QOS_ROUND_TO_UP;
356   else if (unformat (input, "down"))
357     c->rnd_type = QOS_ROUND_TO_DOWN;
358   else
359     return 0;
360   return 1;
361 }
362
363 static uword
364 unformat_policer_rate_type (unformat_input_t * input, va_list * va)
365 {
366   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
367
368   if (!unformat (input, "rate"))
369     return 0;
370
371   if (unformat (input, "kbps"))
372     c->rate_type = QOS_RATE_KBPS;
373   else if (unformat (input, "pps"))
374     c->rate_type = QOS_RATE_PPS;
375   else
376     return 0;
377   return 1;
378 }
379
380 static uword
381 unformat_policer_cir (unformat_input_t * input, va_list * va)
382 {
383   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
384
385   if (unformat (input, "cir %u", &c->rb.kbps.cir_kbps))
386     return 1;
387   return 0;
388 }
389
390 static uword
391 unformat_policer_eir (unformat_input_t * input, va_list * va)
392 {
393   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
394
395   if (unformat (input, "eir %u", &c->rb.kbps.eir_kbps))
396     return 1;
397   return 0;
398 }
399
400 static uword
401 unformat_policer_cb (unformat_input_t * input, va_list * va)
402 {
403   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
404
405   if (unformat (input, "cb %u", &c->rb.kbps.cb_bytes))
406     return 1;
407   return 0;
408 }
409
410 static uword
411 unformat_policer_eb (unformat_input_t * input, va_list * va)
412 {
413   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
414
415   if (unformat (input, "eb %u", &c->rb.kbps.eb_bytes))
416     return 1;
417   return 0;
418 }
419
420 static uword
421 unformat_policer_action_type (unformat_input_t * input, va_list * va)
422 {
423   qos_pol_action_params_st *a = va_arg (*va, qos_pol_action_params_st *);
424
425   if (unformat (input, "drop"))
426     a->action_type = QOS_ACTION_DROP;
427   else if (unformat (input, "transmit"))
428     a->action_type = QOS_ACTION_TRANSMIT;
429   else if (unformat (input, "mark-and-transmit %U", unformat_ip_dscp,
430                      &a->dscp))
431     a->action_type = QOS_ACTION_MARK_AND_TRANSMIT;
432   else
433     return 0;
434   return 1;
435 }
436
437 static uword
438 unformat_policer_action (unformat_input_t * input, va_list * va)
439 {
440   qos_pol_cfg_params_st *c = va_arg (*va, qos_pol_cfg_params_st *);
441
442   if (unformat (input, "conform-action %U", unformat_policer_action_type,
443                 &c->conform_action))
444     return 1;
445   else if (unformat (input, "exceed-action %U", unformat_policer_action_type,
446                      &c->exceed_action))
447     return 1;
448   else if (unformat (input, "violate-action %U", unformat_policer_action_type,
449                      &c->violate_action))
450     return 1;
451   return 0;
452 }
453
454 static uword
455 unformat_policer_classify_next_index (unformat_input_t * input, va_list * va)
456 {
457   u32 *r = va_arg (*va, u32 *);
458   vnet_policer_main_t *pm = &vnet_policer_main;
459   uword *p;
460   u8 *match_name = 0;
461
462   if (unformat (input, "%s", &match_name))
463     ;
464   else
465     return 0;
466
467   p = hash_get_mem (pm->policer_index_by_name, match_name);
468
469   if (p == 0)
470     return 0;
471
472   *r = p[0];
473
474   return 1;
475 }
476
477 static uword
478 unformat_policer_classify_precolor (unformat_input_t * input, va_list * va)
479 {
480   u32 *r = va_arg (*va, u32 *);
481
482   if (unformat (input, "conform-color"))
483     *r = POLICE_CONFORM;
484   else if (unformat (input, "exceed-color"))
485     *r = POLICE_EXCEED;
486   else
487     return 0;
488
489   return 1;
490 }
491
492 #define foreach_config_param                    \
493 _(eb)                                           \
494 _(cb)                                           \
495 _(eir)                                          \
496 _(cir)                                          \
497 _(rate_type)                                    \
498 _(round_type)                                   \
499 _(type)                                         \
500 _(action)
501
502 static clib_error_t *
503 configure_policer_command_fn (vlib_main_t * vm,
504                               unformat_input_t * input,
505                               vlib_cli_command_t * cmd)
506 {
507   qos_pol_cfg_params_st c;
508   unformat_input_t _line_input, *line_input = &_line_input;
509   u8 is_add = 1;
510   u8 *name = 0;
511   u32 pi;
512   clib_error_t *error = NULL;
513
514   /* Get a line of input. */
515   if (!unformat_user (input, unformat_line_input, line_input))
516     return 0;
517
518   clib_memset (&c, 0, sizeof (c));
519
520   while (unformat_check_input (line_input) != UNFORMAT_END_OF_INPUT)
521     {
522       if (unformat (line_input, "del"))
523         is_add = 0;
524       else if (unformat (line_input, "name %s", &name))
525         ;
526       else if (unformat (line_input, "color-aware"))
527         c.color_aware = 1;
528
529 #define _(a) else if (unformat (line_input, "%U", unformat_policer_##a, &c)) ;
530       foreach_config_param
531 #undef _
532         else
533         {
534           error = clib_error_return (0, "unknown input `%U'",
535                                      format_unformat_error, line_input);
536           goto done;
537         }
538     }
539
540   error = policer_add_del (vm, name, &c, &pi, is_add);
541
542 done:
543   unformat_free (line_input);
544
545   return error;
546 }
547
548 /* *INDENT-OFF* */
549 VLIB_CLI_COMMAND (configure_policer_command, static) = {
550     .path = "configure policer",
551     .short_help = "configure policer name <name> <params> ",
552     .function = configure_policer_command_fn,
553 };
554 /* *INDENT-ON* */
555
556 static clib_error_t *
557 show_policer_command_fn (vlib_main_t * vm,
558                          unformat_input_t * input, vlib_cli_command_t * cmd)
559 {
560   vnet_policer_main_t *pm = &vnet_policer_main;
561   hash_pair_t *p;
562   u32 pool_index;
563   u8 *match_name = 0;
564   u8 *name;
565   uword *pi;
566   qos_pol_cfg_params_st *config;
567   policer_t *templ;
568
569   (void) unformat (input, "name %s", &match_name);
570
571   /* *INDENT-OFF* */
572   hash_foreach_pair (p, pm->policer_config_by_name,
573   ({
574     name = (u8 *) p->key;
575     if (match_name == 0 || !strcmp((char *) name, (char *) match_name))
576       {
577         pi = hash_get_mem (pm->policer_index_by_name, name);
578
579         pool_index = p->value[0];
580         config = pool_elt_at_index (pm->configs, pool_index);
581         templ = pool_elt_at_index (pm->policer_templates, pool_index);
582         vlib_cli_output (vm, "Name \"%s\" %U ", name, format_policer_config,
583                          config);
584         vlib_cli_output (vm, "Template %U", format_policer_instance, templ,
585                          pi[0]);
586         vlib_cli_output (vm, "-----------");
587       }
588   }));
589   /* *INDENT-ON* */
590   return 0;
591 }
592
593
594 /* *INDENT-OFF* */
595 VLIB_CLI_COMMAND (show_policer_command, static) = {
596     .path = "show policer",
597     .short_help = "show policer [name]",
598     .function = show_policer_command_fn,
599 };
600 /* *INDENT-ON* */
601
602 static clib_error_t *
603 show_policer_pools_command_fn (vlib_main_t * vm,
604                                unformat_input_t * input,
605                                vlib_cli_command_t * cmd)
606 {
607   vnet_policer_main_t *pm = &vnet_policer_main;
608
609   vlib_cli_output (vm, "pool sizes: configs=%d templates=%d policers=%d",
610                    pool_elts (pm->configs),
611                    pool_elts (pm->policer_templates),
612                    pool_elts (pm->policers));
613   return 0;
614 }
615 /* *INDENT-OFF* */
616 VLIB_CLI_COMMAND (show_policer_pools_command, static) = {
617     .path = "show policer pools",
618     .short_help = "show policer pools",
619     .function = show_policer_pools_command_fn,
620 };
621 /* *INDENT-ON* */
622
623 clib_error_t *
624 policer_init (vlib_main_t * vm)
625 {
626   vnet_policer_main_t *pm = &vnet_policer_main;
627
628   pm->vlib_main = vm;
629   pm->vnet_main = vnet_get_main ();
630   pm->log_class = vlib_log_register_class ("policer", 0);
631
632   pm->policer_config_by_name = hash_create_string (0, sizeof (uword));
633   pm->policer_index_by_name = hash_create_string (0, sizeof (uword));
634
635   vnet_classify_register_unformat_policer_next_index_fn
636     (unformat_policer_classify_next_index);
637   vnet_classify_register_unformat_opaque_index_fn
638     (unformat_policer_classify_precolor);
639
640   return 0;
641 }
642
643 VLIB_INIT_FUNCTION (policer_init);
644
645
646
647 /*
648  * fd.io coding-style-patch-verification: ON
649  *
650  * Local Variables:
651  * eval: (c-set-style "gnu")
652  * End:
653  */