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