summaryrefslogtreecommitdiffstats
path: root/staticd/static_vty.c
diff options
context:
space:
mode:
Diffstat (limited to 'staticd/static_vty.c')
-rw-r--r--staticd/static_vty.c1658
1 files changed, 1658 insertions, 0 deletions
diff --git a/staticd/static_vty.c b/staticd/static_vty.c
new file mode 100644
index 0000000..e94acba
--- /dev/null
+++ b/staticd/static_vty.c
@@ -0,0 +1,1658 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * STATICd - vty code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ */
+#include <zebra.h>
+
+#include "command.h"
+#include "vty.h"
+#include "vrf.h"
+#include "prefix.h"
+#include "nexthop.h"
+#include "table.h"
+#include "srcdest_table.h"
+#include "mgmt_be_client.h"
+#include "mpls.h"
+#include "northbound.h"
+#include "libfrr.h"
+#include "routing_nb.h"
+#include "northbound_cli.h"
+
+#include "static_vrf.h"
+#include "static_vty.h"
+#include "static_routes.h"
+#include "static_debug.h"
+#include "staticd/static_vty_clippy.c"
+#include "static_nb.h"
+
+#define STATICD_STR "Static route daemon\n"
+
+/** All possible route parameters available in CLI. */
+struct static_route_args {
+ /** "no" command? */
+ bool delete;
+ /** Is VRF obtained from XPath? */
+ bool xpath_vrf;
+
+ bool onlink;
+ afi_t afi;
+ safi_t safi;
+
+ const char *vrf;
+ const char *nexthop_vrf;
+ const char *prefix;
+ const char *prefix_mask;
+ const char *source;
+ const char *gateway;
+ const char *interface_name;
+ const char *segs;
+ const char *flag;
+ const char *tag;
+ const char *distance;
+ const char *label;
+ const char *table;
+ const char *color;
+
+ bool bfd;
+ bool bfd_multi_hop;
+ const char *bfd_source;
+ const char *bfd_profile;
+
+ const char *input;
+};
+
+static int static_route_nb_run(struct vty *vty, struct static_route_args *args)
+{
+ int ret;
+ struct prefix p, src;
+ struct in_addr mask;
+ enum static_nh_type type;
+ const char *bh_type;
+ char xpath_prefix[XPATH_MAXLEN];
+ char xpath_nexthop[XPATH_MAXLEN];
+ char xpath_mpls[XPATH_MAXLEN];
+ char xpath_label[XPATH_MAXLEN];
+ char xpath_segs[XPATH_MAXLEN];
+ char xpath_seg[XPATH_MAXLEN];
+ char ab_xpath[XPATH_MAXLEN];
+ char buf_prefix[PREFIX_STRLEN];
+ char buf_src_prefix[PREFIX_STRLEN] = {};
+ char buf_nh_type[PREFIX_STRLEN] = {};
+ char buf_tag[PREFIX_STRLEN];
+ uint8_t label_stack_id = 0;
+ uint8_t segs_stack_id = 0;
+ char *orig_label = NULL, *orig_seg = NULL;
+ const char *buf_gate_str;
+ uint8_t distance = ZEBRA_STATIC_DISTANCE_DEFAULT;
+ route_tag_t tag = 0;
+ uint32_t table_id = 0;
+ const struct lyd_node *dnode;
+ const struct lyd_node *vrf_dnode;
+
+ if (args->xpath_vrf) {
+ vrf_dnode = yang_dnode_get(vty->candidate_config->dnode,
+ VTY_CURR_XPATH);
+ if (vrf_dnode == NULL) {
+ vty_out(vty,
+ "%% Failed to get vrf dnode in candidate db\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ args->vrf = yang_dnode_get_string(vrf_dnode, "./name");
+ } else {
+ if (args->vrf == NULL)
+ args->vrf = VRF_DEFAULT_NAME;
+ }
+ if (args->nexthop_vrf == NULL)
+ args->nexthop_vrf = args->vrf;
+
+ if (args->interface_name &&
+ !strcasecmp(args->interface_name, "Null0")) {
+ args->flag = "Null0";
+ args->interface_name = NULL;
+ }
+
+ assert(!!str2prefix(args->prefix, &p));
+
+ switch (args->afi) {
+ case AFI_IP:
+ /* Cisco like mask notation. */
+ if (args->prefix_mask) {
+ assert(inet_pton(AF_INET, args->prefix_mask, &mask) ==
+ 1);
+ p.prefixlen = ip_masklen(mask);
+ }
+ break;
+ case AFI_IP6:
+ /* srcdest routing */
+ if (args->source)
+ assert(!!str2prefix(args->source, &src));
+ break;
+ case AFI_L2VPN:
+ case AFI_UNSPEC:
+ case AFI_MAX:
+ break;
+ }
+
+ /* Apply mask for given prefix. */
+ apply_mask(&p);
+ prefix2str(&p, buf_prefix, sizeof(buf_prefix));
+
+ if (args->bfd && args->gateway == NULL) {
+ vty_out(vty, "%% Route monitoring requires a gateway\n");
+ return CMD_WARNING_CONFIG_FAILED;
+ }
+
+ if (args->source)
+ prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix));
+ if (args->gateway)
+ buf_gate_str = args->gateway;
+ else
+ buf_gate_str = "";
+
+ if (args->gateway == NULL && args->interface_name == NULL) {
+ type = STATIC_BLACKHOLE;
+ /* If this is blackhole/reject flagged route, then
+ * specify interface_name with the value of what was really
+ * entered.
+ * interface_name will be validated later in NB functions
+ * to check if we don't create blackhole/reject routes that
+ * match the real interface names.
+ * E.g.: `ip route 10.0.0.1/32 bla` will create a blackhole
+ * route despite the real interface named `bla` exists.
+ */
+ if (args->input)
+ args->interface_name = args->input;
+ } else if (args->gateway && args->interface_name) {
+ if (args->afi == AFI_IP)
+ type = STATIC_IPV4_GATEWAY_IFNAME;
+ else
+ type = STATIC_IPV6_GATEWAY_IFNAME;
+ } else if (args->interface_name)
+ type = STATIC_IFNAME;
+ else {
+ if (args->afi == AFI_IP)
+ type = STATIC_IPV4_GATEWAY;
+ else
+ type = STATIC_IPV6_GATEWAY;
+ }
+
+ /* Administrative distance. */
+ if (args->distance)
+ distance = strtol(args->distance, NULL, 10);
+
+ /* tag */
+ if (args->tag)
+ tag = strtoul(args->tag, NULL, 10);
+
+ /* TableID */
+ if (args->table)
+ table_id = strtol(args->table, NULL, 10);
+
+ static_get_nh_type(type, buf_nh_type, sizeof(buf_nh_type));
+ if (!args->delete) {
+ if (args->source)
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH,
+ "frr-staticd:staticd", "staticd", args->vrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(args->afi,
+ args->safi),
+ buf_src_prefix, table_id, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str,
+ args->interface_name);
+ else
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH,
+ "frr-staticd:staticd", "staticd", args->vrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(args->afi,
+ args->safi),
+ table_id, buf_nh_type, args->nexthop_vrf,
+ buf_gate_str, args->interface_name);
+
+ /*
+ * If there's already the same nexthop but with a different
+ * distance, then remove it for the replacement.
+ */
+ dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath);
+ if (dnode) {
+ dnode = yang_get_subtree_with_no_sibling(dnode);
+ assert(dnode);
+ yang_dnode_get_path(dnode, ab_xpath, XPATH_MAXLEN);
+
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_DESTROY,
+ NULL);
+ }
+
+ /* route + path procesing */
+ if (args->source)
+ snprintf(xpath_prefix, sizeof(xpath_prefix),
+ FRR_S_ROUTE_SRC_INFO_KEY_XPATH,
+ "frr-staticd:staticd", "staticd", args->vrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(args->afi,
+ args->safi),
+ buf_src_prefix, table_id, distance);
+ else
+ snprintf(xpath_prefix, sizeof(xpath_prefix),
+ FRR_STATIC_ROUTE_INFO_KEY_XPATH,
+ "frr-staticd:staticd", "staticd", args->vrf,
+ buf_prefix,
+ yang_afi_safi_value2identity(args->afi,
+ args->safi),
+ table_id, distance);
+
+ nb_cli_enqueue_change(vty, xpath_prefix, NB_OP_CREATE, NULL);
+
+ /* Tag processing */
+ snprintf(buf_tag, sizeof(buf_tag), "%u", tag);
+ strlcpy(ab_xpath, xpath_prefix, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_PATH_TAG_XPATH,
+ sizeof(ab_xpath));
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, buf_tag);
+
+ /* nexthop processing */
+
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_STATIC_ROUTE_NH_KEY_XPATH, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str, args->interface_name);
+ strlcpy(xpath_nexthop, xpath_prefix, sizeof(xpath_nexthop));
+ strlcat(xpath_nexthop, ab_xpath, sizeof(xpath_nexthop));
+ nb_cli_enqueue_change(vty, xpath_nexthop, NB_OP_CREATE, NULL);
+
+ if (type == STATIC_BLACKHOLE) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_BH_XPATH,
+ sizeof(ab_xpath));
+
+ /* Route flags */
+ if (args->flag) {
+ switch (args->flag[0]) {
+ case 'r':
+ bh_type = "reject";
+ break;
+ case 'b':
+ bh_type = "unspec";
+ break;
+ case 'N':
+ bh_type = "null";
+ break;
+ default:
+ bh_type = NULL;
+ break;
+ }
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, bh_type);
+ } else {
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "null");
+ }
+ }
+ if (type == STATIC_IPV4_GATEWAY_IFNAME
+ || type == STATIC_IPV6_GATEWAY_IFNAME) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_ONLINK_XPATH,
+ sizeof(ab_xpath));
+
+ if (args->onlink)
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "true");
+ else
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY, "false");
+ }
+ if (type == STATIC_IPV4_GATEWAY ||
+ type == STATIC_IPV6_GATEWAY ||
+ type == STATIC_IPV4_GATEWAY_IFNAME ||
+ type == STATIC_IPV6_GATEWAY_IFNAME) {
+ strlcpy(ab_xpath, xpath_nexthop, sizeof(ab_xpath));
+ strlcat(ab_xpath, FRR_STATIC_ROUTE_NH_COLOR_XPATH,
+ sizeof(ab_xpath));
+ if (args->color)
+ nb_cli_enqueue_change(vty, ab_xpath,
+ NB_OP_MODIFY,
+ args->color);
+ }
+ if (args->label) {
+ /* copy of label string (start) */
+ char *ostr;
+ /* pointer to next segment */
+ char *nump;
+
+ strlcpy(xpath_mpls, xpath_nexthop, sizeof(xpath_mpls));
+ strlcat(xpath_mpls, FRR_STATIC_ROUTE_NH_LABEL_XPATH,
+ sizeof(xpath_mpls));
+
+ nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
+ NULL);
+
+ orig_label = ostr = XSTRDUP(MTYPE_TMP, args->label);
+ while ((nump = strsep(&ostr, "/")) != NULL) {
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_STATIC_ROUTE_NHLB_KEY_XPATH,
+ label_stack_id);
+ strlcpy(xpath_label, xpath_mpls,
+ sizeof(xpath_label));
+ strlcat(xpath_label, ab_xpath,
+ sizeof(xpath_label));
+ nb_cli_enqueue_change(vty, xpath_label,
+ NB_OP_MODIFY, nump);
+ label_stack_id++;
+ }
+ } else {
+ strlcpy(xpath_mpls, xpath_nexthop, sizeof(xpath_mpls));
+ strlcat(xpath_mpls, FRR_STATIC_ROUTE_NH_LABEL_XPATH,
+ sizeof(xpath_mpls));
+ nb_cli_enqueue_change(vty, xpath_mpls, NB_OP_DESTROY,
+ NULL);
+ }
+ if (args->segs) {
+ /* copy of seg string (start) */
+ char *ostr;
+ /* pointer to next segment */
+ char *nump;
+
+ strlcpy(xpath_segs, xpath_nexthop, sizeof(xpath_segs));
+ strlcat(xpath_segs, FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH,
+ sizeof(xpath_segs));
+
+ nb_cli_enqueue_change(vty, xpath_segs, NB_OP_DESTROY,
+ NULL);
+
+ orig_seg = ostr = XSTRDUP(MTYPE_TMP, args->segs);
+ while ((nump = strsep(&ostr, "/")) != NULL) {
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_STATIC_ROUTE_NH_SRV6_KEY_SEG_XPATH,
+ segs_stack_id);
+ strlcpy(xpath_seg, xpath_segs,
+ sizeof(xpath_seg));
+ strlcat(xpath_seg, ab_xpath, sizeof(xpath_seg));
+ nb_cli_enqueue_change(vty, xpath_seg,
+ NB_OP_MODIFY, nump);
+ segs_stack_id++;
+ }
+ } else {
+ strlcpy(xpath_segs, xpath_nexthop, sizeof(xpath_segs));
+ strlcat(xpath_segs, FRR_STATIC_ROUTE_NH_SRV6_SEGS_XPATH,
+ sizeof(xpath_segs));
+ nb_cli_enqueue_change(vty, xpath_segs, NB_OP_DESTROY,
+ NULL);
+ }
+ if (args->bfd) {
+ char xpath_bfd[XPATH_MAXLEN];
+
+ if (args->bfd_source) {
+ strlcpy(xpath_bfd, xpath_nexthop,
+ sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/source",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd,
+ NB_OP_MODIFY,
+ args->bfd_source);
+ }
+
+ strlcpy(xpath_bfd, xpath_nexthop, sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/multi-hop",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd, NB_OP_MODIFY,
+ args->bfd_multi_hop ? "true"
+ : "false");
+
+ if (args->bfd_profile) {
+ strlcpy(xpath_bfd, xpath_nexthop,
+ sizeof(xpath_bfd));
+ strlcat(xpath_bfd,
+ "/frr-staticd:bfd-monitoring/profile",
+ sizeof(xpath_bfd));
+ nb_cli_enqueue_change(vty, xpath_bfd,
+ NB_OP_MODIFY,
+ args->bfd_profile);
+ }
+ }
+
+ ret = nb_cli_apply_changes(vty, "%s", xpath_prefix);
+
+ if (orig_label)
+ XFREE(MTYPE_TMP, orig_label);
+ if (orig_seg)
+ XFREE(MTYPE_TMP, orig_seg);
+ } else {
+ if (args->source) {
+ if (args->distance)
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH,
+ "frr-staticd:staticd", "staticd",
+ args->vrf, buf_prefix,
+ yang_afi_safi_value2identity(
+ args->afi, args->safi),
+ buf_src_prefix, table_id, distance,
+ buf_nh_type, args->nexthop_vrf,
+ buf_gate_str, args->interface_name);
+ else
+ snprintf(
+ ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH,
+ "frr-staticd:staticd", "staticd",
+ args->vrf, buf_prefix,
+ yang_afi_safi_value2identity(
+ args->afi, args->safi),
+ buf_src_prefix, table_id, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str,
+ args->interface_name);
+ } else {
+ if (args->distance)
+ snprintf(ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_NH_KEY_XPATH,
+ "frr-staticd:staticd", "staticd",
+ args->vrf, buf_prefix,
+ yang_afi_safi_value2identity(
+ args->afi, args->safi),
+ table_id, distance, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str,
+ args->interface_name);
+ else
+ snprintf(
+ ab_xpath, sizeof(ab_xpath),
+ FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH,
+ "frr-staticd:staticd", "staticd",
+ args->vrf, buf_prefix,
+ yang_afi_safi_value2identity(
+ args->afi, args->safi),
+ table_id, buf_nh_type,
+ args->nexthop_vrf, buf_gate_str,
+ args->interface_name);
+ }
+
+ dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath);
+ if (!dnode) {
+ vty_out(vty,
+ "%% Refusing to remove a non-existent route\n");
+ return CMD_SUCCESS;
+ }
+
+ dnode = yang_get_subtree_with_no_sibling(dnode);
+ assert(dnode);
+ yang_dnode_get_path(dnode, ab_xpath, XPATH_MAXLEN);
+
+ nb_cli_enqueue_change(vty, ab_xpath, NB_OP_DESTROY, NULL);
+ ret = nb_cli_apply_changes(vty, "%s", ab_xpath);
+ }
+
+ return ret;
+}
+
+/* Static unicast routes for multicast RPF lookup. */
+DEFPY_YANG (ip_mroute_dist,
+ ip_mroute_dist_cmd,
+ "[no] ip mroute A.B.C.D/M$prefix <A.B.C.D$gate|INTERFACE$ifname> [{"
+ "(1-255)$distance"
+ "|bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}]"
+ "}]",
+ NO_STR
+ IP_STR
+ "Configure static unicast route into MRIB for multicast RPF lookup\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "Nexthop address\n"
+ "Nexthop interface name\n"
+ "Distance\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_MULTICAST,
+ .prefix = prefix_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .distance = distance_str,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+/* Static route configuration. */
+DEFPY_YANG(ip_route_blackhole,
+ ip_route_blackhole_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ int idx_flag = 0;
+
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .flag = flag,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .vrf = vrf,
+ };
+
+ if (flag && argv_find(argv, argc, flag, &idx_flag))
+ args.input = argv[idx_flag]->arg;
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route_blackhole_vrf,
+ ip_route_blackhole_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ int idx_flag = 0;
+
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .flag = flag,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .xpath_vrf = true,
+ };
+
+ /*
+ * Coverity is complaining that prefix could
+ * be dereferenced, but we know that prefix will
+ * valid. Add an assert to make it happy
+ */
+ assert(args.prefix);
+
+ if (flag && argv_find(argv, argc, flag, &idx_flag))
+ args.input = argv[idx_flag]->arg;
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route_address_interface,
+ ip_route_address_interface_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ A.B.C.D$gate \
+ <INTERFACE|Null0>$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |onlink$onlink \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n"
+ VRF_CMD_HELP_STR
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .onlink = !!onlink,
+ .vrf = vrf,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route_address_interface_vrf,
+ ip_route_address_interface_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ A.B.C.D$gate \
+ <INTERFACE|Null0>$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |onlink$onlink \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n"
+ VRF_CMD_HELP_STR
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .onlink = !!onlink,
+ .xpath_vrf = true,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route,
+ ip_route_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <A.B.C.D$gate|<INTERFACE|Null0>$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n"
+ VRF_CMD_HELP_STR
+ "SR-TE color\n"
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .vrf = vrf,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ip_route_vrf,
+ ip_route_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <A.B.C.D$gate|<INTERFACE|Null0>$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source A.B.C.D$bfd_source|profile BFDPROF$bfd_profile}] \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "IP gateway address\n"
+ "IP gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n"
+ VRF_CMD_HELP_STR
+ "SR-TE color\n"
+ "The SR-TE color to configure\n"
+ BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR
+ BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR
+ BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR)
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix,
+ .prefix_mask = mask_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .xpath_vrf = true,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_blackhole,
+ ipv6_route_blackhole_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ int idx_flag = 0;
+
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .flag = flag,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .vrf = vrf,
+ };
+
+ if (flag && argv_find(argv, argc, flag, &idx_flag))
+ args.input = argv[idx_flag]->arg;
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_blackhole_vrf,
+ ipv6_route_blackhole_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ int idx_flag = 0;
+
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .flag = flag,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .xpath_vrf = true,
+ };
+
+ /*
+ * Coverity is complaining that prefix could
+ * be dereferenced, but we know that prefix will
+ * valid. Add an assert to make it happy
+ */
+ assert(args.prefix);
+
+ if (flag && argv_find(argv, argc, flag, &idx_flag))
+ args.input = argv[idx_flag]->arg;
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_address_interface, ipv6_route_address_interface_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ X:X::X:X$gate \
+ <INTERFACE|Null0>$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |onlink$onlink \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
+ |segments WORD \
+ }]",
+ NO_STR IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n" VRF_CMD_HELP_STR
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n" BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR "Value of segs\n"
+ "Segs (SIDs)\n")
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .onlink = !!onlink,
+ .vrf = vrf,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ .segs = segments,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_address_interface_vrf,
+ ipv6_route_address_interface_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ X:X::X:X$gate \
+ <INTERFACE|Null0>$ifname \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |onlink$onlink \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
+ |segments WORD \
+ }]",
+ NO_STR IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n" MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n" VRF_CMD_HELP_STR
+ "Treat the nexthop as directly attached to the interface\n"
+ "SR-TE color\n"
+ "The SR-TE color to configure\n" BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR "Value of segs\n"
+ "Segs (SIDs)\n")
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .onlink = !!onlink,
+ .xpath_vrf = true,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ .segs = segments,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route, ipv6_route_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <X:X::X:X$gate|<INTERFACE|Null0>$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
+ |segments WORD \
+ }]",
+ NO_STR IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n" VRF_CMD_HELP_STR MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n"
+ "The SR-TE color to configure\n" BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR "Value of segs\n"
+ "Segs (SIDs)\n")
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .vrf = vrf,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ .segs = segments,
+
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+DEFPY_YANG(ipv6_route_vrf, ipv6_route_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <X:X::X:X$gate|<INTERFACE|Null0>$ifname> \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ |nexthop-vrf NAME \
+ |color (1-4294967295) \
+ |bfd$bfd [{multi-hop$bfd_multi_hop|source X:X::X:X$bfd_source|profile BFDPROF$bfd_profile}] \
+ |segments WORD \
+ }]",
+ NO_STR IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "IPv6 gateway address\n"
+ "IPv6 gateway interface name\n"
+ "Null interface\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this prefix\n" MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n" VRF_CMD_HELP_STR "SR-TE color\n"
+ "The SR-TE color to configure\n" BFD_INTEGRATION_STR
+ BFD_INTEGRATION_MULTI_HOP_STR BFD_INTEGRATION_SOURCE_STR
+ BFD_INTEGRATION_SOURCEV4_STR BFD_PROFILE_STR
+ BFD_PROFILE_NAME_STR "Value of segs\n"
+ "Segs (SIDs)\n")
+{
+ struct static_route_args args = {
+ .delete = !!no,
+ .afi = AFI_IP6,
+ .safi = SAFI_UNICAST,
+ .prefix = prefix_str,
+ .source = from_str,
+ .gateway = gate_str,
+ .interface_name = ifname,
+ .tag = tag_str,
+ .distance = distance_str,
+ .label = label,
+ .table = table_str,
+ .color = color_str,
+ .xpath_vrf = true,
+ .nexthop_vrf = nexthop_vrf,
+ .bfd = !!bfd,
+ .bfd_multi_hop = !!bfd_multi_hop,
+ .bfd_source = bfd_source_str,
+ .bfd_profile = bfd_profile,
+ .segs = segments,
+ };
+
+ return static_route_nb_run(vty, &args);
+}
+
+void static_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const char *vrf;
+
+ vrf = yang_dnode_get_string(dnode, "../vrf");
+ if (strcmp(vrf, VRF_DEFAULT_NAME))
+ vty_out(vty, "vrf %s\n", vrf);
+}
+
+void static_cli_show_end(struct vty *vty, const struct lyd_node *dnode)
+{
+ const char *vrf;
+
+ vrf = yang_dnode_get_string(dnode, "../vrf");
+ if (strcmp(vrf, VRF_DEFAULT_NAME))
+ vty_out(vty, "exit-vrf\n");
+}
+
+struct mpls_label_iter {
+ struct vty *vty;
+ bool first;
+};
+
+static int mpls_label_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct mpls_label_iter *iter = arg;
+
+ if (yang_dnode_exists(dnode, "./label")) {
+ if (iter->first)
+ vty_out(iter->vty, " label %s",
+ yang_dnode_get_string(dnode, "./label"));
+ else
+ vty_out(iter->vty, "/%s",
+ yang_dnode_get_string(dnode, "./label"));
+ iter->first = false;
+ }
+
+ return YANG_ITER_CONTINUE;
+}
+
+struct srv6_seg_iter {
+ struct vty *vty;
+ bool first;
+};
+
+static int srv6_seg_iter_cb(const struct lyd_node *dnode, void *arg)
+{
+ struct srv6_seg_iter *iter = arg;
+ char buffer[INET6_ADDRSTRLEN];
+ struct in6_addr cli_seg;
+
+ if (yang_dnode_exists(dnode, "./seg")) {
+ if (iter->first) {
+ yang_dnode_get_ipv6(&cli_seg, dnode, "./seg");
+ if (inet_ntop(AF_INET6, &cli_seg, buffer,
+ INET6_ADDRSTRLEN) == NULL) {
+ return 1;
+ }
+ vty_out(iter->vty, " segments %s", buffer);
+ } else {
+ yang_dnode_get_ipv6(&cli_seg, dnode, "./seg");
+ if (inet_ntop(AF_INET6, &cli_seg, buffer,
+ INET6_ADDRSTRLEN) == NULL) {
+ return 1;
+ }
+ vty_out(iter->vty, "/%s", buffer);
+ }
+ iter->first = false;
+ }
+
+ return YANG_ITER_CONTINUE;
+}
+
+static void nexthop_cli_show(struct vty *vty, const struct lyd_node *route,
+ const struct lyd_node *src,
+ const struct lyd_node *path,
+ const struct lyd_node *nexthop, bool show_defaults)
+{
+ const char *vrf;
+ const char *afi_safi;
+ afi_t afi;
+ safi_t safi;
+ enum static_nh_type nh_type;
+ enum static_blackhole_type bh_type;
+ uint32_t tag;
+ uint8_t distance;
+ struct mpls_label_iter iter;
+ struct srv6_seg_iter seg_iter;
+ const char *nexthop_vrf;
+ uint32_t table_id;
+ bool onlink;
+
+ vrf = yang_dnode_get_string(route, "../../vrf");
+
+ afi_safi = yang_dnode_get_string(route, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi, &afi, &safi);
+
+ if (afi == AFI_IP)
+ vty_out(vty, "%sip",
+ strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " ");
+ else
+ vty_out(vty, "%sipv6",
+ strmatch(vrf, VRF_DEFAULT_NAME) ? "" : " ");
+
+ if (safi == SAFI_UNICAST)
+ vty_out(vty, " route");
+ else
+ vty_out(vty, " mroute");
+
+ vty_out(vty, " %s", yang_dnode_get_string(route, "./prefix"));
+
+ if (src)
+ vty_out(vty, " from %s",
+ yang_dnode_get_string(src, "./src-prefix"));
+
+ nh_type = yang_dnode_get_enum(nexthop, "./nh-type");
+ switch (nh_type) {
+ case STATIC_IFNAME:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./interface"));
+ break;
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./gateway"));
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./gateway"));
+ vty_out(vty, " %s",
+ yang_dnode_get_string(nexthop, "./interface"));
+ break;
+ case STATIC_BLACKHOLE:
+ bh_type = yang_dnode_get_enum(nexthop, "./bh-type");
+ switch (bh_type) {
+ case STATIC_BLACKHOLE_DROP:
+ vty_out(vty, " blackhole");
+ break;
+ case STATIC_BLACKHOLE_NULL:
+ vty_out(vty, " Null0");
+ break;
+ case STATIC_BLACKHOLE_REJECT:
+ vty_out(vty, " reject");
+ break;
+ }
+ break;
+ }
+
+ if (yang_dnode_exists(path, "./tag")) {
+ tag = yang_dnode_get_uint32(path, "./tag");
+ if (tag != 0 || show_defaults)
+ vty_out(vty, " tag %" PRIu32, tag);
+ }
+
+ distance = yang_dnode_get_uint8(path, "./distance");
+ if (distance != ZEBRA_STATIC_DISTANCE_DEFAULT || show_defaults)
+ vty_out(vty, " %" PRIu8, distance);
+
+ iter.vty = vty;
+ iter.first = true;
+ yang_dnode_iterate(mpls_label_iter_cb, &iter, nexthop,
+ "./mpls-label-stack/entry");
+
+ seg_iter.vty = vty;
+ seg_iter.first = true;
+ yang_dnode_iterate(srv6_seg_iter_cb, &seg_iter, nexthop,
+ "./srv6-segs-stack/entry");
+
+ nexthop_vrf = yang_dnode_get_string(nexthop, "./vrf");
+ if (strcmp(vrf, nexthop_vrf))
+ vty_out(vty, " nexthop-vrf %s", nexthop_vrf);
+
+ table_id = yang_dnode_get_uint32(path, "./table-id");
+ if (table_id || show_defaults)
+ vty_out(vty, " table %" PRIu32, table_id);
+
+ if (yang_dnode_exists(nexthop, "./onlink")) {
+ onlink = yang_dnode_get_bool(nexthop, "./onlink");
+ if (onlink)
+ vty_out(vty, " onlink");
+ }
+
+ if (yang_dnode_exists(nexthop, "./srte-color"))
+ vty_out(vty, " color %s",
+ yang_dnode_get_string(nexthop, "./srte-color"));
+
+ if (yang_dnode_exists(nexthop, "./bfd-monitoring")) {
+ const struct lyd_node *bfd_dnode =
+ yang_dnode_get(nexthop, "./bfd-monitoring");
+
+ if (yang_dnode_get_bool(bfd_dnode, "./multi-hop")) {
+ vty_out(vty, " bfd multi-hop");
+
+ if (yang_dnode_exists(bfd_dnode, "./source"))
+ vty_out(vty, " source %s",
+ yang_dnode_get_string(bfd_dnode,
+ "./source"));
+ } else
+ vty_out(vty, " bfd");
+
+ if (yang_dnode_exists(bfd_dnode, "./profile"))
+ vty_out(vty, " profile %s",
+ yang_dnode_get_string(bfd_dnode, "./profile"));
+ }
+
+ vty_out(vty, "\n");
+}
+
+void static_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list");
+ const struct lyd_node *route =
+ yang_dnode_get_parent(path, "route-list");
+
+ nexthop_cli_show(vty, route, NULL, path, dnode, show_defaults);
+}
+
+void static_src_nexthop_cli_show(struct vty *vty, const struct lyd_node *dnode,
+ bool show_defaults)
+{
+ const struct lyd_node *path = yang_dnode_get_parent(dnode, "path-list");
+ const struct lyd_node *src = yang_dnode_get_parent(path, "src-list");
+ const struct lyd_node *route = yang_dnode_get_parent(src, "route-list");
+
+ nexthop_cli_show(vty, route, src, path, dnode, show_defaults);
+}
+
+int static_nexthop_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ enum static_nh_type nh_type1, nh_type2;
+ struct prefix prefix1, prefix2;
+ const char *vrf1, *vrf2;
+ int ret = 0;
+
+ nh_type1 = yang_dnode_get_enum(dnode1, "./nh-type");
+ nh_type2 = yang_dnode_get_enum(dnode2, "./nh-type");
+
+ if (nh_type1 != nh_type2)
+ return (int)nh_type1 - (int)nh_type2;
+
+ switch (nh_type1) {
+ case STATIC_IFNAME:
+ ret = if_cmp_name_func(
+ yang_dnode_get_string(dnode1, "./interface"),
+ yang_dnode_get_string(dnode2, "./interface"));
+ break;
+ case STATIC_IPV4_GATEWAY:
+ case STATIC_IPV6_GATEWAY:
+ yang_dnode_get_prefix(&prefix1, dnode1, "./gateway");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./gateway");
+ ret = prefix_cmp(&prefix1, &prefix2);
+ break;
+ case STATIC_IPV4_GATEWAY_IFNAME:
+ case STATIC_IPV6_GATEWAY_IFNAME:
+ yang_dnode_get_prefix(&prefix1, dnode1, "./gateway");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./gateway");
+ ret = prefix_cmp(&prefix1, &prefix2);
+ if (!ret)
+ ret = if_cmp_name_func(
+ yang_dnode_get_string(dnode1, "./interface"),
+ yang_dnode_get_string(dnode2, "./interface"));
+ break;
+ case STATIC_BLACKHOLE:
+ /* There's only one blackhole nexthop per route */
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ vrf1 = yang_dnode_get_string(dnode1, "./vrf");
+ if (strmatch(vrf1, "default"))
+ vrf1 = "";
+ vrf2 = yang_dnode_get_string(dnode2, "./vrf");
+ if (strmatch(vrf2, "default"))
+ vrf2 = "";
+
+ return if_cmp_name_func(vrf1, vrf2);
+}
+
+int static_route_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ const char *afi_safi1, *afi_safi2;
+ afi_t afi1, afi2;
+ safi_t safi1, safi2;
+ struct prefix prefix1, prefix2;
+
+ afi_safi1 = yang_dnode_get_string(dnode1, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi1, &afi1, &safi1);
+
+ afi_safi2 = yang_dnode_get_string(dnode2, "./afi-safi");
+ yang_afi_safi_identity2value(afi_safi2, &afi2, &safi2);
+
+ if (afi1 != afi2)
+ return (int)afi1 - (int)afi2;
+
+ if (safi1 != safi2)
+ return (int)safi1 - (int)safi2;
+
+ yang_dnode_get_prefix(&prefix1, dnode1, "./prefix");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./prefix");
+
+ return prefix_cmp(&prefix1, &prefix2);
+}
+
+int static_src_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ struct prefix prefix1, prefix2;
+
+ yang_dnode_get_prefix(&prefix1, dnode1, "./src-prefix");
+ yang_dnode_get_prefix(&prefix2, dnode2, "./src-prefix");
+
+ return prefix_cmp(&prefix1, &prefix2);
+}
+
+int static_path_list_cli_cmp(const struct lyd_node *dnode1,
+ const struct lyd_node *dnode2)
+{
+ uint32_t table_id1, table_id2;
+ uint8_t distance1, distance2;
+
+ table_id1 = yang_dnode_get_uint32(dnode1, "./table-id");
+ table_id2 = yang_dnode_get_uint32(dnode2, "./table-id");
+
+ if (table_id1 != table_id2)
+ return (int)table_id1 - (int)table_id2;
+
+ distance1 = yang_dnode_get_uint8(dnode1, "./distance");
+ distance2 = yang_dnode_get_uint8(dnode2, "./distance");
+
+ return (int)distance1 - (int)distance2;
+}
+
+#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY
+
+DEFPY_YANG(debug_staticd, debug_staticd_cmd,
+ "[no] debug static [{events$events|route$route|bfd$bfd}]",
+ NO_STR DEBUG_STR STATICD_STR
+ "Debug events\n"
+ "Debug route\n"
+ "Debug bfd\n")
+{
+ /* If no specific category, change all */
+ if (strmatch(argv[argc - 1]->text, "static"))
+ static_debug_set(vty->node, !no, true, true, true);
+ else
+ static_debug_set(vty->node, !no, !!events, !!route, !!bfd);
+
+ return CMD_SUCCESS;
+}
+
+DEFPY(staticd_show_bfd_routes, staticd_show_bfd_routes_cmd,
+ "show bfd static route [json]$isjson",
+ SHOW_STR
+ BFD_INTEGRATION_STR
+ STATICD_STR
+ ROUTE_STR
+ JSON_STR)
+{
+ static_bfd_show(vty, !!isjson);
+ return CMD_SUCCESS;
+}
+
+DEFUN_NOSH (show_debugging_static,
+ show_debugging_static_cmd,
+ "show debugging [static]",
+ SHOW_STR
+ DEBUG_STR
+ "Static Information\n")
+{
+ vty_out(vty, "Staticd debugging status\n");
+
+ static_debug_status_write(vty);
+
+ cmd_show_lib_debugs(vty);
+
+ return CMD_SUCCESS;
+}
+
+static struct cmd_node debug_node = {
+ .name = "debug",
+ .node = DEBUG_NODE,
+ .prompt = "",
+ .config_write = static_config_write_debug,
+};
+
+#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */
+
+void static_vty_init(void)
+{
+#ifndef INCLUDE_MGMTD_CMDDEFS_ONLY
+ install_node(&debug_node);
+ install_element(ENABLE_NODE, &debug_staticd_cmd);
+ install_element(CONFIG_NODE, &debug_staticd_cmd);
+ install_element(ENABLE_NODE, &show_debugging_static_cmd);
+ install_element(ENABLE_NODE, &staticd_show_bfd_routes_cmd);
+#endif /* ifndef INCLUDE_MGMTD_CMDDEFS_ONLY */
+
+ install_element(CONFIG_NODE, &ip_mroute_dist_cmd);
+
+ install_element(CONFIG_NODE, &ip_route_blackhole_cmd);
+ install_element(VRF_NODE, &ip_route_blackhole_vrf_cmd);
+ install_element(CONFIG_NODE, &ip_route_address_interface_cmd);
+ install_element(VRF_NODE, &ip_route_address_interface_vrf_cmd);
+ install_element(CONFIG_NODE, &ip_route_cmd);
+ install_element(VRF_NODE, &ip_route_vrf_cmd);
+
+ install_element(CONFIG_NODE, &ipv6_route_blackhole_cmd);
+ install_element(VRF_NODE, &ipv6_route_blackhole_vrf_cmd);
+ install_element(CONFIG_NODE, &ipv6_route_address_interface_cmd);
+ install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd);
+ install_element(CONFIG_NODE, &ipv6_route_cmd);
+ install_element(VRF_NODE, &ipv6_route_vrf_cmd);
+
+ mgmt_be_client_lib_vty_init();
+}