diff options
Diffstat (limited to 'sharpd/sharp_vty.c')
-rw-r--r-- | sharpd/sharp_vty.c | 1452 |
1 files changed, 1452 insertions, 0 deletions
diff --git a/sharpd/sharp_vty.c b/sharpd/sharp_vty.c new file mode 100644 index 0000000..cf79bac --- /dev/null +++ b/sharpd/sharp_vty.c @@ -0,0 +1,1452 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SHARP - vty code + * Copyright (C) Cumulus Networks, Inc. + * Donald Sharp + */ +#include <zebra.h> + +#include "vty.h" +#include "command.h" +#include "prefix.h" +#include "nexthop.h" +#include "log.h" +#include "vrf.h" +#include "zclient.h" +#include "nexthop_group.h" +#include "linklist.h" +#include "link_state.h" +#include "cspf.h" +#include "tc.h" + +#include "sharpd/sharp_globals.h" +#include "sharpd/sharp_zebra.h" +#include "sharpd/sharp_nht.h" +#include "sharpd/sharp_vty.h" +#include "sharpd/sharp_vty_clippy.c" + +DEFINE_MTYPE_STATIC(SHARPD, SRV6_LOCATOR, "SRv6 Locator"); + +DEFPY(watch_redistribute, watch_redistribute_cmd, + "sharp watch [vrf NAME$vrf_name] redistribute " FRR_REDIST_STR_SHARPD, + "Sharp routing Protocol\n" + "Watch for changes\n" + "The vrf we would like to watch if non-default\n" + "The NAME of the vrf\n" + "Redistribute into Sharp\n" + FRR_REDIST_HELP_STR_SHARPD) +{ + struct vrf *vrf; + int source; + + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + vrf = vrf_lookup_by_name(vrf_name); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name); + return CMD_WARNING; + } + + source = proto_redistnum(AFI_IP, argv[argc-1]->text); + sharp_redistribute_vrf(vrf, source); + + return CMD_SUCCESS; +} + +DEFPY(watch_nexthop_v6, watch_nexthop_v6_cmd, + "sharp watch [vrf NAME$vrf_name] <nexthop$n X:X::X:X$nhop|import$import X:X::X:X/M$inhop> [connected$connected]", + "Sharp routing Protocol\n" + "Watch for changes\n" + "The vrf we would like to watch if non-default\n" + "The NAME of the vrf\n" + "Watch for nexthop changes\n" + "The v6 nexthop to signal for watching\n" + "Watch for import check changes\n" + "The v6 prefix to signal for watching\n" + "Should the route be connected\n") +{ + struct vrf *vrf; + struct prefix p; + bool type_import; + + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + vrf = vrf_lookup_by_name(vrf_name); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name); + return CMD_WARNING; + } + + memset(&p, 0, sizeof(p)); + + if (n) { + type_import = false; + p.prefixlen = IPV6_MAX_BITLEN; + memcpy(&p.u.prefix6, &nhop, IPV6_MAX_BYTELEN); + p.family = AF_INET6; + } else { + type_import = true; + prefix_copy(&p, inhop); + } + + sharp_nh_tracker_get(&p); + sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, + true, !!connected); + + return CMD_SUCCESS; +} + +DEFPY(watch_nexthop_v4, watch_nexthop_v4_cmd, + "sharp watch [vrf NAME$vrf_name] <nexthop$n A.B.C.D$nhop|import$import A.B.C.D/M$inhop> [connected$connected]", + "Sharp routing Protocol\n" + "Watch for changes\n" + "The vrf we would like to watch if non-default\n" + "The NAME of the vrf\n" + "Watch for nexthop changes\n" + "The v4 address to signal for watching\n" + "Watch for import check changes\n" + "The v4 prefix for import check to watch\n" + "Should the route be connected\n") +{ + struct vrf *vrf; + struct prefix p; + bool type_import; + + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + vrf = vrf_lookup_by_name(vrf_name); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name); + return CMD_WARNING; + } + + memset(&p, 0, sizeof(p)); + + if (n) { + type_import = false; + p.prefixlen = IPV4_MAX_BITLEN; + p.u.prefix4 = nhop; + p.family = AF_INET; + } + else { + type_import = true; + prefix_copy(&p, inhop); + } + + sharp_nh_tracker_get(&p); + sharp_zebra_nexthop_watch(&p, vrf->vrf_id, type_import, + true, !!connected); + + return CMD_SUCCESS; +} + +DEFPY(sharp_nht_data_dump, + sharp_nht_data_dump_cmd, + "sharp data nexthop", + "Sharp routing Protocol\n" + "Data about what is going on\n" + "Nexthop information\n") +{ + sharp_nh_tracker_dump(vty); + + return CMD_SUCCESS; +} + +DEFPY (install_routes_data_dump, + install_routes_data_dump_cmd, + "sharp data route", + "Sharp routing Protocol\n" + "Data about what is going on\n" + "Route Install/Removal Information\n") +{ + struct timeval r; + + timersub(&sg.r.t_end, &sg.r.t_start, &r); + vty_out(vty, "Prefix: %pFX Total: %u %u %u Time: %jd.%ld\n", + &sg.r.orig_prefix, sg.r.total_routes, sg.r.installed_routes, + sg.r.removed_routes, (intmax_t)r.tv_sec, (long)r.tv_usec); + + return CMD_SUCCESS; +} + +DEFPY (install_routes, + install_routes_cmd, + "sharp install routes [vrf NAME$vrf_name]\ + <A.B.C.D$start4|X:X::X:X$start6>\ + <nexthop <A.B.C.D$nexthop4|X:X::X:X$nexthop6>|\ + nexthop-group NHGNAME$nexthop_group>\ + [backup$backup <A.B.C.D$backup_nexthop4|X:X::X:X$backup_nexthop6>] \ + (1-1000000)$routes [instance (0-255)$instance] [repeat (2-1000)$rpt] [opaque WORD] [no-recurse$norecurse]", + "Sharp routing Protocol\n" + "install some routes\n" + "Routes to install\n" + "The vrf we would like to install into if non-default\n" + "The NAME of the vrf\n" + "v4 Address to start /32 generation at\n" + "v6 Address to start /32 generation at\n" + "Nexthop to use(Can be an IPv4 or IPv6 address)\n" + "V4 Nexthop address to use\n" + "V6 Nexthop address to use\n" + "Nexthop-Group to use\n" + "The Name of the nexthop-group\n" + "Backup nexthop to use(Can be an IPv4 or IPv6 address)\n" + "Backup V4 Nexthop address to use\n" + "Backup V6 Nexthop address to use\n" + "How many to create\n" + "Instance to use\n" + "Instance\n" + "Should we repeat this command\n" + "How many times to repeat this command\n" + "What opaque data to send down\n" + "The opaque data\n" + "No recursive nexthops\n") +{ + struct vrf *vrf; + struct prefix prefix; + uint32_t rts; + uint32_t nhgid = 0; + + sg.r.total_routes = routes; + sg.r.installed_routes = 0; + sg.r.flags = 0; + + if (rpt >= 2) + sg.r.repeat = rpt * 2; + else + sg.r.repeat = 0; + + memset(&prefix, 0, sizeof(prefix)); + memset(&sg.r.orig_prefix, 0, sizeof(sg.r.orig_prefix)); + nexthop_del_srv6_seg6local(&sg.r.nhop); + nexthop_del_srv6_seg6(&sg.r.nhop); + memset(&sg.r.nhop, 0, sizeof(sg.r.nhop)); + memset(&sg.r.nhop_group, 0, sizeof(sg.r.nhop_group)); + memset(&sg.r.backup_nhop, 0, sizeof(sg.r.nhop)); + memset(&sg.r.backup_nhop_group, 0, sizeof(sg.r.nhop_group)); + + if (start4.s_addr != INADDR_ANY) { + prefix.family = AF_INET; + prefix.prefixlen = IPV4_MAX_BITLEN; + prefix.u.prefix4 = start4; + } else { + prefix.family = AF_INET6; + prefix.prefixlen = IPV6_MAX_BITLEN; + prefix.u.prefix6 = start6; + } + sg.r.orig_prefix = prefix; + + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + + vrf = vrf_lookup_by_name(vrf_name); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name); + return CMD_WARNING; + } + + /* Explicit backup not available with named nexthop-group */ + if (backup && nexthop_group) { + vty_out(vty, "%% Invalid: cannot specify both nexthop-group and backup\n"); + return CMD_WARNING; + } + + if (nexthop_group) { + struct nexthop_group_cmd *nhgc = nhgc_find(nexthop_group); + if (!nhgc) { + vty_out(vty, + "Specified Nexthop Group: %s does not exist\n", + nexthop_group); + return CMD_WARNING; + } + + nhgid = sharp_nhgroup_get_id(nexthop_group); + sg.r.nhgid = nhgid; + sg.r.nhop_group.nexthop = nhgc->nhg.nexthop; + + /* Use group's backup nexthop info if present */ + if (nhgc->backup_list_name[0]) { + struct nexthop_group_cmd *bnhgc = + nhgc_find(nhgc->backup_list_name); + + if (!bnhgc) { + vty_out(vty, "%% Backup group %s not found for group %s\n", + nhgc->backup_list_name, + nhgc->name); + return CMD_WARNING; + } + + sg.r.backup_nhop.vrf_id = vrf->vrf_id; + sg.r.backup_nhop_group.nexthop = bnhgc->nhg.nexthop; + } + } else { + if (nexthop4.s_addr != INADDR_ANY) { + sg.r.nhop.gate.ipv4 = nexthop4; + sg.r.nhop.type = NEXTHOP_TYPE_IPV4; + } else { + sg.r.nhop.gate.ipv6 = nexthop6; + sg.r.nhop.type = NEXTHOP_TYPE_IPV6; + } + + sg.r.nhop.vrf_id = vrf->vrf_id; + sg.r.nhop_group.nexthop = &sg.r.nhop; + } + + /* Use single backup nexthop if specified */ + if (backup) { + /* Set flag and index in primary nexthop */ + SET_FLAG(sg.r.nhop.flags, NEXTHOP_FLAG_HAS_BACKUP); + sg.r.nhop.backup_num = 1; + sg.r.nhop.backup_idx[0] = 0; + + if (backup_nexthop4.s_addr != INADDR_ANY) { + sg.r.backup_nhop.gate.ipv4 = backup_nexthop4; + sg.r.backup_nhop.type = NEXTHOP_TYPE_IPV4; + } else { + sg.r.backup_nhop.gate.ipv6 = backup_nexthop6; + sg.r.backup_nhop.type = NEXTHOP_TYPE_IPV6; + } + + sg.r.backup_nhop.vrf_id = vrf->vrf_id; + sg.r.backup_nhop_group.nexthop = &sg.r.backup_nhop; + } + + if (opaque) + strlcpy(sg.r.opaque, opaque, ZAPI_MESSAGE_OPAQUE_LENGTH); + else + sg.r.opaque[0] = '\0'; + + /* Default is to ask for recursive nexthop resolution */ + if (norecurse == NULL) + SET_FLAG(sg.r.flags, ZEBRA_FLAG_ALLOW_RECURSION); + + sg.r.inst = instance; + sg.r.vrf_id = vrf->vrf_id; + rts = routes; + sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, nhgid, + &sg.r.nhop_group, &sg.r.backup_nhop_group, + rts, sg.r.flags, sg.r.opaque); + + return CMD_SUCCESS; +} + +DEFPY (install_seg6_routes, + install_seg6_routes_cmd, + "sharp install seg6-routes [vrf NAME$vrf_name]\ + <A.B.C.D$start4|X:X::X:X$start6>\ + nexthop-seg6 X:X::X:X$seg6_nh6 encap X:X::X:X$seg6_seg\ + (1-1000000)$routes [repeat (2-1000)$rpt]", + "Sharp routing Protocol\n" + "install some routes\n" + "Routes to install\n" + "The vrf we would like to install into if non-default\n" + "The NAME of the vrf\n" + "v4 Address to start /32 generation at\n" + "v6 Address to start /32 generation at\n" + "Nexthop-seg6 to use\n" + "V6 Nexthop address to use\n" + "Encap mode\n" + "Segment List to use\n" + "How many to create\n" + "Should we repeat this command\n" + "How many times to repeat this command\n") +{ + struct vrf *vrf; + struct prefix prefix; + uint32_t route_flags = 0; + + sg.r.total_routes = routes; + sg.r.installed_routes = 0; + + if (rpt >= 2) + sg.r.repeat = rpt * 2; + else + sg.r.repeat = 0; + + memset(&prefix, 0, sizeof(prefix)); + memset(&sg.r.orig_prefix, 0, sizeof(sg.r.orig_prefix)); + nexthop_del_srv6_seg6local(&sg.r.nhop); + nexthop_del_srv6_seg6(&sg.r.nhop); + memset(&sg.r.nhop, 0, sizeof(sg.r.nhop)); + memset(&sg.r.nhop_group, 0, sizeof(sg.r.nhop_group)); + memset(&sg.r.backup_nhop, 0, sizeof(sg.r.nhop)); + memset(&sg.r.backup_nhop_group, 0, sizeof(sg.r.nhop_group)); + sg.r.opaque[0] = '\0'; + sg.r.inst = 0; + + if (start4.s_addr != INADDR_ANY) { + prefix.family = AF_INET; + prefix.prefixlen = IPV4_MAX_BITLEN; + prefix.u.prefix4 = start4; + } else { + prefix.family = AF_INET6; + prefix.prefixlen = IPV6_MAX_BITLEN; + prefix.u.prefix6 = start6; + } + sg.r.orig_prefix = prefix; + + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + + vrf = vrf_lookup_by_name(vrf_name); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name); + return CMD_WARNING; + } + + sg.r.nhop.type = NEXTHOP_TYPE_IPV6; + sg.r.nhop.gate.ipv6 = seg6_nh6; + sg.r.nhop.vrf_id = vrf->vrf_id; + sg.r.nhop_group.nexthop = &sg.r.nhop; + nexthop_add_srv6_seg6(&sg.r.nhop, &seg6_seg, 1); + + sg.r.vrf_id = vrf->vrf_id; + sharp_install_routes_helper(&prefix, sg.r.vrf_id, sg.r.inst, 0, + &sg.r.nhop_group, &sg.r.backup_nhop_group, + routes, route_flags, sg.r.opaque); + + return CMD_SUCCESS; +} + +DEFPY (install_seg6local_routes, + install_seg6local_routes_cmd, + "sharp install seg6local-routes [vrf NAME$vrf_name]\ + X:X::X:X$start6\ + nexthop-seg6local NAME$seg6l_oif\ + <End$seg6l_end|\ + End_X$seg6l_endx X:X::X:X$seg6l_endx_nh6|\ + End_T$seg6l_endt (1-4294967295)$seg6l_endt_table|\ + End_DX4$seg6l_enddx4 A.B.C.D$seg6l_enddx4_nh4|\ + End_DT6$seg6l_enddt6 (1-4294967295)$seg6l_enddt6_table|\ + End_DT4$seg6l_enddt4 (1-4294967295)$seg6l_enddt4_table|\ + End_DT46$seg6l_enddt46 (1-4294967295)$seg6l_enddt46_table>\ + (1-1000000)$routes [repeat (2-1000)$rpt]", + "Sharp routing Protocol\n" + "install some routes\n" + "Routes to install\n" + "The vrf we would like to install into if non-default\n" + "The NAME of the vrf\n" + "v6 Address to start /32 generation at\n" + "Nexthop-seg6local to use\n" + "Output device to use\n" + "SRv6 End function to use\n" + "SRv6 End.X function to use\n" + "V6 Nexthop address to use\n" + "SRv6 End.T function to use\n" + "Redirect table id to use\n" + "SRv6 End.DX4 function to use\n" + "V4 Nexthop address to use\n" + "SRv6 End.DT6 function to use\n" + "Redirect table id to use\n" + "SRv6 End.DT4 function to use\n" + "Redirect table id to use\n" + "SRv6 End.DT46 function to use\n" + "Redirect table id to use\n" + "How many to create\n" + "Should we repeat this command\n" + "How many times to repeat this command\n") +{ + struct vrf *vrf; + uint32_t route_flags = 0; + struct seg6local_context ctx = {}; + enum seg6local_action_t action; + + sg.r.total_routes = routes; + sg.r.installed_routes = 0; + + if (rpt >= 2) + sg.r.repeat = rpt * 2; + else + sg.r.repeat = 0; + + memset(&sg.r.orig_prefix, 0, sizeof(sg.r.orig_prefix)); + nexthop_del_srv6_seg6local(&sg.r.nhop); + nexthop_del_srv6_seg6(&sg.r.nhop); + memset(&sg.r.nhop, 0, sizeof(sg.r.nhop)); + memset(&sg.r.nhop_group, 0, sizeof(sg.r.nhop_group)); + memset(&sg.r.backup_nhop, 0, sizeof(sg.r.nhop)); + memset(&sg.r.backup_nhop_group, 0, sizeof(sg.r.nhop_group)); + sg.r.opaque[0] = '\0'; + sg.r.inst = 0; + sg.r.orig_prefix.family = AF_INET6; + sg.r.orig_prefix.prefixlen = 128; + sg.r.orig_prefix.u.prefix6 = start6; + + if (!vrf_name) + vrf_name = VRF_DEFAULT_NAME; + + vrf = vrf_lookup_by_name(vrf_name); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name); + return CMD_WARNING; + } + + if (seg6l_enddx4) { + action = ZEBRA_SEG6_LOCAL_ACTION_END_DX4; + ctx.nh4 = seg6l_enddx4_nh4; + } else if (seg6l_endx) { + action = ZEBRA_SEG6_LOCAL_ACTION_END_X; + ctx.nh6 = seg6l_endx_nh6; + } else if (seg6l_endt) { + action = ZEBRA_SEG6_LOCAL_ACTION_END_T; + ctx.table = seg6l_endt_table; + } else if (seg6l_enddt6) { + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT6; + ctx.table = seg6l_enddt6_table; + } else if (seg6l_enddt4) { + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT4; + ctx.table = seg6l_enddt4_table; + } else if (seg6l_enddt46) { + action = ZEBRA_SEG6_LOCAL_ACTION_END_DT46; + ctx.table = seg6l_enddt46_table; + } else { + action = ZEBRA_SEG6_LOCAL_ACTION_END; + } + + sg.r.nhop.type = NEXTHOP_TYPE_IFINDEX; + sg.r.nhop.ifindex = ifname2ifindex(seg6l_oif, vrf->vrf_id); + sg.r.nhop.vrf_id = vrf->vrf_id; + sg.r.nhop_group.nexthop = &sg.r.nhop; + nexthop_add_srv6_seg6local(&sg.r.nhop, action, &ctx); + + sg.r.vrf_id = vrf->vrf_id; + sharp_install_routes_helper(&sg.r.orig_prefix, sg.r.vrf_id, sg.r.inst, + 0, &sg.r.nhop_group, + &sg.r.backup_nhop_group, routes, + route_flags, sg.r.opaque); + + return CMD_SUCCESS; +} + +DEFPY(vrf_label, vrf_label_cmd, + "sharp label <ip$ipv4|ipv6$ipv6> vrf NAME$vrf_name label (0-100000)$label", + "Sharp Routing Protocol\n" + "Give a vrf a label\n" + "Pop and forward for IPv4\n" + "Pop and forward for IPv6\n" + VRF_CMD_HELP_STR + "The label to use, 0 specifies remove the label installed from previous\n" + "Specified range to use\n") +{ + struct vrf *vrf; + afi_t afi = (ipv4) ? AFI_IP : AFI_IP6; + + if (strcmp(vrf_name, "default") == 0) + vrf = vrf_lookup_by_id(VRF_DEFAULT); + else + vrf = vrf_lookup_by_name(vrf_name); + + if (!vrf) { + vty_out(vty, "Unable to find vrf you silly head\n"); + return CMD_WARNING_CONFIG_FAILED; + } + + if (label == 0) + label = MPLS_LABEL_NONE; + + vrf_label_add(vrf->vrf_id, afi, label); + return CMD_SUCCESS; +} + +DEFPY (remove_routes, + remove_routes_cmd, + "sharp remove routes [vrf NAME$vrf_name] <A.B.C.D$start4|X:X::X:X$start6> (1-1000000)$routes [instance (0-255)$instance]", + "Sharp Routing Protocol\n" + "Remove some routes\n" + "Routes to remove\n" + "The vrf we would like to remove from if non-default\n" + "The NAME of the vrf\n" + "v4 Starting spot\n" + "v6 Starting spot\n" + "Routes to uninstall\n" + "instance to use\n" + "Value of instance\n") +{ + struct vrf *vrf; + struct prefix prefix; + + sg.r.total_routes = routes; + sg.r.removed_routes = 0; + uint32_t rts; + + memset(&prefix, 0, sizeof(prefix)); + + if (start4.s_addr != INADDR_ANY) { + prefix.family = AF_INET; + prefix.prefixlen = IPV4_MAX_BITLEN; + prefix.u.prefix4 = start4; + } else { + prefix.family = AF_INET6; + prefix.prefixlen = IPV6_MAX_BITLEN; + prefix.u.prefix6 = start6; + } + + vrf = vrf_lookup_by_name(vrf_name ? vrf_name : VRF_DEFAULT_NAME); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name ? vrf_name : VRF_DEFAULT_NAME); + return CMD_WARNING; + } + + sg.r.inst = instance; + sg.r.vrf_id = vrf->vrf_id; + rts = routes; + sharp_remove_routes_helper(&prefix, sg.r.vrf_id, + sg.r.inst, rts); + + return CMD_SUCCESS; +} + +DEFUN_NOSH (show_debugging_sharpd, + show_debugging_sharpd_cmd, + "show debugging [sharp]", + SHOW_STR + DEBUG_STR + "Sharp Information\n") +{ + vty_out(vty, "Sharp debugging status:\n"); + + cmd_show_lib_debugs(vty); + + return CMD_SUCCESS; +} + +DEFPY (sharp_lsp_prefix_v4, sharp_lsp_prefix_v4_cmd, + "sharp lsp [update]$update (0-100000)$inlabel\ + nexthop-group NHGNAME$nhgname\ + [prefix A.B.C.D/M$pfx\ + " FRR_IP_REDIST_STR_ZEBRA "$type_str [instance (0-255)$instance]]", + "Sharp Routing Protocol\n" + "Add an LSP\n" + "Update an LSP\n" + "The ingress label to use\n" + "Use nexthops from a nexthop-group\n" + "The nexthop-group name\n" + "Label a prefix\n" + "The v4 prefix to label\n" + FRR_IP_REDIST_HELP_STR_ZEBRA + "Instance to use\n" + "Instance\n") +{ + struct nexthop_group_cmd *nhgc = NULL; + struct nexthop_group_cmd *backup_nhgc = NULL; + struct nexthop_group *backup_nhg = NULL; + struct prefix p = {}; + int type = 0; + bool update_p; + + update_p = (update != NULL); + + /* We're offered a v4 prefix */ + if (pfx->family > 0 && type_str) { + p.family = pfx->family; + p.prefixlen = pfx->prefixlen; + p.u.prefix4 = pfx->prefix; + + type = proto_redistnum(AFI_IP, type_str); + if (type < 0) { + vty_out(vty, "%% Unknown route type '%s'\n", type_str); + return CMD_WARNING; + } + } else if (pfx->family > 0 || type_str) { + vty_out(vty, "%% Must supply both prefix and type\n"); + return CMD_WARNING; + } + + nhgc = nhgc_find(nhgname); + if (!nhgc) { + vty_out(vty, "%% Nexthop-group '%s' does not exist\n", + nhgname); + return CMD_WARNING; + } + + if (nhgc->nhg.nexthop == NULL) { + vty_out(vty, "%% Nexthop-group '%s' is empty\n", nhgname); + return CMD_WARNING; + } + + /* Use group's backup nexthop info if present */ + if (nhgc->backup_list_name[0]) { + backup_nhgc = nhgc_find(nhgc->backup_list_name); + + if (!backup_nhgc) { + vty_out(vty, + "%% Backup group %s not found for group %s\n", + nhgc->backup_list_name, + nhgname); + return CMD_WARNING; + } + backup_nhg = &(backup_nhgc->nhg); + } + + if (sharp_install_lsps_helper(true /*install*/, update_p, + pfx->family > 0 ? &p : NULL, + type, instance, inlabel, + &(nhgc->nhg), backup_nhg) == 0) + return CMD_SUCCESS; + else { + vty_out(vty, "%% LSP install failed!\n"); + return CMD_WARNING; + } +} + +DEFPY(sharp_remove_lsp_prefix_v4, sharp_remove_lsp_prefix_v4_cmd, + "sharp remove lsp \ + (0-100000)$inlabel\ + [nexthop-group NHGNAME$nhgname] \ + [prefix A.B.C.D/M$pfx\ + " FRR_IP_REDIST_STR_ZEBRA "$type_str [instance (0-255)$instance]]", + "Sharp Routing Protocol\n" + "Remove data\n" + "Remove an LSP\n" + "The ingress label\n" + "Use nexthops from a nexthop-group\n" + "The nexthop-group name\n" + "Specify a v4 prefix\n" + "The v4 prefix to label\n" + FRR_IP_REDIST_HELP_STR_ZEBRA + "Routing instance\n" + "Instance to use\n") +{ + struct nexthop_group_cmd *nhgc = NULL; + struct prefix p = {}; + int type = 0; + struct nexthop_group *nhg = NULL; + + /* We're offered a v4 prefix */ + if (pfx->family > 0 && type_str) { + p.family = pfx->family; + p.prefixlen = pfx->prefixlen; + p.u.prefix4 = pfx->prefix; + + type = proto_redistnum(AFI_IP, type_str); + if (type < 0) { + vty_out(vty, "%% Unknown route type '%s'\n", type_str); + return CMD_WARNING; + } + } else if (pfx->family > 0 || type_str) { + vty_out(vty, "%% Must supply both prefix and type\n"); + return CMD_WARNING; + } + + if (nhgname) { + nhgc = nhgc_find(nhgname); + if (!nhgc) { + vty_out(vty, "%% Nexthop-group '%s' does not exist\n", + nhgname); + return CMD_WARNING; + } + + if (nhgc->nhg.nexthop == NULL) { + vty_out(vty, "%% Nexthop-group '%s' is empty\n", + nhgname); + return CMD_WARNING; + } + nhg = &(nhgc->nhg); + } + + if (sharp_install_lsps_helper(false /*!install*/, false, + pfx->family > 0 ? &p : NULL, + type, instance, inlabel, nhg, NULL) == 0) + return CMD_SUCCESS; + else { + vty_out(vty, "%% LSP remove failed!\n"); + return CMD_WARNING; + } +} + +DEFPY (logpump, + logpump_cmd, + "sharp logpump duration (1-60) frequency (1-1000000) burst (1-1000)", + "Sharp Routing Protocol\n" + "Generate bulk log messages for testing\n" + "Duration of run (s)\n" + "Duration of run (s)\n" + "Frequency of bursts (s^-1)\n" + "Frequency of bursts (s^-1)\n" + "Number of log messages per each burst\n" + "Number of log messages per each burst\n") +{ + sharp_logpump_run(vty, duration, frequency, burst); + return CMD_SUCCESS; +} + +DEFPY (create_session, + create_session_cmd, + "sharp create session (1-1024)", + "Sharp Routing Protocol\n" + "Create data\n" + "Create a test session\n" + "Session ID\n") +{ + if (sharp_zclient_create(session) != 0) { + vty_out(vty, "%% Client session error\n"); + return CMD_WARNING; + } + + return CMD_SUCCESS; +} + +DEFPY (remove_session, + remove_session_cmd, + "sharp remove session (1-1024)", + "Sharp Routing Protocol\n" + "Remove data\n" + "Remove a test session\n" + "Session ID\n") +{ + sharp_zclient_delete(session); + return CMD_SUCCESS; +} + +DEFPY (send_opaque, + send_opaque_cmd, + "sharp send opaque type (1-255) (1-1000)$count", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Type code to send\n" + "Type code to send\n" + "Number of messages to send\n") +{ + sharp_opaque_send(type, 0, 0, 0, count); + return CMD_SUCCESS; +} + +DEFPY (send_opaque_unicast, + send_opaque_unicast_cmd, + "sharp send opaque unicast type (1-255) \ + " FRR_IP_REDIST_STR_ZEBRA "$proto_str \ + [{instance (0-1000) | session (1-1000)}] (1-1000)$count", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Send unicast messages\n" + "Type code to send\n" + "Type code to send\n" + FRR_IP_REDIST_HELP_STR_ZEBRA + "Daemon instance\n" + "Daemon instance\n" + "Session ID\n" + "Session ID\n" + "Number of messages to send\n") +{ + uint32_t proto; + + proto = proto_redistnum(AFI_IP, proto_str); + + sharp_opaque_send(type, proto, instance, session, count); + + return CMD_SUCCESS; +} + +DEFPY (send_opaque_reg, + send_opaque_reg_cmd, + "sharp send opaque <reg$reg | unreg> \ + " FRR_IP_REDIST_STR_ZEBRA "$proto_str \ + [{instance (0-1000) | session (1-1000)}] type (1-1000)", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Send opaque registration\n" + "Send opaque unregistration\n" + FRR_IP_REDIST_HELP_STR_ZEBRA + "Daemon instance\n" + "Daemon instance\n" + "Session ID\n" + "Session ID\n" + "Opaque sub-type code\n" + "Opaque sub-type code\n") +{ + int proto; + + proto = proto_redistnum(AFI_IP, proto_str); + + sharp_opaque_reg_send((reg != NULL), proto, instance, session, type); + return CMD_SUCCESS; +} + +/* Opaque notifications - register or unregister */ +DEFPY (send_opaque_notif_reg, + send_opaque_notif_reg_cmd, + "sharp send opaque notify <reg$reg | unreg> type (1-1000)", + SHARP_STR + "Send messages for testing\n" + "Send opaque messages\n" + "Opaque notification messages\n" + "Send notify registration\n" + "Send notify unregistration\n" + "Opaque sub-type code\n" + "Opaque sub-type code\n") +{ + sharp_zebra_opaque_notif_reg((reg != NULL), type); + + return CMD_SUCCESS; +} + +DEFPY (neigh_discover, + neigh_discover_cmd, + "sharp neigh discover [vrf NAME$vrf_name] <A.B.C.D$dst4|X:X::X:X$dst6> IFNAME$ifname", + SHARP_STR + "Discover neighbours\n" + "Send an ARP/NDP request\n" + VRF_CMD_HELP_STR + "v4 Destination address\n" + "v6 Destination address\n" + "Interface name\n") +{ + struct vrf *vrf; + struct interface *ifp; + struct prefix prefix; + + memset(&prefix, 0, sizeof(prefix)); + + if (dst4.s_addr != INADDR_ANY) { + prefix.family = AF_INET; + prefix.prefixlen = IPV4_MAX_BITLEN; + prefix.u.prefix4 = dst4; + } else { + prefix.family = AF_INET6; + prefix.prefixlen = 128; + prefix.u.prefix6 = dst6; + } + + vrf = vrf_lookup_by_name(vrf_name ? vrf_name : VRF_DEFAULT_NAME); + if (!vrf) { + vty_out(vty, "The vrf NAME specified: %s does not exist\n", + vrf_name ? vrf_name : VRF_DEFAULT_NAME); + return CMD_WARNING; + } + + ifp = if_lookup_by_name_vrf(ifname, vrf); + if (ifp == NULL) { + vty_out(vty, "%% Can't find interface %s\n", ifname); + return CMD_WARNING; + } + + sharp_zebra_send_arp(ifp, &prefix); + + return CMD_SUCCESS; +} + +DEFPY (import_te, + import_te_cmd, + "sharp import-te", + SHARP_STR + "Import Traffic Engineering\n") +{ + sg.ted = ls_ted_new(1, "Sharp", 0); + sharp_zebra_register_te(); + + return CMD_SUCCESS; +} + +static void sharp_srv6_locator_chunk_free(struct prefix_ipv6 *chunk) +{ + prefix_ipv6_free(&chunk); +} + +DEFPY (sharp_srv6_manager_get_locator_chunk, + sharp_srv6_manager_get_locator_chunk_cmd, + "sharp srv6-manager get-locator-chunk NAME$locator_name", + SHARP_STR + "Segment-Routing IPv6\n" + "Get SRv6 locator-chunk\n" + "SRv6 Locator name\n") +{ + int ret; + struct listnode *node; + struct sharp_srv6_locator *loc; + struct sharp_srv6_locator *loc_found = NULL; + + for (ALL_LIST_ELEMENTS_RO(sg.srv6_locators, node, loc)) { + if (strcmp(loc->name, locator_name)) + continue; + loc_found = loc; + break; + } + if (!loc_found) { + loc = XCALLOC(MTYPE_SRV6_LOCATOR, + sizeof(struct sharp_srv6_locator)); + loc->chunks = list_new(); + loc->chunks->del = + (void (*)(void *))sharp_srv6_locator_chunk_free; + snprintf(loc->name, SRV6_LOCNAME_SIZE, "%s", locator_name); + listnode_add(sg.srv6_locators, loc); + } + + ret = sharp_zebra_srv6_manager_get_locator_chunk(locator_name); + if (ret < 0) + return CMD_WARNING_CONFIG_FAILED; + + return CMD_SUCCESS; +} + +DEFUN (show_sharp_ted, + show_sharp_ted_cmd, + "show sharp ted [<vertex [A.B.C.D]|edge [A.B.C.D]|subnet [A.B.C.D/M]>] [verbose|json]", + SHOW_STR + SHARP_STR + "Traffic Engineering Database\n" + "MPLS-TE Vertex\n" + "MPLS-TE router ID (as an IP address)\n" + "MPLS-TE Edge\n" + "MPLS-TE Edge ID (as an IP address)\n" + "MPLS-TE Subnet\n" + "MPLS-TE Subnet ID (as an IP prefix)\n" + "Verbose output\n" + JSON_STR) +{ + int idx = 0; + struct in_addr ip_addr; + struct prefix pref; + struct ls_vertex *vertex; + struct ls_edge *edge; + struct ls_subnet *subnet; + uint64_t key; + struct ls_edge_key ekey; + bool verbose = false; + bool uj = use_json(argc, argv); + json_object *json = NULL; + + if (sg.ted == NULL) { + vty_out(vty, "MPLS-TE import is not enabled\n"); + return CMD_WARNING; + } + + if (uj) + json = json_object_new_object(); + + if (argv[argc - 1]->arg && strmatch(argv[argc - 1]->text, "verbose")) + verbose = true; + + if (argv_find(argv, argc, "vertex", &idx)) { + /* Show Vertex */ + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx + 1]->arg, &ip_addr)) { + vty_out(vty, + "Specified Router ID %s is invalid\n", + argv[idx + 1]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Vertex from the Link State Database */ + key = ((uint64_t)ip_addr.s_addr) & 0xffffffff; + vertex = ls_find_vertex_by_key(sg.ted, key); + if (!vertex) { + vty_out(vty, "No vertex found for ID %pI4\n", + &ip_addr); + return CMD_WARNING; + } + } else + vertex = NULL; + + if (vertex) + ls_show_vertex(vertex, vty, json, verbose); + else + ls_show_vertices(sg.ted, vty, json, verbose); + + } else if (argv_find(argv, argc, "edge", &idx)) { + /* Show Edge */ + if (argv_find(argv, argc, "A.B.C.D", &idx)) { + if (!inet_aton(argv[idx]->arg, &ip_addr)) { + vty_out(vty, + "Specified Edge ID %s is invalid\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Edge from the Link State Database */ + ekey.family = AF_INET; + IPV4_ADDR_COPY(&ekey.k.addr, &ip_addr); + edge = ls_find_edge_by_key(sg.ted, ekey); + if (!edge) { + vty_out(vty, "No edge found for ID %pI4\n", + &ip_addr); + return CMD_WARNING; + } + } else + edge = NULL; + + if (edge) + ls_show_edge(edge, vty, json, verbose); + else + ls_show_edges(sg.ted, vty, json, verbose); + + } else if (argv_find(argv, argc, "subnet", &idx)) { + /* Show Subnet */ + if (argv_find(argv, argc, "A.B.C.D/M", &idx)) { + if (!str2prefix(argv[idx]->arg, &pref)) { + vty_out(vty, "Invalid prefix format %s\n", + argv[idx]->arg); + return CMD_WARNING_CONFIG_FAILED; + } + /* Get the Subnet from the Link State Database */ + subnet = ls_find_subnet(sg.ted, &pref); + if (!subnet) { + vty_out(vty, "No subnet found for ID %pFX\n", + &pref); + return CMD_WARNING; + } + } else + subnet = NULL; + + if (subnet) + ls_show_subnet(subnet, vty, json, verbose); + else + ls_show_subnets(sg.ted, vty, json, verbose); + + } else { + /* Show the complete TED */ + ls_show_ted(sg.ted, vty, json, verbose); + } + + if (uj) + vty_json(vty, json); + + return CMD_SUCCESS; +} + +DEFPY (sharp_srv6_manager_release_locator_chunk, + sharp_srv6_manager_release_locator_chunk_cmd, + "sharp srv6-manager release-locator-chunk NAME$locator_name", + SHARP_STR + "Segment-Routing IPv6\n" + "Release SRv6 locator-chunk\n" + "SRv6 Locator name\n") +{ + int ret; + struct listnode *loc_node; + struct sharp_srv6_locator *loc; + + for (ALL_LIST_ELEMENTS_RO(sg.srv6_locators, loc_node, loc)) { + if (!strcmp(loc->name, locator_name)) { + list_delete_all_node(loc->chunks); + list_delete(&loc->chunks); + listnode_delete(sg.srv6_locators, loc); + XFREE(MTYPE_SRV6_LOCATOR, loc); + break; + } + } + + ret = sharp_zebra_srv6_manager_release_locator_chunk(locator_name); + if (ret < 0) + return CMD_WARNING_CONFIG_FAILED; + + return CMD_SUCCESS; +} + +DEFPY (show_sharp_segment_routing_srv6, + show_sharp_segment_routing_srv6_cmd, + "show sharp segment-routing srv6 [json]", + SHOW_STR + SHARP_STR + "Segment-Routing\n" + "Segment-Routing IPv6\n" + JSON_STR) +{ + char str[256]; + struct listnode *loc_node; + struct listnode *chunk_node; + struct sharp_srv6_locator *loc; + struct prefix_ipv6 *chunk; + bool uj = use_json(argc, argv); + json_object *jo_locs = NULL; + json_object *jo_loc = NULL; + json_object *jo_chunks = NULL; + + if (uj) { + jo_locs = json_object_new_array(); + for (ALL_LIST_ELEMENTS_RO(sg.srv6_locators, loc_node, loc)) { + jo_loc = json_object_new_object(); + json_object_array_add(jo_locs, jo_loc); + json_object_string_add(jo_loc, "name", loc->name); + jo_chunks = json_object_new_array(); + json_object_object_add(jo_loc, "chunks", jo_chunks); + for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, + chunk)) { + prefix2str(chunk, str, sizeof(str)); + json_array_string_add(jo_chunks, str); + } + } + + vty_json(vty, jo_locs); + } else { + for (ALL_LIST_ELEMENTS_RO(sg.srv6_locators, loc_node, loc)) { + vty_out(vty, "Locator %s has %d prefix chunks\n", + loc->name, listcount(loc->chunks)); + for (ALL_LIST_ELEMENTS_RO(loc->chunks, chunk_node, + chunk)) { + prefix2str(chunk, str, sizeof(str)); + vty_out(vty, " %s\n", str); + } + vty_out(vty, "\n"); + } + } + + return CMD_SUCCESS; +} + +DEFPY (show_sharp_cspf, + show_sharp_cspf_cmd, + "show sharp cspf source <A.B.C.D$src4|X:X::X:X$src6> \ + destination <A.B.C.D$dst4|X:X::X:X$dst6> \ + <metric|te-metric|delay> (0-16777215)$cost \ + [rsv-bw (0-7)$cos BANDWIDTH$bw]", + SHOW_STR + SHARP_STR + "Constraint Shortest Path First path computation\n" + "Source of the path\n" + "IPv4 Source address in dot decimal A.B.C.D\n" + "IPv6 Source address as X:X:X:X\n" + "Destination of the path\n" + "IPv4 Destination address in dot decimal A.B.C.D\n" + "IPv6 Destination address as X:X:X:X\n" + "Maximum Metric\n" + "Maximum TE Metric\n" + "Maxim Delay\n" + "Value of Maximum cost\n" + "Reserved Bandwidth of this path\n" + "Class of Service or Priority level\n" + "Bytes/second (IEEE floating point format)\n") +{ + + struct cspf *algo; + struct constraints csts; + struct c_path *path; + struct listnode *node; + struct ls_edge *edge; + int idx; + + if (sg.ted == NULL) { + vty_out(vty, "MPLS-TE import is not enabled\n"); + return CMD_WARNING; + } + + if ((src4.s_addr != INADDR_ANY && dst4.s_addr == INADDR_ANY) || + (src4.s_addr == INADDR_ANY && dst4.s_addr != INADDR_ANY)) { + vty_out(vty, "Don't mix IPv4 and IPv6 addresses\n"); + return CMD_WARNING; + } + + idx = 6; + memset(&csts, 0, sizeof(struct constraints)); + if (argv_find(argv, argc, "metric", &idx)) { + csts.ctype = CSPF_METRIC; + csts.cost = cost; + } + idx = 6; + if (argv_find(argv, argc, "te-metric", &idx)) { + csts.ctype = CSPF_TE_METRIC; + csts.cost = cost; + } + idx = 6; + if (argv_find(argv, argc, "delay", &idx)) { + csts.ctype = CSPF_DELAY; + csts.cost = cost; + } + if (argc > 9) { + if (sscanf(bw, "%g", &csts.bw) != 1) { + vty_out(vty, "Bandwidth constraints: fscanf: %s\n", + safe_strerror(errno)); + return CMD_WARNING_CONFIG_FAILED; + } + csts.cos = cos; + } + + /* Initialize and call point-to-point Path computation */ + if (src4.s_addr != INADDR_ANY) + algo = cspf_init_v4(NULL, sg.ted, src4, dst4, &csts); + else + algo = cspf_init_v6(NULL, sg.ted, src6, dst6, &csts); + path = compute_p2p_path(algo, sg.ted); + cspf_del(algo); + + if (!path) { + vty_out(vty, "Path computation failed without error\n"); + return CMD_SUCCESS; + } + if (path->status != SUCCESS) { + vty_out(vty, "Path computation failed: %d\n", path->status); + cpath_del(path); + return CMD_SUCCESS; + } + + vty_out(vty, "Path computation success\n"); + vty_out(vty, "\tCost: %d\n", path->weight); + vty_out(vty, "\tEdges:"); + for (ALL_LIST_ELEMENTS_RO(path->edges, node, edge)) { + if (src4.s_addr != INADDR_ANY) + vty_out(vty, " %pI4", + &edge->attributes->standard.remote); + else + vty_out(vty, " %pI6", + &edge->attributes->standard.remote6); + } + vty_out(vty, "\n"); + cpath_del(path); + return CMD_SUCCESS; +} + +static struct interface *if_lookup_vrf_all(const char *ifname) +{ + struct interface *ifp; + struct vrf *vrf; + + RB_FOREACH(vrf, vrf_name_head, &vrfs_by_name) { + ifp = if_lookup_by_name(ifname, vrf->vrf_id); + if (ifp) + return ifp; + } + + return NULL; +} + +DEFPY (sharp_interface_protodown, + sharp_interface_protodown_cmd, + "sharp interface IFNAME$ifname protodown", + SHARP_STR + INTERFACE_STR + IFNAME_STR + "Set interface protodown\n") +{ + struct interface *ifp; + + ifp = if_lookup_vrf_all(ifname); + + if (!ifp) { + vty_out(vty, "%% Can't find interface %s\n", ifname); + return CMD_WARNING; + } + + if (sharp_zebra_send_interface_protodown(ifp, true) != 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFPY (no_sharp_interface_protodown, + no_sharp_interface_protodown_cmd, + "no sharp interface IFNAME$ifname protodown", + NO_STR + SHARP_STR + INTERFACE_STR + IFNAME_STR + "Set interface protodown\n") +{ + struct interface *ifp; + + ifp = if_lookup_vrf_all(ifname); + + if (!ifp) { + vty_out(vty, "%% Can't find interface %s\n", ifname); + return CMD_WARNING; + } + + if (sharp_zebra_send_interface_protodown(ifp, false) != 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +DEFPY (tc_filter_rate, + tc_filter_rate_cmd, + "sharp tc dev IFNAME$ifname \ + source <A.B.C.D/M|X:X::X:X/M>$src \ + destination <A.B.C.D/M|X:X::X:X/M>$dst \ + ip-protocol <tcp|udp>$ip_proto \ + src-port (1-65535)$src_port \ + dst-port (1-65535)$dst_port \ + rate RATE$ratestr", + SHARP_STR + "Traffic control\n" + "TC interface (for qdisc, class, filter)\n" + "TC interface name\n" + "TC filter source\n" + "TC filter source IPv4 prefix\n" + "TC filter source IPv6 prefix\n" + "TC filter destination\n" + "TC filter destination IPv4 prefix\n" + "TC filter destination IPv6 prefix\n" + "TC filter IP protocol\n" + "TC filter IP protocol TCP\n" + "TC filter IP protocol UDP\n" + "TC filter source port\n" + "TC filter source port\n" + "TC filter destination port\n" + "TC filter destination port\n" + "TC rate\n" + "TC rate number (bits/s) or rate string (suffixed with Bps or bit)\n") +{ + struct interface *ifp; + struct protoent *p; + uint64_t rate; + + ifp = if_lookup_vrf_all(ifname); + + if (!ifp) { + vty_out(vty, "%% Can't find interface %s\n", ifname); + return CMD_WARNING; + } + + p = getprotobyname(ip_proto); + if (!p) { + vty_out(vty, "Unable to convert %s to proto id\n", ip_proto); + return CMD_WARNING; + } + + if (tc_getrate(ratestr, &rate) != 0) { + vty_out(vty, "Unable to convert %s to rate\n", ratestr); + return CMD_WARNING; + } + + if (sharp_zebra_send_tc_filter_rate(ifp, src, dst, p->p_proto, src_port, + dst_port, rate) != 0) + return CMD_WARNING; + + return CMD_SUCCESS; +} + +void sharp_vty_init(void) +{ + install_element(ENABLE_NODE, &install_routes_data_dump_cmd); + install_element(ENABLE_NODE, &install_routes_cmd); + install_element(ENABLE_NODE, &install_seg6_routes_cmd); + install_element(ENABLE_NODE, &install_seg6local_routes_cmd); + install_element(ENABLE_NODE, &remove_routes_cmd); + install_element(ENABLE_NODE, &vrf_label_cmd); + install_element(ENABLE_NODE, &sharp_nht_data_dump_cmd); + install_element(ENABLE_NODE, &watch_redistribute_cmd); + install_element(ENABLE_NODE, &watch_nexthop_v6_cmd); + install_element(ENABLE_NODE, &watch_nexthop_v4_cmd); + install_element(ENABLE_NODE, &sharp_lsp_prefix_v4_cmd); + install_element(ENABLE_NODE, &sharp_remove_lsp_prefix_v4_cmd); + install_element(ENABLE_NODE, &logpump_cmd); + install_element(ENABLE_NODE, &create_session_cmd); + install_element(ENABLE_NODE, &remove_session_cmd); + install_element(ENABLE_NODE, &send_opaque_cmd); + install_element(ENABLE_NODE, &send_opaque_unicast_cmd); + install_element(ENABLE_NODE, &send_opaque_reg_cmd); + install_element(ENABLE_NODE, &send_opaque_notif_reg_cmd); + install_element(ENABLE_NODE, &neigh_discover_cmd); + install_element(ENABLE_NODE, &import_te_cmd); + + install_element(ENABLE_NODE, &show_debugging_sharpd_cmd); + install_element(ENABLE_NODE, &show_sharp_ted_cmd); + install_element(ENABLE_NODE, &show_sharp_cspf_cmd); + + install_element(ENABLE_NODE, &sharp_srv6_manager_get_locator_chunk_cmd); + install_element(ENABLE_NODE, + &sharp_srv6_manager_release_locator_chunk_cmd); + install_element(ENABLE_NODE, &show_sharp_segment_routing_srv6_cmd); + + install_element(ENABLE_NODE, &sharp_interface_protodown_cmd); + install_element(ENABLE_NODE, &no_sharp_interface_protodown_cmd); + + install_element(ENABLE_NODE, &tc_filter_rate_cmd); + + return; +} |