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