/* SPDX-License-Identifier: Apache-2.0 * Copyright(c) 2022 Cisco Systems, Inc. */ /** * @file * @brief SR Path Tracing (PT) * * SR PT CLI * */ #include #include #include #include #include #include #include #include #include #include #include #include sr_pt_main_t sr_pt_main; void * sr_pt_find_iface (u32 iface) { sr_pt_main_t *sr_pt = &sr_pt_main; uword *p; /* Search for the item */ p = mhash_get (&sr_pt->sr_pt_iface_index_hash, &iface); if (p) { /* Retrieve sr_pt_iface */ return pool_elt_at_index (sr_pt->sr_pt_iface, p[0]); } return NULL; } int sr_pt_add_iface (u32 iface, u16 id, u8 ingress_load, u8 egress_load, u8 tts_template) { sr_pt_main_t *sr_pt = &sr_pt_main; uword *p; sr_pt_iface_t *ls = 0; if (iface == (u32) ~0) return SR_PT_ERR_IFACE_INVALID; /* Search for the item */ p = mhash_get (&sr_pt->sr_pt_iface_index_hash, &iface); if (p) return SR_PT_ERR_EXIST; if (id > SR_PT_ID_MAX) return SR_PT_ERR_ID_INVALID; if (ingress_load > SR_PT_LOAD_MAX || egress_load > SR_PT_LOAD_MAX) return SR_PT_ERR_LOAD_INVALID; if (tts_template > SR_PT_TTS_TEMPLATE_MAX) return SR_PT_ERR_TTS_TEMPLATE_INVALID; vnet_feature_enable_disable ("ip6-output", "pt", iface, 1, 0, 0); /* Create a new sr_pt_iface */ pool_get_zero (sr_pt->sr_pt_iface, ls); ls->iface = iface; ls->id = id; ls->ingress_load = ingress_load; ls->egress_load = egress_load; ls->tts_template = tts_template; /* Set hash key for searching sr_pt_iface by iface */ mhash_set (&sr_pt->sr_pt_iface_index_hash, &iface, ls - sr_pt->sr_pt_iface, NULL); return 0; } int sr_pt_del_iface (u32 iface) { sr_pt_main_t *sr_pt = &sr_pt_main; uword *p; sr_pt_iface_t *ls = 0; if (iface == (u32) ~0) return SR_PT_ERR_IFACE_INVALID; /* Search for the item */ p = mhash_get (&sr_pt->sr_pt_iface_index_hash, &iface); if (p) { /* Retrieve sr_pt_iface */ ls = pool_elt_at_index (sr_pt->sr_pt_iface, p[0]); vnet_feature_enable_disable ("ip6-output", "pt", iface, 0, 0, 0); /* Delete sr_pt_iface */ pool_put (sr_pt->sr_pt_iface, ls); mhash_unset (&sr_pt->sr_pt_iface_index_hash, &iface, NULL); } else { return SR_PT_ERR_NOENT; } return 0; } /** * @brief "sr pt add iface" CLI function. * * @see sr_pt_add_iface */ static clib_error_t * sr_pt_add_iface_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { vnet_main_t *vnm = vnet_get_main (); u32 iface = (u32) ~0; u32 id = (u32) ~0; u32 ingress_load = 0; u32 egress_load = 0; u32 tts_template = SR_PT_TTS_TEMPLATE_DEFAULT; int rv; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, &iface)) ; else if (unformat (input, "id %u", &id)) ; else if (unformat (input, "ingress-load %u", &ingress_load)) ; else if (unformat (input, "egress-load %u", &egress_load)) ; else if (unformat (input, "tts-template %u", &tts_template)) ; else break; } rv = sr_pt_add_iface (iface, id, ingress_load, egress_load, tts_template); switch (rv) { case 0: break; case SR_PT_ERR_EXIST: return clib_error_return (0, "Error: Identical iface already exists."); case SR_PT_ERR_IFACE_INVALID: return clib_error_return (0, "Error: The iface name invalid."); case SR_PT_ERR_ID_INVALID: return clib_error_return (0, "Error: The iface id value invalid."); case SR_PT_ERR_LOAD_INVALID: return clib_error_return ( 0, "Error: The iface ingress or egress load value invalid."); case SR_PT_ERR_TTS_TEMPLATE_INVALID: return clib_error_return ( 0, "Error: The iface TTS Template value invalid."); default: return clib_error_return (0, "Error: unknown error."); } return 0; } /** * @brief "sr pt del iface" CLI function. * * @see sr_pt_del_iface */ static clib_error_t * sr_pt_del_iface_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { vnet_main_t *vnm = vnet_get_main (); u32 iface = (u32) ~0; int rv; while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT) { if (unformat (input, "%U", unformat_vnet_sw_interface, vnm, &iface)) ; else break; } rv = sr_pt_del_iface (iface); switch (rv) { case 0: break; case SR_PT_ERR_NOENT: return clib_error_return (0, "Error: No such iface."); case SR_PT_ERR_IFACE_INVALID: return clib_error_return (0, "Error: The iface name is not valid."); default: return clib_error_return (0, "Error: unknown error."); } return 0; } /** * @brief CLI function to show all SR PT interfcaes */ static clib_error_t * sr_pt_show_iface_command_fn (vlib_main_t *vm, unformat_input_t *input, vlib_cli_command_t *cmd) { vnet_main_t *vnm = vnet_get_main (); sr_pt_main_t *sr_pt = &sr_pt_main; sr_pt_iface_t **sr_pt_iface_list = 0; sr_pt_iface_t *ls; int i; vlib_cli_output (vm, "SR PT Interfaces"); vlib_cli_output (vm, "=================================="); pool_foreach (ls, sr_pt->sr_pt_iface) { vec_add1 (sr_pt_iface_list, ls); }; for (i = 0; i < vec_len (sr_pt_iface_list); i++) { ls = sr_pt_iface_list[i]; vlib_cli_output ( vm, "\tiface : \t%U\n\tid : \t%d\n\tingress-load: " "\t%d\n\tegress-load : \t%d\n\ttts-template: \t%d ", format_vnet_sw_if_index_name, vnm, ls->iface, ls->id, ls->ingress_load, ls->egress_load, ls->tts_template); vlib_cli_output (vm, "--------------------------------"); } return 0; } VLIB_CLI_COMMAND (sr_pt_add_iface_command, static) = { .path = "sr pt add iface", .short_help = "sr pt add iface id ingress-load " " egress-load " "tts-template ", .function = sr_pt_add_iface_command_fn, }; VLIB_CLI_COMMAND (sr_pt_del_iface_command, static) = { .path = "sr pt del iface", .short_help = "sr pt del iface ", .function = sr_pt_del_iface_command_fn, }; VLIB_CLI_COMMAND (sr_pt_show_iface_command, static) = { .path = "sr pt show iface", .short_help = "sr pt show iface", .function = sr_pt_show_iface_command_fn, }; /** * * @brief SR PT initialization * */ clib_error_t * sr_pt_init (vlib_main_t *vm) { sr_pt_main_t *pt = &sr_pt_main; mhash_init (&pt->sr_pt_iface_index_hash, sizeof (uword), sizeof (u32)); return 0; } VLIB_INIT_FUNCTION (sr_pt_init);