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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include <vlib/punt.h>
19 * The last allocated punt reason
21 static vlib_punt_reason_t punt_reason_last;
24 * Counters per punt-reason
26 vlib_combined_counter_main_t punt_counters = {
28 .stat_segment_name = "/net/punt",
34 typedef struct punt_reason_data_t_
42 * The allocated reason value
44 vlib_punt_reason_t pd_reason;
47 * Clients/owners that have registered this reason
53 * data for each punt reason
55 static punt_reason_data_t *punt_reason_data;
57 typedef enum punt_format_flags_t_
59 PUNT_FORMAT_FLAG_NONE = 0,
60 PUNT_FORMAT_FLAG_DETAIL = (1 << 0),
61 } punt_format_flags_t;
64 * A registration, by a client, to direct punted traffic to a given node
66 typedef struct punt_reg_t_
69 * Reason the packets were punted
71 vlib_punt_reason_t pr_reason;
74 * number of clients that have made this registration
79 * The edge from the punt dispatch node to the requested node
84 * node-index to send punted packets to
90 * Pool of registrations
92 static punt_reg_t *punt_reg_pool;
95 * A DB of all the register nodes against punt reason and node index
97 static uword *punt_reg_db;
100 * A DB used in the DP per-reason to dispatch packets to the requested nodes.
101 * this is a vector of edges per-reason
106 * A client using the punt serivce and its registrations
108 typedef struct punt_client_t_
111 * The name of the client
116 * The registrations is has made
124 static punt_client_t *punt_client_pool;
127 * DB of clients key'd by their name
129 static uword *punt_client_db;
132 format_vlib_punt_reason (u8 * s, va_list * args)
134 vlib_punt_reason_t pr = va_arg (*args, int);
136 return (format (s, "[%d] %v", pr, punt_reason_data[pr].pd_name));
140 vlib_punt_client_register (const char *who)
146 pc_name = format (NULL, "%s", who);
147 p = hash_get_mem (punt_client_db, pc_name);
153 pool_get (punt_client_pool, pc);
154 pci = pc - punt_client_pool;
156 pc->pc_name = pc_name;
158 hash_set_mem (punt_client_db, pc->pc_name, pci);
170 punt_validate_client (vlib_punt_hdl_t client)
172 return (!pool_is_free_index (punt_client_pool, client));
176 punt_reg_mk_key (vlib_punt_reason_t reason, u32 node_index)
178 return (((u64) node_index) << 32 | reason);
182 punt_reg_find (vlib_punt_reason_t reason, u32 node_index)
186 p = hash_get (punt_reg_db, punt_reg_mk_key (reason, node_index));
195 punt_reg_add (const punt_reg_t * pr)
197 hash_set (punt_reg_db, punt_reg_mk_key (pr->pr_reason,
203 punt_reg_remove (const punt_reg_t * pr)
205 hash_unset (punt_reg_db, punt_reg_mk_key (pr->pr_reason,
210 * reconstruct the DP per-reason DB
213 punt_reg_mk_dp (vlib_punt_reason_t reason)
215 u32 pri, *prip, *pris;
216 const punt_reg_t *pr;
222 vec_validate (punt_dp_db, reason);
224 old = punt_dp_db[reason];
227 hash_foreach (key, pri, punt_reg_db,
234 * A check for an empty vector is done in the DP, so the a zero
235 * length vector here is ok
237 vec_foreach (prip, pris)
239 pr = pool_elt_at_index (punt_reg_pool, *prip);
241 if (pr->pr_reason == reason)
242 vec_add1 (edges, pr->pr_edge);
245 /* atomic update of the DP */
246 punt_dp_db[reason] = edges;
252 vlib_punt_register (vlib_punt_hdl_t client, vlib_punt_reason_t reason,
253 const char *node_name)
255 vlib_node_t *punt_to, *punt_from;
261 if (reason >= punt_reason_last)
263 if (!punt_validate_client (client))
266 vm = vlib_get_main ();
267 pc = pool_elt_at_index (punt_client_pool, client);
268 punt_to = vlib_get_node_by_name (vm, (u8 *) node_name);
269 punt_from = vlib_get_node_by_name (vm, (u8 *) "punt-dispatch");
272 * find a global matching registration
274 pri = punt_reg_find (reason, punt_to->index);
280 pos = vec_search (pc->pc_regs, pri);
284 /* duplicate registration for this client */
288 pr = pool_elt_at_index (punt_reg_pool, pri);
292 pool_get (punt_reg_pool, pr);
294 pr->pr_reason = reason;
295 pr->pr_node_index = punt_to->index;
296 pr->pr_edge = vlib_node_add_next (vm,
297 punt_from->index, pr->pr_node_index);
299 pri = pr - punt_reg_pool;
305 * add this reg to the list the client has made
308 vec_add1 (pc->pc_regs, pri);
310 punt_reg_mk_dp (reason);
316 vlib_punt_unregister (vlib_punt_hdl_t client,
317 vlib_punt_reason_t reason, const char *node_name)
319 vlib_node_t *punt_to;
325 if (reason >= punt_reason_last)
328 vm = vlib_get_main ();
329 pc = pool_elt_at_index (punt_client_pool, client);
330 punt_to = vlib_get_node_by_name (vm, (u8 *) node_name);
333 * construct a registration and check if it's one this client already has
335 pri = punt_reg_find (reason, punt_to->index);
341 pos = vec_search (pc->pc_regs, pri);
345 /* not a registration for this client */
348 vec_del1 (pc->pc_regs, pos);
350 pr = pool_elt_at_index (punt_reg_pool, pri);
354 if (0 == pr->pr_locks)
356 punt_reg_remove (pr);
357 pool_put (punt_reg_pool, pr);
362 * rebuild the DP data-base
364 punt_reg_mk_dp (reason);
370 vlib_punt_reason_alloc (vlib_punt_hdl_t client,
371 const char *reason_name, vlib_punt_reason_t * reason)
373 vlib_punt_reason_t new;
375 if (!punt_validate_client (client))
378 new = punt_reason_last++;
379 vec_validate (punt_reason_data, new);
380 punt_reason_data[new].pd_name = format (NULL, "%s", reason_name);
381 punt_reason_data[new].pd_reason = new;
382 vec_add1 (punt_reason_data[new].pd_owners, client);
384 vlib_validate_combined_counter (&punt_counters, new);
385 vlib_zero_combined_counter (&punt_counters, new);
389 /* build the DP data-base */
390 punt_reg_mk_dp (*reason);
395 /* Parse node name -> node index. */
397 unformat_punt_client (unformat_input_t * input, va_list * args)
399 u32 *result = va_arg (*args, u32 *);
401 return unformat_user (input, unformat_hash_vec_string,
402 punt_client_db, result);
406 format_punt_reg (u8 * s, va_list * args)
408 u32 pri = va_arg (*args, u32);
411 pr = pool_elt_at_index (punt_reg_pool, pri);
413 s = format (s, "%U -> %U",
414 format_vlib_punt_reason, pr->pr_reason,
415 format_vlib_node_name, vlib_get_main (), pr->pr_node_index);
421 format_punt_reason_data (u8 * s, va_list * args)
423 punt_reason_data_t *pd = va_arg (*args, punt_reason_data_t *);
427 s = format (s, "[%d] %v from:[", pd->pd_reason, pd->pd_name);
428 vec_foreach (pci, pd->pd_owners)
430 pc = pool_elt_at_index (punt_client_pool, *pci);
431 s = format (s, "%v ", pc->pc_name);
439 format_punt_client (u8 * s, va_list * args)
441 u32 pci = va_arg (*args, u32);
442 punt_format_flags_t flags = va_arg (*args, punt_format_flags_t);
445 pc = pool_elt_at_index (punt_client_pool, pci);
447 s = format (s, "%v", pc->pc_name);
449 if (flags & PUNT_FORMAT_FLAG_DETAIL)
451 punt_reason_data_t *pd;
454 s = format (s, "\n registrations:");
455 vec_foreach (pri, pc->pc_regs)
457 s = format (s, "\n [%U]", format_punt_reg, *pri);
460 s = format (s, "\n reasons:");
462 vec_foreach (pd, punt_reason_data)
466 vec_foreach (tmp, pd->pd_owners)
469 s = format (s, "\n %U", format_punt_reason_data, pd);
476 static clib_error_t *
477 punt_client_show (vlib_main_t * vm,
478 unformat_input_t * input, vlib_cli_command_t * cmd)
482 while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
484 if (unformat (input, "%U", unformat_punt_client, &pci))
492 vlib_cli_output (vm, "%U", format_punt_client, pci,
493 PUNT_FORMAT_FLAG_DETAIL);
500 hash_foreach(name, pci, punt_client_db,
502 vlib_cli_output (vm, "%U", format_punt_client, pci,
503 PUNT_FORMAT_FLAG_NONE);
512 VLIB_CLI_COMMAND (punt_client_show_command, static) =
514 .path = "show punt client",
515 .short_help = "show client[s] registered with the punt infra",
516 .function = punt_client_show,
520 static clib_error_t *
521 punt_reason_show (vlib_main_t * vm,
522 unformat_input_t * input, vlib_cli_command_t * cmd)
524 const punt_reason_data_t *pd;
526 vec_foreach (pd, punt_reason_data)
528 vlib_cli_output (vm, "%U", format_punt_reason_data, pd);
535 VLIB_CLI_COMMAND (punt_reason_show_command, static) =
537 .path = "show punt reasons",
538 .short_help = "show all punt reasons",
539 .function = punt_reason_show,
543 static clib_error_t *
544 punt_db_show (vlib_main_t * vm,
545 unformat_input_t * input, vlib_cli_command_t * cmd)
551 hash_foreach (key, pri, punt_reg_db,
553 vlib_cli_output (vm, " %U", format_punt_reg, pri);
557 vlib_cli_output (vm, "\nDerived data-plane data-base:");
559 " (for each punt-reason the edge[s] from punt-dispatch)");
561 vec_foreach_index (ii, punt_dp_db)
564 vlib_cli_output (vm, " %U", format_vlib_punt_reason, ii);
566 vec_foreach_index (jj, punt_dp_db[ii])
568 s = format (s, "%d ", punt_dp_db[ii][jj]);
570 vlib_cli_output (vm, " [%v]", s);
578 VLIB_CLI_COMMAND (punt_db_show_command, static) =
580 .path = "show punt db",
581 .short_help = "show the punt DB",
582 .function = punt_db_show,
586 static clib_error_t *
587 punt_stats_show (vlib_main_t * vm,
588 unformat_input_t * input, vlib_cli_command_t * cmd)
590 vlib_combined_counter_main_t *cm = &punt_counters;
594 for (ii = 0; ii < vlib_combined_counter_n_counters (cm); ii++)
596 vlib_get_combined_counter (cm, ii, &c);
597 vlib_cli_output (vm, "%U packets:%lld bytes:%lld",
598 format_vlib_punt_reason, ii, c.packets, c.bytes);
605 VLIB_CLI_COMMAND (punt_stats_show_command, static) =
607 .path = "show punt stats",
608 .short_help = "show the punt stats",
609 .function = punt_stats_show,
613 static clib_error_t *
614 punt_init (vlib_main_t * vm)
616 punt_client_db = hash_create_vec (0, sizeof (u8), sizeof (u32));
621 VLIB_INIT_FUNCTION (punt_init);
624 * fd.io coding-style-patch-verification: ON
627 * eval: (c-set-style "gnu")