/* * 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" #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; 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; 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) { if (src_str) snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_SRC_NH_KEY_NO_DISTANCE_XPATH, "frr-staticd:staticd", "staticd", svrf, buf_prefix, yang_afi_safi_value2identity(afi, safi), buf_src_prefix, table_id, buf_nh_type, nh_svrf, buf_gate_str, ifname); else snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, "frr-staticd:staticd", "staticd", svrf, buf_prefix, yang_afi_safi_value2identity(afi, safi), table_id, buf_nh_type, nh_svrf, buf_gate_str, ifname); /* * 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 (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, table_id, 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), 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, 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_NO_DISTANCE_XPATH, "frr-staticd:staticd", "staticd", svrf, buf_prefix, yang_afi_safi_value2identity(afi, safi), buf_src_prefix, table_id, buf_nh_type, nh_svrf, buf_gate_str, ifname); else snprintf(ab_xpath, sizeof(ab_xpath), FRR_DEL_S_ROUTE_NH_KEY_NO_DISTANCE_XPATH, "frr-staticd:staticd", "staticd", svrf, buf_prefix, yang_afi_safi_value2identity(afi, safi), table_id, buf_nh_type, nh_svrf, buf_gate_str, ifname); 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, 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); } /* 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); } 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")); 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}]", 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); }