/* * STATICd - vty code * Copyright (C) 2018 Cumulus Networks, Inc. * Donald Sharp * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; see the file COPYING; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include "command.h" #include "vty.h" #include "vrf.h" #include "prefix.h" #include "nexthop.h" #include "table.h" #include "srcdest_table.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 *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; }; 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 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; 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; 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); 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++; } XFREE(MTYPE_TMP, ostr); } 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->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); } else { 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); 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 [{" "(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\ \ $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") { 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, }; return static_route_nb_run(vty, &args); } DEFPY_YANG(ip_route_blackhole_vrf, ip_route_blackhole_vrf_cmd, "[no] ip route\ \ $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") { 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); 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$gate \ $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$gate \ $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\ \ $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\ \ $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] \ $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") { 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, }; 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] \ $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") { 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); 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 \ $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}] \ }]", 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) { 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, }; 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 \ $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}] \ }]", 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) { 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, }; 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] \ $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}] \ }]", 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) { 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, }; 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] \ $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}] \ }]", 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) { 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, }; 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; } 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; 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"); 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; 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; return if_cmp_name_func(yang_dnode_get_string(dnode1, "./vrf"), yang_dnode_get_string(dnode2, "./vrf")); } 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; } 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, }; void static_vty_init(void) { install_node(&debug_node); 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); install_element(ENABLE_NODE, &show_debugging_static_cmd); install_element(ENABLE_NODE, &debug_staticd_cmd); install_element(CONFIG_NODE, &debug_staticd_cmd); install_element(ENABLE_NODE, &staticd_show_bfd_routes_cmd); }