Line data Source code
1 : /*
2 : * Copyright (c) 2019 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 <vlib/punt.h>
17 :
18 : /**
19 : * The last allocated punt reason
20 : * Value 0 is reserved for invalid index.
21 : */
22 : static vlib_punt_reason_t punt_reason_last = 1;
23 :
24 : /**
25 : * Counters per punt-reason
26 : */
27 : vlib_combined_counter_main_t punt_counters = {
28 : .name = "punt",
29 : .stat_segment_name = "/net/punt",
30 : };
31 :
32 : /**
33 : * A punt reason
34 : */
35 : typedef struct punt_reason_data_t_
36 : {
37 : /**
38 : * The reason name
39 : */
40 : u8 *pd_name;
41 :
42 : /**
43 : * The allocated reason value
44 : */
45 : vlib_punt_reason_t pd_reason;
46 :
47 : /**
48 : * Clients/owners that have registered this reason
49 : */
50 : u32 *pd_owners;
51 :
52 : /**
53 : * clients interested/listening to this reason
54 : */
55 : u32 pd_users;
56 :
57 : /**
58 : * function to invoke if a client becomes interested in the code.
59 : */
60 : punt_interested_listener_t pd_fn;
61 :
62 : /**
63 : * Data to pass to the callback
64 : */
65 : void *pd_data;
66 :
67 : /**
68 : * Flags associated to the reason
69 : */
70 : u32 flags;
71 :
72 : /**
73 : * Formatting function for flags;
74 : */
75 : format_function_t *flags_format;
76 : } punt_reason_data_t;
77 :
78 : /**
79 : * data for each punt reason
80 : */
81 : static punt_reason_data_t *punt_reason_data;
82 :
83 : typedef enum punt_format_flags_t_
84 : {
85 : PUNT_FORMAT_FLAG_NONE = 0,
86 : PUNT_FORMAT_FLAG_DETAIL = (1 << 0),
87 : } punt_format_flags_t;
88 :
89 : /**
90 : * A registration, by a client, to direct punted traffic to a given node
91 : */
92 : typedef struct punt_reg_t_
93 : {
94 : /**
95 : * Reason the packets were punted
96 : */
97 : vlib_punt_reason_t pr_reason;
98 :
99 : /**
100 : * number of clients that have made this registration
101 : */
102 : u16 pr_locks;
103 :
104 : /**
105 : * The edge from the punt dispatch node to the requested node
106 : */
107 : u16 pr_edge;
108 :
109 : /**
110 : * node-index to send punted packets to
111 : */
112 : u32 pr_node_index;
113 : } punt_reg_t;
114 :
115 : /**
116 : * Pool of registrations
117 : */
118 : static punt_reg_t *punt_reg_pool;
119 :
120 : /**
121 : * A DB of all the register nodes against punt reason and node index
122 : */
123 : static uword *punt_reg_db;
124 :
125 : /**
126 : * A DB used in the DP per-reason to dispatch packets to the requested nodes.
127 : * this is a vector of edges per-reason
128 : */
129 : u16 **punt_dp_db;
130 :
131 : /**
132 : * A client using the punt serivce and its registrations
133 : */
134 : typedef struct punt_client_t_
135 : {
136 : /**
137 : * The name of the client
138 : */
139 : u8 *pc_name;
140 :
141 : /**
142 : * The registrations is has made
143 : */
144 : u32 *pc_regs;
145 : } punt_client_t;
146 :
147 : /**
148 : * Pool of clients
149 : */
150 : static punt_client_t *punt_client_pool;
151 :
152 : /**
153 : * DB of clients key'd by their name
154 : */
155 : static uword *punt_client_db;
156 :
157 : u8 *
158 1720 : format_vlib_punt_reason (u8 * s, va_list * args)
159 : {
160 1720 : vlib_punt_reason_t pr = va_arg (*args, int);
161 1720 : format_function_t *flags_format = punt_reason_data[pr].flags_format;
162 1720 : u32 flags = punt_reason_data[pr].flags;
163 1720 : if (flags_format)
164 1715 : return (format (s, "[%d] %v flags: %U", pr, punt_reason_data[pr].pd_name,
165 : flags_format, flags));
166 : else
167 5 : return (format (s, "[%d] %v", pr, punt_reason_data[pr].pd_name));
168 : }
169 :
170 : vlib_punt_hdl_t
171 1174 : vlib_punt_client_register (const char *who)
172 : {
173 : u8 *pc_name;
174 : uword *p;
175 : u32 pci;
176 :
177 1174 : pc_name = format (NULL, "%s", who);
178 2348 : p = hash_get_mem (punt_client_db, pc_name);
179 :
180 1174 : if (NULL == p)
181 : {
182 : punt_client_t *pc;
183 :
184 1174 : pool_get (punt_client_pool, pc);
185 1174 : pci = pc - punt_client_pool;
186 :
187 1174 : pc->pc_name = pc_name;
188 :
189 2348 : hash_set_mem (punt_client_db, pc->pc_name, pci);
190 : }
191 : else
192 : {
193 0 : pci = p[0];
194 0 : vec_free (pc_name);
195 : }
196 :
197 1174 : return (pci);
198 : }
199 :
200 : static int
201 2336 : punt_validate_client (vlib_punt_hdl_t client)
202 : {
203 2336 : return (!pool_is_free_index (punt_client_pool, client));
204 : }
205 :
206 : static u64
207 104 : punt_reg_mk_key (vlib_punt_reason_t reason, u32 node_index)
208 : {
209 104 : return (((u64) node_index) << 32 | reason);
210 : }
211 :
212 : static u32
213 53 : punt_reg_find (vlib_punt_reason_t reason, u32 node_index)
214 : {
215 : uword *p;
216 :
217 53 : p = hash_get (punt_reg_db, punt_reg_mk_key (reason, node_index));
218 :
219 53 : if (p)
220 21 : return p[0];
221 :
222 32 : return ~0;
223 : }
224 :
225 : static void
226 32 : punt_reg_add (const punt_reg_t * pr)
227 : {
228 32 : hash_set (punt_reg_db, punt_reg_mk_key (pr->pr_reason,
229 : pr->pr_node_index),
230 : pr - punt_reg_pool);
231 32 : }
232 :
233 : static void
234 19 : punt_reg_remove (const punt_reg_t * pr)
235 : {
236 19 : hash_unset (punt_reg_db, punt_reg_mk_key (pr->pr_reason,
237 : pr->pr_node_index));
238 19 : }
239 :
240 : /**
241 : * reconstruct the DP per-reason DB
242 : */
243 : static void
244 2353 : punt_reg_mk_dp (vlib_punt_reason_t reason)
245 : {
246 : u32 pri, *prip, *pris;
247 : const punt_reg_t *pr;
248 : u16 *edges, *old;
249 : u64 key;
250 :
251 2353 : pris = NULL;
252 2353 : edges = NULL;
253 2353 : vec_validate (punt_dp_db, reason);
254 :
255 2353 : old = punt_dp_db[reason];
256 :
257 : /* *INDENT-OFF* */
258 5660 : hash_foreach (key, pri, punt_reg_db,
259 : ({
260 : vec_add1(pris, pri);
261 : }));
262 : /* *INDENT-ON* */
263 :
264 : /*
265 : * A check for an empty vector is done in the DP, so the a zero
266 : * length vector here is ok
267 : */
268 2396 : vec_foreach (prip, pris)
269 : {
270 43 : pr = pool_elt_at_index (punt_reg_pool, *prip);
271 :
272 43 : if (pr->pr_reason == reason)
273 34 : vec_add1 (edges, pr->pr_edge);
274 : }
275 :
276 : /* atomic update of the DP */
277 2353 : punt_dp_db[reason] = edges;
278 :
279 2353 : vec_free (old);
280 2353 : }
281 :
282 : int
283 34 : vlib_punt_register (vlib_punt_hdl_t client,
284 : vlib_punt_reason_t reason, const char *node_name)
285 : {
286 : vlib_node_t *punt_to, *punt_from;
287 : punt_client_t *pc;
288 : vlib_main_t *vm;
289 : punt_reg_t *pr;
290 : u32 pri;
291 :
292 34 : if (reason >= punt_reason_last)
293 0 : return -1;
294 34 : if (!punt_validate_client (client))
295 0 : return -2;
296 :
297 34 : vm = vlib_get_main ();
298 34 : pc = pool_elt_at_index (punt_client_pool, client);
299 34 : punt_to = vlib_get_node_by_name (vm, (u8 *) node_name);
300 34 : punt_from = vlib_get_node_by_name (vm, (u8 *) "punt-dispatch");
301 :
302 : /*
303 : * find a global matching registration
304 : */
305 34 : pri = punt_reg_find (reason, punt_to->index);
306 :
307 34 : if (~0 != pri)
308 : {
309 : u32 pos;
310 :
311 3 : pos = vec_search (pc->pc_regs, pri);
312 :
313 2 : if (~0 != pos)
314 : {
315 : /* duplicate registration for this client */
316 2 : return -1;
317 : }
318 :
319 0 : pr = pool_elt_at_index (punt_reg_pool, pri);
320 : }
321 : else
322 : {
323 32 : pool_get (punt_reg_pool, pr);
324 :
325 32 : pr->pr_reason = reason;
326 32 : pr->pr_node_index = punt_to->index;
327 64 : pr->pr_edge = vlib_node_add_next (vm,
328 32 : punt_from->index, pr->pr_node_index);
329 :
330 32 : pri = pr - punt_reg_pool;
331 :
332 32 : if (0 == punt_reason_data[reason].pd_users++ &&
333 30 : NULL != punt_reason_data[reason].pd_fn)
334 28 : punt_reason_data[reason].pd_fn (VLIB_ENABLE,
335 28 : punt_reason_data[reason].pd_data);
336 :
337 32 : punt_reg_add (pr);
338 : }
339 :
340 : /*
341 : * add this reg to the list the client has made
342 : */
343 32 : pr->pr_locks++;
344 32 : vec_add1 (pc->pc_regs, pri);
345 :
346 32 : punt_reg_mk_dp (reason);
347 :
348 32 : return 0;
349 : }
350 :
351 : int
352 19 : vlib_punt_unregister (vlib_punt_hdl_t client,
353 : vlib_punt_reason_t reason, const char *node_name)
354 : {
355 : vlib_node_t *punt_to;
356 : punt_client_t *pc;
357 : vlib_main_t *vm;
358 : punt_reg_t *pr;
359 : u32 pri;
360 :
361 19 : if (reason >= punt_reason_last)
362 0 : return -1;
363 :
364 19 : vm = vlib_get_main ();
365 19 : pc = pool_elt_at_index (punt_client_pool, client);
366 19 : punt_to = vlib_get_node_by_name (vm, (u8 *) node_name);
367 :
368 : /*
369 : * construct a registration and check if it's one this client already has
370 : */
371 19 : pri = punt_reg_find (reason, punt_to->index);
372 :
373 19 : if (~0 != pri)
374 : {
375 : u32 pos;
376 :
377 19 : pos = vec_search (pc->pc_regs, pri);
378 :
379 19 : if (~0 == pos)
380 : {
381 : /* not a registration for this client */
382 0 : return -1;
383 : }
384 19 : vec_del1 (pc->pc_regs, pos);
385 :
386 19 : pr = pool_elt_at_index (punt_reg_pool, pri);
387 :
388 19 : pr->pr_locks--;
389 :
390 19 : if (0 == pr->pr_locks)
391 : {
392 19 : if (0 == --punt_reason_data[reason].pd_users &&
393 19 : NULL != punt_reason_data[reason].pd_fn)
394 19 : punt_reason_data[reason].pd_fn (VLIB_DISABLE,
395 19 : punt_reason_data[reason].pd_data);
396 19 : punt_reg_remove (pr);
397 19 : pool_put (punt_reg_pool, pr);
398 : }
399 : }
400 :
401 : /*
402 : * rebuild the DP data-base
403 : */
404 19 : punt_reg_mk_dp (reason);
405 :
406 19 : return (0);
407 : }
408 :
409 : int
410 10 : vlib_punt_reason_validate (vlib_punt_reason_t reason)
411 : {
412 10 : if (reason < punt_reason_last)
413 10 : return (0);
414 :
415 0 : return (-1);
416 : }
417 :
418 : u32
419 0 : vlib_punt_reason_get_flags (vlib_punt_reason_t pr)
420 : {
421 0 : return pr < punt_reason_last ? punt_reason_data[pr].flags : 0;
422 : }
423 :
424 : int
425 2302 : vlib_punt_reason_alloc (vlib_punt_hdl_t client, const char *reason_name,
426 : punt_interested_listener_t fn, void *data,
427 : vlib_punt_reason_t *reason, u32 flags,
428 : format_function_t *flags_format)
429 : {
430 : vlib_punt_reason_t new;
431 :
432 2302 : if (!punt_validate_client (client))
433 0 : return -2;
434 :
435 2302 : new = punt_reason_last++;
436 2302 : vec_validate (punt_reason_data, new);
437 2302 : punt_reason_data[new].pd_name = format (NULL, "%s", reason_name);
438 2302 : punt_reason_data[new].pd_reason = new;
439 2302 : punt_reason_data[new].pd_fn = fn;
440 2302 : punt_reason_data[new].pd_data = data;
441 2302 : punt_reason_data[new].flags = flags;
442 2302 : punt_reason_data[new].flags_format = flags_format;
443 2302 : vec_add1 (punt_reason_data[new].pd_owners, client);
444 :
445 2302 : vlib_validate_combined_counter (&punt_counters, new);
446 2302 : vlib_zero_combined_counter (&punt_counters, new);
447 :
448 2302 : *reason = new;
449 :
450 : /* build the DP data-base */
451 2302 : punt_reg_mk_dp (*reason);
452 :
453 2302 : return (0);
454 : }
455 :
456 : void
457 4 : punt_reason_walk (punt_reason_walk_cb_t cb, void *ctx)
458 : {
459 : punt_reason_data_t *pd;
460 :
461 24 : for (pd = punt_reason_data + 1; pd < vec_end (punt_reason_data); pd++)
462 : {
463 20 : cb (pd->pd_reason, pd->pd_name, ctx);
464 : }
465 4 : }
466 :
467 : /* Parse node name -> node index. */
468 : uword
469 0 : unformat_punt_client (unformat_input_t * input, va_list * args)
470 : {
471 0 : u32 *result = va_arg (*args, u32 *);
472 :
473 0 : return unformat_user (input, unformat_hash_vec_string,
474 : punt_client_db, result);
475 : }
476 :
477 : /* Parse punt reason */
478 : uword
479 0 : unformat_punt_reason (unformat_input_t *input, va_list *args)
480 : {
481 0 : u32 *result = va_arg (*args, u32 *);
482 0 : u8 *s = 0;
483 0 : u8 found = 0;
484 0 : for (int i = 0; i < punt_reason_last - 1; i++)
485 : {
486 0 : punt_reason_data_t *pd = vec_elt_at_index (punt_reason_data, 1 + i);
487 0 : vec_reset_length (s);
488 0 : s = format (0, "%v%c", pd->pd_name, 0);
489 0 : if (unformat (input, (const char *) s))
490 : {
491 0 : *result = pd->pd_reason;
492 0 : found = 1;
493 0 : break;
494 : }
495 : }
496 0 : vec_free (s);
497 0 : return found;
498 : }
499 :
500 : u8 *
501 4 : format_punt_reg (u8 * s, va_list * args)
502 : {
503 4 : u32 pri = va_arg (*args, u32);
504 : punt_reg_t *pr;
505 :
506 4 : pr = pool_elt_at_index (punt_reg_pool, pri);
507 :
508 4 : s = format (s, "%U -> %U",
509 4 : format_vlib_punt_reason, pr->pr_reason,
510 : format_vlib_node_name, vlib_get_main (), pr->pr_node_index);
511 :
512 4 : return (s);
513 : }
514 :
515 : u8 *
516 7 : format_punt_reason_data (u8 * s, va_list * args)
517 : {
518 7 : punt_reason_data_t *pd = va_arg (*args, punt_reason_data_t *);
519 : punt_client_t *pc;
520 : u32 *pci;
521 7 : if (pd->flags_format)
522 6 : s = format (s, "[%d] %v flags: %U from:[", pd->pd_reason, pd->pd_name,
523 : pd->flags_format, pd->flags);
524 : else
525 1 : s = format (s, "[%d] %v from:[", pd->pd_reason, pd->pd_name);
526 13 : vec_foreach (pci, pd->pd_owners)
527 : {
528 6 : pc = pool_elt_at_index (punt_client_pool, *pci);
529 6 : s = format (s, "%v ", pc->pc_name);
530 : }
531 7 : s = format (s, "]");
532 :
533 7 : return (s);
534 : }
535 :
536 : u8 *
537 3 : format_punt_client (u8 * s, va_list * args)
538 : {
539 3 : u32 pci = va_arg (*args, u32);
540 3 : punt_format_flags_t flags = va_arg (*args, punt_format_flags_t);
541 : punt_client_t *pc;
542 :
543 3 : pc = pool_elt_at_index (punt_client_pool, pci);
544 :
545 3 : s = format (s, "%v", pc->pc_name);
546 :
547 3 : if (flags & PUNT_FORMAT_FLAG_DETAIL)
548 : {
549 : punt_reason_data_t *pd;
550 : u32 *pri;
551 :
552 0 : s = format (s, "\n registrations:");
553 0 : vec_foreach (pri, pc->pc_regs)
554 : {
555 0 : s = format (s, "\n [%U]", format_punt_reg, *pri);
556 : }
557 :
558 0 : s = format (s, "\n reasons:");
559 :
560 0 : vec_foreach (pd, punt_reason_data)
561 : {
562 : u32 *tmp;
563 :
564 0 : vec_foreach (tmp, pd->pd_owners)
565 : {
566 0 : if (*tmp == pci)
567 0 : s = format (s, "\n %U", format_punt_reason_data, pd);
568 : }
569 : }
570 : }
571 3 : return (s);
572 : }
573 :
574 : static clib_error_t *
575 1 : punt_client_show (vlib_main_t * vm,
576 : unformat_input_t * input, vlib_cli_command_t * cmd)
577 : {
578 1 : u32 pci = ~0;
579 :
580 1 : while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
581 : {
582 0 : if (unformat (input, "%U", unformat_punt_client, &pci))
583 : ;
584 : else
585 0 : break;
586 : }
587 :
588 1 : if (~0 != pci)
589 : {
590 0 : vlib_cli_output (vm, "%U", format_punt_client, pci,
591 : PUNT_FORMAT_FLAG_DETAIL);
592 : }
593 : else
594 : {
595 : u8 *name;
596 :
597 : /* *INDENT-OFF* */
598 68 : hash_foreach(name, pci, punt_client_db,
599 : ({
600 : vlib_cli_output (vm, "%U", format_punt_client, pci,
601 : PUNT_FORMAT_FLAG_NONE);
602 : }));
603 : /* *INDENT-ON* */
604 : }
605 :
606 1 : return (NULL);
607 : }
608 :
609 : /* *INDENT-OFF* */
610 285289 : VLIB_CLI_COMMAND (punt_client_show_command, static) =
611 : {
612 : .path = "show punt client",
613 : .short_help = "show client[s] registered with the punt infra",
614 : .function = punt_client_show,
615 : };
616 : /* *INDENT-ON* */
617 :
618 : static clib_error_t *
619 1 : punt_reason_show (vlib_main_t * vm,
620 : unformat_input_t * input, vlib_cli_command_t * cmd)
621 : {
622 : const punt_reason_data_t *pd;
623 :
624 8 : vec_foreach (pd, punt_reason_data)
625 : {
626 7 : vlib_cli_output (vm, "%U", format_punt_reason_data, pd);
627 : }
628 :
629 1 : return (NULL);
630 : }
631 :
632 : /* *INDENT-OFF* */
633 285289 : VLIB_CLI_COMMAND (punt_reason_show_command, static) =
634 : {
635 : .path = "show punt reasons",
636 : .short_help = "show all punt reasons",
637 : .function = punt_reason_show,
638 : };
639 : /* *INDENT-ON* */
640 :
641 : static clib_error_t *
642 1 : punt_db_show (vlib_main_t * vm,
643 : unformat_input_t * input, vlib_cli_command_t * cmd)
644 : {
645 : u32 pri, ii, jj;
646 : u64 key;
647 :
648 : /* *INDENT-OFF* */
649 69 : hash_foreach (key, pri, punt_reg_db,
650 : ({
651 : vlib_cli_output (vm, " %U", format_punt_reg, pri);
652 : }));
653 : /* *INDENT-ON* */
654 :
655 1 : vlib_cli_output (vm, "\nDerived data-plane data-base:");
656 1 : vlib_cli_output (vm,
657 : " (for each punt-reason the edge[s] from punt-dispatch)");
658 :
659 8 : vec_foreach_index (ii, punt_dp_db)
660 : {
661 7 : u8 *s = NULL;
662 7 : vlib_cli_output (vm, " %U", format_vlib_punt_reason, ii);
663 :
664 11 : vec_foreach_index (jj, punt_dp_db[ii])
665 : {
666 4 : s = format (s, "%d ", punt_dp_db[ii][jj]);
667 : }
668 7 : vlib_cli_output (vm, " [%v]", s);
669 7 : vec_free (s);
670 : }
671 :
672 1 : return (NULL);
673 : }
674 :
675 : /* *INDENT-OFF* */
676 285289 : VLIB_CLI_COMMAND (punt_db_show_command, static) =
677 : {
678 : .path = "show punt db",
679 : .short_help = "show the punt DB",
680 : .function = punt_db_show,
681 : };
682 : /* *INDENT-ON* */
683 :
684 : static clib_error_t *
685 4 : punt_stats_show (vlib_main_t * vm,
686 : unformat_input_t * input, vlib_cli_command_t * cmd)
687 : {
688 4 : vlib_combined_counter_main_t *cm = &punt_counters;
689 : vlib_counter_t c;
690 : u32 ii;
691 :
692 26 : for (ii = 0; ii < vlib_combined_counter_n_counters (cm); ii++)
693 : {
694 22 : vlib_get_combined_counter (cm, ii, &c);
695 22 : vlib_cli_output (vm, "%U packets:%lld bytes:%lld",
696 : format_vlib_punt_reason, ii, c.packets, c.bytes);
697 : }
698 :
699 4 : return (NULL);
700 : }
701 :
702 : /* *INDENT-OFF* */
703 285289 : VLIB_CLI_COMMAND (punt_stats_show_command, static) =
704 : {
705 : .path = "show punt stats",
706 : .short_help = "show the punt stats",
707 : .function = punt_stats_show,
708 : };
709 : /* *INDENT-ON* */
710 :
711 : static clib_error_t *
712 575 : punt_init (vlib_main_t * vm)
713 : {
714 575 : punt_client_db = hash_create_vec (0, sizeof (u8), sizeof (u32));
715 :
716 575 : return (NULL);
717 : }
718 :
719 2879 : VLIB_INIT_FUNCTION (punt_init);
720 :
721 : /*
722 : * fd.io coding-style-patch-verification: ON
723 : *
724 : * Local Variables:
725 : * eval: (c-set-style "gnu")
726 : * End:
727 : */
|