/* * 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_memory.h" #include "static_vty.h" #include "static_routes.h" #include "static_debug.h" #ifndef VTYSH_EXTRACT_PL #include "staticd/static_vty_clippy.c" #endif #include "static_nb.h" #define STATICD_STR "Static route daemon\n" static int static_route_leak(struct vty *vty, const char *svrf, const char *nh_svrf, afi_t afi, safi_t safi, const char *negate, const char *dest_str, const char *mask_str, const char *src_str, const char *gate_str, const char *ifname, const char *flag_str, const char *tag_str, const char *distance_str, const char *label_str, const char *table_str, bool onlink, const char *color_str) { int ret; struct prefix p, src; struct in_addr mask; uint8_t 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]; char buf_tableid[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; memset(buf_src_prefix, 0, PREFIX_STRLEN); memset(buf_nh_type, 0, PREFIX_STRLEN); ret = str2prefix(dest_str, &p); if (ret <= 0) { vty_out(vty, "%% Malformed address\n"); return CMD_WARNING_CONFIG_FAILED; } switch (afi) { case AFI_IP: /* Cisco like mask notation. */ if (mask_str) { ret = inet_aton(mask_str, &mask); if (ret == 0) { vty_out(vty, "%% Malformed address\n"); return CMD_WARNING_CONFIG_FAILED; } p.prefixlen = ip_masklen(mask); } break; case AFI_IP6: /* srcdest routing */ if (src_str) { ret = str2prefix(src_str, &src); if (ret <= 0 || src.family != AF_INET6) { vty_out(vty, "%% Malformed source address\n"); return CMD_WARNING_CONFIG_FAILED; } } break; default: break; } /* Apply mask for given prefix. */ apply_mask(&p); prefix2str(&p, buf_prefix, sizeof(buf_prefix)); if (src_str) prefix2str(&src, buf_src_prefix, sizeof(buf_src_prefix)); if (gate_str) buf_gate_str = gate_str; else buf_gate_str = ""; if (gate_str == NULL && ifname == NULL) type = STATIC_BLACKHOLE; else if (gate_str && ifname) { if (afi == AFI_IP) type = STATIC_IPV4_GATEWAY_IFNAME; else type = STATIC_IPV6_GATEWAY_IFNAME; } else if (ifname) type = STATIC_IFNAME; else { if (afi == AFI_IP) type = STATIC_IPV4_GATEWAY; else type = STATIC_IPV6_GATEWAY; } /* Administrative distance. */ if (distance_str) distance = atoi(distance_str); else distance = ZEBRA_STATIC_DISTANCE_DEFAULT; /* tag */ if (tag_str) tag = strtoul(tag_str, NULL, 10); /* TableID */ if (table_str) table_id = atol(table_str); static_get_nh_type(type, buf_nh_type, PREFIX_STRLEN); if (!negate) { /* route + path procesing */ if (src_str) snprintf(xpath_prefix, sizeof(xpath_prefix), FRR_S_ROUTE_SRC_INFO_KEY_XPATH, "frr-staticd:staticd", "staticd", svrf, buf_prefix, yang_afi_safi_value2identity(afi, safi), buf_src_prefix, distance); else snprintf(xpath_prefix, sizeof(xpath_prefix), FRR_STATIC_ROUTE_INFO_KEY_XPATH, "frr-staticd:staticd", "staticd", svrf, buf_prefix, yang_afi_safi_value2identity(afi, safi), 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); /* Table-Id processing */ snprintf(buf_tableid, sizeof(buf_tableid), "%u", table_id); strlcpy(ab_xpath, xpath_prefix, sizeof(ab_xpath)); strlcat(ab_xpath, FRR_STATIC_ROUTE_PATH_TABLEID_XPATH, sizeof(ab_xpath)); nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, buf_tableid); /* nexthop processing */ snprintf(ab_xpath, sizeof(ab_xpath), FRR_STATIC_ROUTE_NH_KEY_XPATH, buf_nh_type, nh_svrf, buf_gate_str, ifname); 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 (flag_str) { switch (flag_str[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 (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 (color_str) nb_cli_enqueue_change(vty, ab_xpath, NB_OP_MODIFY, color_str); } if (label_str) { /* 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, label_str); 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); } ret = nb_cli_apply_changes(vty, xpath_prefix); } else { if (src_str) snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_SRC_NH_KEY_XPATH, "frr-staticd:staticd", "staticd", svrf, buf_prefix, yang_afi_safi_value2identity(afi, safi), buf_src_prefix, distance, buf_nh_type, nh_svrf, buf_gate_str, ifname); else snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_XPATH, "frr-staticd:staticd", "staticd", svrf, buf_prefix, yang_afi_safi_value2identity(afi, safi), distance, buf_nh_type, nh_svrf, buf_gate_str, ifname); dnode = yang_dnode_get(vty->candidate_config->dnode, ab_xpath); if (!dnode) return ret; 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, ab_xpath); } return ret; } static int static_route(struct vty *vty, afi_t afi, safi_t safi, const char *negate, const char *dest_str, const char *mask_str, const char *src_str, const char *gate_str, const char *ifname, const char *flag_str, const char *tag_str, const char *distance_str, const char *vrf_name, const char *label_str, const char *table_str) { if (!vrf_name) vrf_name = VRF_DEFAULT_NAME; return static_route_leak(vty, vrf_name, vrf_name, afi, safi, negate, dest_str, mask_str, src_str, gate_str, ifname, flag_str, tag_str, distance_str, label_str, table_str, false, NULL); } /* Write static route configuration. */ int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi, safi_t safi, const char *cmd) { char spacing[100]; struct route_node *rn; struct static_nexthop *nh; struct static_path *pn; struct route_table *stable; struct static_route_info *si; char buf[SRCDEST2STR_BUFFER]; int write = 0; struct stable_info *info; stable = svrf->stable[afi][safi]; if (stable == NULL) return write; snprintf(spacing, sizeof(spacing), "%s%s", (svrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", cmd); for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) { si = static_route_info_from_rnode(rn); if (!si) continue; info = static_get_stable_info(rn); frr_each(static_path_list, &si->path_list, pn) { frr_each(static_nexthop_list, &pn->nexthop_list, nh) { vty_out(vty, "%s %s", spacing, srcdest_rnode2str(rn, buf, sizeof(buf))); switch (nh->type) { case STATIC_IPV4_GATEWAY: vty_out(vty, " %pI4", &nh->addr.ipv4); break; case STATIC_IPV6_GATEWAY: vty_out(vty, " %s", inet_ntop(AF_INET6, &nh->addr.ipv6, buf, sizeof(buf))); break; case STATIC_IFNAME: vty_out(vty, " %s", nh->ifname); break; case STATIC_BLACKHOLE: switch (nh->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; case STATIC_IPV4_GATEWAY_IFNAME: vty_out(vty, " %s %s", inet_ntop(AF_INET, &nh->addr.ipv4, buf, sizeof(buf)), nh->ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: vty_out(vty, " %s %s", inet_ntop(AF_INET6, &nh->addr.ipv6, buf, sizeof(buf)), nh->ifname); break; } if (pn->tag) vty_out(vty, " tag %" ROUTE_TAG_PRI, pn->tag); if (pn->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) vty_out(vty, " %u", pn->distance); /* Label information */ if (nh->snh_label.num_labels) vty_out(vty, " label %s", mpls_label2str( nh->snh_label .num_labels, nh->snh_label.label, buf, sizeof(buf), 0)); if (!strmatch(nh->nh_vrfname, info->svrf->vrf->name)) vty_out(vty, " nexthop-vrf %s", nh->nh_vrfname); /* * table ID from VRF overrides * configured */ if (pn->table_id && svrf->vrf->data.l.table_id == RT_TABLE_MAIN) vty_out(vty, " table %u", pn->table_id); if (nh->onlink) vty_out(vty, " onlink"); /* * SR-TE color */ if (nh->color != 0) vty_out(vty, " color %u", nh->color); vty_out(vty, "\n"); write = 1; } } } return write; } /* 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]", 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") { return static_route(vty, AFI_IP, SAFI_MULTICAST, no, prefix_str, NULL, NULL, gate_str, ifname, NULL, NULL, distance_str, NULL, NULL, NULL); } /* 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") { return static_route(vty, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, NULL, NULL, flag, tag_str, distance_str, vrf, label, table_str); } 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") { const struct lyd_node *vrf_dnode; const char *vrfname; vrf_dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); if (!vrf_dnode) { vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } vrfname = yang_dnode_get_string(vrf_dnode, "./name"); /* * Coverity is complaining that prefix could * be dereferenced, but we know that prefix will * valid. Add an assert to make it happy */ assert(prefix); return static_route_leak(vty, vrfname, vrfname, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, NULL, NULL, flag, tag_str, distance_str, label, table_str, false, NULL); } 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) \ }]", 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") { const char *nh_vrf; const char *flag = NULL; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } if (!vrf) vrf = VRF_DEFAULT_NAME; if (nexthop_vrf) nh_vrf = nexthop_vrf; else nh_vrf = vrf; return static_route_leak(vty, vrf, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, !!onlink, color_str); } 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) \ }]", 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") { const char *nh_vrf; const char *flag = NULL; const struct lyd_node *vrf_dnode; const char *vrfname; vrf_dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); if (!vrf_dnode) { vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } vrfname = yang_dnode_get_string(vrf_dnode, "./name"); if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } if (nexthop_vrf) nh_vrf = nexthop_vrf; else nh_vrf = vrfname; return static_route_leak(vty, vrfname, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, !!onlink, color_str); } 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) \ }]", 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") { const char *nh_vrf; const char *flag = NULL; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } if (!vrf) vrf = VRF_DEFAULT_NAME; if (nexthop_vrf) nh_vrf = nexthop_vrf; else nh_vrf = vrf; return static_route_leak(vty, vrf, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false, color_str); } 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) \ }]", 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") { const char *nh_vrf; const char *flag = NULL; const struct lyd_node *vrf_dnode; const char *vrfname; vrf_dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); if (!vrf_dnode) { vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } vrfname = yang_dnode_get_string(vrf_dnode, "./name"); if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } if (nexthop_vrf) nh_vrf = nexthop_vrf; else nh_vrf = vrfname; return static_route_leak(vty, vrfname, nh_vrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false, color_str); } 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") { return static_route(vty, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, NULL, NULL, flag, tag_str, distance_str, vrf, label, table_str); } 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") { const struct lyd_node *vrf_dnode; const char *vrfname; vrf_dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); if (!vrf_dnode) { vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } vrfname = yang_dnode_get_string(vrf_dnode, "./name"); /* * Coverity is complaining that prefix could * be dereferenced, but we know that prefix will * valid. Add an assert to make it happy */ assert(prefix); return static_route_leak(vty, vrfname, vrfname, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, NULL, NULL, flag, tag_str, distance_str, label, table_str, false, NULL); } 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) \ }]", 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") { const char *nh_vrf; const char *flag = NULL; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } if (!vrf) vrf = VRF_DEFAULT_NAME; if (nexthop_vrf) nh_vrf = nexthop_vrf; else nh_vrf = vrf; return static_route_leak(vty, vrf, nh_vrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, !!onlink, color_str); } 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) \ }]", 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") { const char *nh_vrf; const char *flag = NULL; const struct lyd_node *vrf_dnode; const char *vrfname; vrf_dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); if (!vrf_dnode) { vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } vrfname = yang_dnode_get_string(vrf_dnode, "./name"); if (nexthop_vrf) nh_vrf = nexthop_vrf; else nh_vrf = vrfname; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } return static_route_leak(vty, vrfname, nh_vrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, !!onlink, color_str); } 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) \ }]", 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") { const char *nh_vrf; const char *flag = NULL; if (!vrf) vrf = VRF_DEFAULT_NAME; if (nexthop_vrf) nh_vrf = nexthop_vrf; else nh_vrf = vrf; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } return static_route_leak(vty, vrf, nh_vrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false, color_str); } 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) \ }]", 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") { const char *nh_vrf; const char *flag = NULL; const struct lyd_node *vrf_dnode; const char *vrfname; vrf_dnode = yang_dnode_get(vty->candidate_config->dnode, VTY_CURR_XPATH); if (!vrf_dnode) { vty_out(vty, "%% Failed to get vrf dnode in candidate db\n"); return CMD_WARNING_CONFIG_FAILED; } vrfname = yang_dnode_get_string(vrf_dnode, "./name"); if (nexthop_vrf) nh_vrf = nexthop_vrf; else nh_vrf = vrfname; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } return static_route_leak(vty, vrfname, nh_vrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false, color_str); } DEFPY_YANG(debug_staticd, debug_staticd_cmd, "[no] debug static [{events$events|route$route}]", NO_STR DEBUG_STR STATICD_STR "Debug events\n" "Debug route\n") { /* If no specific category, change all */ if (strmatch(argv[argc - 1]->text, "static")) static_debug_set(vty->node, !no, true, true); else static_debug_set(vty->node, !no, !!events, !!route); 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); 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); }