/* * 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 "static_vrf.h" #include "static_memory.h" #include "static_vty.h" #include "static_routes.h" #ifndef VTYSH_EXTRACT_PL #include "staticd/static_vty_clippy.c" #endif static struct static_vrf *static_vty_get_unknown_vrf(struct vty *vty, const char *vrf_name) { struct static_vrf *svrf; struct vrf *vrf; svrf = static_vrf_lookup_by_name(vrf_name); if (svrf) return svrf; vrf = vrf_get(VRF_UNKNOWN, vrf_name); if (!vrf) { vty_out(vty, "%% Could not create vrf %s\n", vrf_name); return NULL; } svrf = vrf->info; if (!svrf) { vty_out(vty, "%% Could not create vrf-info %s\n", vrf_name); return NULL; } /* Mark as having FRR configuration */ vrf_set_user_cfged(vrf); return svrf; } struct static_hold_route { char *vrf_name; char *nhvrf_name; afi_t afi; safi_t safi; char *dest_str; char *mask_str; char *src_str; char *gate_str; char *ifname; char *flag_str; char *tag_str; char *distance_str; char *label_str; char *table_str; bool onlink; /* processed & masked destination, used for config display */ struct prefix dest; }; static struct list *static_list; static int static_list_compare_helper(const char *s1, const char *s2) { /* extra (!s1 && !s2) to keep SA happy */ if (s1 == s2 || (!s1 && !s2)) return 0; if (!s1 && s2) return -1; if (s1 && !s2) return 1; return strcmp(s1, s2); } static void static_list_delete(struct static_hold_route *shr) { if (shr->vrf_name) XFREE(MTYPE_STATIC_ROUTE, shr->vrf_name); if (shr->nhvrf_name) XFREE(MTYPE_STATIC_ROUTE, shr->nhvrf_name); if (shr->dest_str) XFREE(MTYPE_STATIC_ROUTE, shr->dest_str); if (shr->mask_str) XFREE(MTYPE_STATIC_ROUTE, shr->mask_str); if (shr->src_str) XFREE(MTYPE_STATIC_ROUTE, shr->src_str); if (shr->gate_str) XFREE(MTYPE_STATIC_ROUTE, shr->gate_str); if (shr->ifname) XFREE(MTYPE_STATIC_ROUTE, shr->ifname); if (shr->flag_str) XFREE(MTYPE_STATIC_ROUTE, shr->flag_str); if (shr->tag_str) XFREE(MTYPE_STATIC_ROUTE, shr->tag_str); if (shr->distance_str) XFREE(MTYPE_STATIC_ROUTE, shr->distance_str); if (shr->label_str) XFREE(MTYPE_STATIC_ROUTE, shr->label_str); if (shr->table_str) XFREE(MTYPE_STATIC_ROUTE, shr->table_str); XFREE(MTYPE_STATIC_ROUTE, shr); } static int static_list_compare(void *arg1, void *arg2) { struct static_hold_route *shr1 = arg1; struct static_hold_route *shr2 = arg2; int ret; ret = strcmp(shr1->vrf_name, shr2->vrf_name); if (ret) return ret; ret = strcmp(shr1->nhvrf_name, shr2->nhvrf_name); if (ret) return ret; ret = shr1->afi - shr2->afi; if (ret) return ret; ret = shr1->safi - shr2->safi; if (ret) return ret; ret = prefix_cmp(&shr1->dest, &shr2->dest); if (ret) return ret; ret = static_list_compare_helper(shr1->src_str, shr2->src_str); if (ret) return ret; ret = static_list_compare_helper(shr1->gate_str, shr2->gate_str); if (ret) return ret; ret = static_list_compare_helper(shr1->ifname, shr2->ifname); if (ret) return ret; ret = static_list_compare_helper(shr1->flag_str, shr2->flag_str); if (ret) return ret; ret = static_list_compare_helper(shr1->tag_str, shr2->tag_str); if (ret) return ret; ret = static_list_compare_helper(shr1->distance_str, shr2->distance_str); if (ret) return ret; ret = static_list_compare_helper(shr1->table_str, shr2->table_str); if (ret) return ret; return static_list_compare_helper(shr1->label_str, shr2->label_str); } /* General function for static route. */ static int zebra_static_route_holdem( struct static_vrf *svrf, struct static_vrf *nh_svrf, afi_t afi, safi_t safi, const char *negate, struct prefix *dest, 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) { struct static_hold_route *shr, *lookup; struct listnode *node; zlog_warn("Static Route to %s not installed currently because dependent config not fully available", dest_str); shr = XCALLOC(MTYPE_STATIC_ROUTE, sizeof(*shr)); shr->vrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, svrf->vrf->name); shr->nhvrf_name = XSTRDUP(MTYPE_STATIC_ROUTE, nh_svrf->vrf->name); shr->afi = afi; shr->safi = safi; shr->onlink = onlink; if (dest) prefix_copy(&shr->dest, dest); if (dest_str) shr->dest_str = XSTRDUP(MTYPE_STATIC_ROUTE, dest_str); if (mask_str) shr->mask_str = XSTRDUP(MTYPE_STATIC_ROUTE, mask_str); if (src_str) shr->src_str = XSTRDUP(MTYPE_STATIC_ROUTE, src_str); if (gate_str) shr->gate_str = XSTRDUP(MTYPE_STATIC_ROUTE, gate_str); if (ifname) shr->ifname = XSTRDUP(MTYPE_STATIC_ROUTE, ifname); if (flag_str) shr->flag_str = XSTRDUP(MTYPE_STATIC_ROUTE, flag_str); if (tag_str) shr->tag_str = XSTRDUP(MTYPE_STATIC_ROUTE, tag_str); if (distance_str) shr->distance_str = XSTRDUP(MTYPE_STATIC_ROUTE, distance_str); if (label_str) shr->label_str = XSTRDUP(MTYPE_STATIC_ROUTE, label_str); if (table_str) shr->table_str = XSTRDUP(MTYPE_STATIC_ROUTE, table_str); for (ALL_LIST_ELEMENTS_RO(static_list, node, lookup)) { if (static_list_compare(shr, lookup) == 0) break; } if (lookup) { if (negate) { listnode_delete(static_list, lookup); static_list_delete(shr); static_list_delete(lookup); return CMD_SUCCESS; } /* * If a person enters the same line again * we need to silently accept it */ goto shr_cleanup; } if (!negate) { listnode_add_sort(static_list, shr); return CMD_SUCCESS; } shr_cleanup: XFREE(MTYPE_STATIC_ROUTE, shr->nhvrf_name); XFREE(MTYPE_STATIC_ROUTE, shr->vrf_name); XFREE(MTYPE_STATIC_ROUTE, shr); return CMD_SUCCESS; } static int static_route_leak( struct vty *vty, struct static_vrf *svrf, struct static_vrf *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) { int ret; uint8_t distance; struct prefix p, src; struct prefix_ipv6 *src_p = NULL; union g_addr gate; union g_addr *gatep = NULL; struct in_addr mask; enum static_blackhole_type bh_type = 0; route_tag_t tag = 0; uint8_t type; struct static_nh_label snh_label; uint32_t table_id = 0; ret = str2prefix(dest_str, &p); if (ret <= 0) { if (vty) vty_out(vty, "%% Malformed address\n"); else zlog_warn("%s: Malformed address: %s", __PRETTY_FUNCTION__, dest_str); 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) { if (vty) vty_out(vty, "%% Malformed address\n"); else zlog_warn("%s: Malformed address: %s", __PRETTY_FUNCTION__, mask_str); 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) { if (vty) vty_out(vty, "%% Malformed source address\n"); else zlog_warn( "%s: Malformed source address: %s", __PRETTY_FUNCTION__, src_str); return CMD_WARNING_CONFIG_FAILED; } src_p = (struct prefix_ipv6 *)&src; } break; default: break; } /* Apply mask for given prefix. */ apply_mask(&p); if (svrf->vrf->vrf_id == VRF_UNKNOWN || nh_svrf->vrf->vrf_id == VRF_UNKNOWN) { vrf_set_user_cfged(svrf->vrf); return zebra_static_route_holdem( svrf, nh_svrf, afi, safi, negate, &p, dest_str, mask_str, src_str, gate_str, ifname, flag_str, tag_str, distance_str, label_str, table_str, onlink); } if (table_str) { /* table configured. check consistent with vrf config */ if (svrf->vrf->data.l.table_id != RT_TABLE_MAIN) { if (vty) vty_out(vty, "%% Table %s overlaps vrf table %u\n", table_str, svrf->vrf->data.l.table_id); else zlog_warn( "%s: Table %s overlaps vrf table %u", __PRETTY_FUNCTION__, table_str, svrf->vrf->data.l.table_id); return CMD_WARNING_CONFIG_FAILED; } } /* 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); /* Labels */ memset(&snh_label, 0, sizeof(struct static_nh_label)); if (label_str) { if (!mpls_enabled) { if (vty) vty_out(vty, "%% MPLS not turned on in kernel, ignoring command\n"); else zlog_warn( "%s: MPLS not turned on in kernel ignoring static route to %s", __PRETTY_FUNCTION__, dest_str); return CMD_WARNING_CONFIG_FAILED; } int rc = mpls_str2label(label_str, &snh_label.num_labels, snh_label.label); if (rc < 0) { switch (rc) { case -1: if (vty) vty_out(vty, "%% Malformed label(s)\n"); else zlog_warn( "%s: Malformed labels specified for route %s", __PRETTY_FUNCTION__, dest_str); break; case -2: if (vty) vty_out(vty, "%% Cannot use reserved label(s) (%d-%d)\n", MPLS_LABEL_RESERVED_MIN, MPLS_LABEL_RESERVED_MAX); else zlog_warn( "%s: Cannot use reserved labels (%d-%d) for %s", __PRETTY_FUNCTION__, MPLS_LABEL_RESERVED_MIN, MPLS_LABEL_RESERVED_MAX, dest_str); break; case -3: if (vty) vty_out(vty, "%% Too many labels. Enter %d or fewer\n", MPLS_MAX_LABELS); else zlog_warn( "%s: Too many labels, Enter %d or fewer for %s", __PRETTY_FUNCTION__, MPLS_MAX_LABELS, dest_str); break; } return CMD_WARNING_CONFIG_FAILED; } } /* TableID */ if (table_str) table_id = atol(table_str); /* Null0 static route. */ if (ifname != NULL) { if (strncasecmp(ifname, "Null0", strlen(ifname)) == 0 || strncasecmp(ifname, "reject", strlen(ifname)) == 0 || strncasecmp(ifname, "blackhole", strlen(ifname)) == 0) { if (vty) vty_out(vty, "%% Nexthop interface cannot be Null0, reject or blackhole\n"); else zlog_warn( "%s: Nexthop interface cannot be Null0, reject or blackhole for %s", __PRETTY_FUNCTION__, dest_str); return CMD_WARNING_CONFIG_FAILED; } } /* Route flags */ if (flag_str) { switch (flag_str[0]) { case 'r': bh_type = STATIC_BLACKHOLE_REJECT; break; case 'b': bh_type = STATIC_BLACKHOLE_DROP; break; case 'N': bh_type = STATIC_BLACKHOLE_NULL; break; default: if (vty) vty_out(vty, "%% Malformed flag %s \n", flag_str); else zlog_warn("%s: Malformed flag %s for %s", __PRETTY_FUNCTION__, flag_str, dest_str); return CMD_WARNING_CONFIG_FAILED; } } if (gate_str) { if (inet_pton(afi2family(afi), gate_str, &gate) != 1) { if (vty) vty_out(vty, "%% Malformed nexthop address %s\n", gate_str); else zlog_warn( "%s: Malformed nexthop address %s for %s", __PRETTY_FUNCTION__, gate_str, dest_str); return CMD_WARNING_CONFIG_FAILED; } gatep = &gate; } 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; } if (!negate) { static_add_route(afi, safi, type, &p, src_p, gatep, ifname, bh_type, tag, distance, svrf, nh_svrf, &snh_label, table_id, onlink); /* Mark as having FRR configuration */ vrf_set_user_cfged(svrf->vrf); } else { static_delete_route(afi, safi, type, &p, src_p, gatep, ifname, tag, distance, svrf, &snh_label, table_id); /* If no other FRR config for this VRF, mark accordingly. */ if (!static_vrf_has_config(svrf)) vrf_reset_user_cfged(svrf->vrf); } return CMD_SUCCESS; } 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) { struct static_vrf *svrf; /* VRF id */ svrf = static_vrf_lookup_by_name(vrf_name); /* When trying to delete, the VRF must exist. */ if (negate && !svrf) { vty_out(vty, "%% vrf %s is not defined\n", vrf_name); return CMD_WARNING_CONFIG_FAILED; } /* When trying to create, create the VRF if it doesn't exist. * Note: The VRF isn't active until we hear about it from the kernel. */ if (!svrf) { svrf = static_vty_get_unknown_vrf(vty, vrf_name); if (!svrf) return CMD_WARNING_CONFIG_FAILED; } return static_route_leak(vty, svrf, svrf, afi, safi, negate, dest_str, mask_str, src_str, gate_str, ifname, flag_str, tag_str, distance_str, label_str, table_str, false); } void static_config_install_delayed_routes(struct static_vrf *svrf) { struct listnode *node, *nnode; struct static_hold_route *shr; struct static_vrf *osvrf, *nh_svrf; int installed; for (ALL_LIST_ELEMENTS(static_list, node, nnode, shr)) { osvrf = static_vrf_lookup_by_name(shr->vrf_name); nh_svrf = static_vrf_lookup_by_name(shr->nhvrf_name); if (osvrf != svrf && nh_svrf != svrf) continue; if (osvrf->vrf->vrf_id == VRF_UNKNOWN || nh_svrf->vrf->vrf_id == VRF_UNKNOWN) continue; installed = static_route_leak( NULL, osvrf, nh_svrf, shr->afi, shr->safi, NULL, shr->dest_str, shr->mask_str, shr->src_str, shr->gate_str, shr->ifname, shr->flag_str, shr->tag_str, shr->distance_str, shr->label_str, shr->table_str, shr->onlink); if (installed != CMD_SUCCESS) zlog_debug( "%s: Attempt to install %s as a route and it was rejected", __PRETTY_FUNCTION__, shr->dest_str); listnode_delete(static_list, shr); static_list_delete(shr); } } /* Write static route configuration. */ int static_config(struct vty *vty, struct static_vrf *svrf, afi_t afi, safi_t safi, const char *cmd) { struct static_hold_route *shr; struct listnode *node; char spacing[100]; struct route_node *rn; struct static_route *si; struct route_table *stable; char buf[SRCDEST2STR_BUFFER]; int write = 0; stable = svrf->stable[afi][safi]; if (stable == NULL) return write; sprintf(spacing, "%s%s", (svrf->vrf->vrf_id == VRF_DEFAULT) ? "" : " ", cmd); /* * Static routes for vrfs not fully inited */ for (ALL_LIST_ELEMENTS_RO(static_list, node, shr)) { if (shr->afi != afi || shr->safi != safi) continue; if (strcmp(svrf->vrf->name, shr->vrf_name) != 0) continue; char dest_str[PREFIX_STRLEN]; prefix2str(&shr->dest, dest_str, sizeof(dest_str)); vty_out(vty, "%s ", spacing); if (shr->dest_str) vty_out(vty, "%s ", dest_str); if (shr->src_str) vty_out(vty, "from %s ", shr->src_str); if (shr->gate_str) vty_out(vty, "%s ", shr->gate_str); if (shr->ifname) vty_out(vty, "%s ", shr->ifname); if (shr->flag_str) vty_out(vty, "%s ", shr->flag_str); if (shr->tag_str) vty_out(vty, "tag %s ", shr->tag_str); if (shr->distance_str) vty_out(vty, "%s ", shr->distance_str); if (shr->label_str) vty_out(vty, "label %s ", shr->label_str); if (shr->table_str) vty_out(vty, "table %s", shr->table_str); if (strcmp(shr->vrf_name, shr->nhvrf_name) != 0) vty_out(vty, "nexthop-vrf %s ", shr->nhvrf_name); if (shr->onlink) vty_out(vty, "onlink"); vty_out(vty, "\n"); } for (rn = route_top(stable); rn; rn = srcdest_route_next(rn)) for (si = rn->info; si; si = si->next) { vty_out(vty, "%s %s", spacing, srcdest_rnode2str(rn, buf, sizeof(buf))); switch (si->type) { case STATIC_IPV4_GATEWAY: vty_out(vty, " %s", inet_ntoa(si->addr.ipv4)); break; case STATIC_IPV6_GATEWAY: vty_out(vty, " %s", inet_ntop(AF_INET6, &si->addr.ipv6, buf, sizeof(buf))); break; case STATIC_IFNAME: vty_out(vty, " %s", si->ifname); break; case STATIC_BLACKHOLE: switch (si->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, &si->addr.ipv4, buf, sizeof(buf)), si->ifname); break; case STATIC_IPV6_GATEWAY_IFNAME: vty_out(vty, " %s %s", inet_ntop(AF_INET6, &si->addr.ipv6, buf, sizeof(buf)), si->ifname); break; } if (si->tag) vty_out(vty, " tag %" ROUTE_TAG_PRI, si->tag); if (si->distance != ZEBRA_STATIC_DISTANCE_DEFAULT) vty_out(vty, " %d", si->distance); /* Label information */ if (si->snh_label.num_labels) vty_out(vty, " label %s", mpls_label2str(si->snh_label.num_labels, si->snh_label.label, buf, sizeof(buf), 0)); if (si->nh_vrf_id != si->vrf_id) vty_out(vty, " nexthop-vrf %s", si->nh_vrfname); /* * table ID from VRF overrides configured */ if (si->table_id && svrf->vrf->data.l.table_id == RT_TABLE_MAIN) vty_out(vty, " table %u", si->table_id); if (si->onlink) vty_out(vty, " onlink"); vty_out(vty, "\n"); write = 1; } return write; } /* Static unicast routes for multicast RPF lookup. */ DEFPY (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(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") { if (table_str && vrf && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } 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(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") { VTY_DECLVAR_CONTEXT(vrf, vrf); struct static_vrf *svrf = vrf->info; if (table_str && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } /* * 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, svrf, svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, NULL, NULL, flag, tag_str, distance_str, label, table_str, false); } DEFPY(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 \ }]", 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") { struct static_vrf *svrf; struct static_vrf *nh_svrf; const char *flag = NULL; if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } svrf = static_vty_get_unknown_vrf(vty, vrf); if (!svrf) { vty_out(vty, "%% vrf %s is not defined\n", vrf); return CMD_WARNING_CONFIG_FAILED; } if (table_str && vrf && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } if (nexthop_vrf) nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); else nh_svrf = svrf; if (!nh_svrf) { vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); return CMD_WARNING_CONFIG_FAILED; } return static_route_leak(vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, !!onlink); } DEFPY(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 \ }]", 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") { VTY_DECLVAR_CONTEXT(vrf, vrf); const char *flag = NULL; struct static_vrf *svrf = vrf->info; struct static_vrf *nh_svrf; if (table_str && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } if (nexthop_vrf) nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); else nh_svrf = svrf; if (!nh_svrf) { vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); return CMD_WARNING_CONFIG_FAILED; } return static_route_leak(vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, !!onlink); } DEFPY(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 \ }]", 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) { struct static_vrf *svrf; struct static_vrf *nh_svrf; const char *flag = NULL; if (table_str && vrf && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } svrf = static_vty_get_unknown_vrf(vty, vrf); if (!svrf) { vty_out(vty, "%% vrf %s is not defined\n", vrf); return CMD_WARNING_CONFIG_FAILED; } if (nexthop_vrf) nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); else nh_svrf = svrf; if (!nh_svrf) { vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); return CMD_WARNING_CONFIG_FAILED; } return static_route_leak( vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false); } DEFPY(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 \ }]", 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) { VTY_DECLVAR_CONTEXT(vrf, vrf); struct static_vrf *svrf = vrf->info; struct static_vrf *nh_svrf; const char *flag = NULL; if (table_str && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } if (nexthop_vrf) nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); else nh_svrf = svrf; if (!nh_svrf) { vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); return CMD_WARNING_CONFIG_FAILED; } return static_route_leak( vty, svrf, nh_svrf, AFI_IP, SAFI_UNICAST, no, prefix, mask_str, NULL, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false); } DEFPY(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") { if (table_str && vrf && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } 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(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") { VTY_DECLVAR_CONTEXT(vrf, vrf); struct static_vrf *svrf = vrf->info; if (table_str && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } /* * 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, svrf, svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, NULL, NULL, flag, tag_str, distance_str, label, table_str, false); } DEFPY(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 \ }]", 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") { struct static_vrf *svrf; struct static_vrf *nh_svrf; const char *flag = NULL; if (table_str && vrf && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } svrf = static_vty_get_unknown_vrf(vty, vrf); if (!svrf) { vty_out(vty, "%% vrf %s is not defined\n", vrf); return CMD_WARNING_CONFIG_FAILED; } if (nexthop_vrf) nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); else nh_svrf = svrf; if (!nh_svrf) { vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); return CMD_WARNING_CONFIG_FAILED; } if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } return static_route_leak( vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, !!onlink); } DEFPY(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 \ }]", 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") { VTY_DECLVAR_CONTEXT(vrf, vrf); struct static_vrf *svrf = vrf->info; struct static_vrf *nh_svrf; const char *flag = NULL; if (table_str && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } if (nexthop_vrf) nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); else nh_svrf = svrf; if (!nh_svrf) { vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); return CMD_WARNING_CONFIG_FAILED; } if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } return static_route_leak( vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, !!onlink); } DEFPY(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 \ }]", 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) { struct static_vrf *svrf; struct static_vrf *nh_svrf; const char *flag = NULL; if (table_str && vrf && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } svrf = static_vty_get_unknown_vrf(vty, vrf); if (!svrf) { vty_out(vty, "%% vrf %s is not defined\n", vrf); return CMD_WARNING_CONFIG_FAILED; } if (nexthop_vrf) nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); else nh_svrf = svrf; if (!nh_svrf) { vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); return CMD_WARNING_CONFIG_FAILED; } if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } return static_route_leak( vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false); } DEFPY(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 \ }]", 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) { VTY_DECLVAR_CONTEXT(vrf, vrf); struct static_vrf *svrf = vrf->info; struct static_vrf *nh_svrf; const char *flag = NULL; if (table_str && !vrf_is_backend_netns()) { vty_out(vty, "%% table param only available when running on netns-based vrfs\n"); return CMD_WARNING_CONFIG_FAILED; } if (nexthop_vrf) nh_svrf = static_vty_get_unknown_vrf(vty, nexthop_vrf); else nh_svrf = svrf; if (!nh_svrf) { vty_out(vty, "%% nexthop vrf %s is not defined\n", nexthop_vrf); return CMD_WARNING_CONFIG_FAILED; } if (ifname && !strncasecmp(ifname, "Null0", 5)) { flag = "Null0"; ifname = NULL; } return static_route_leak( vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL, from_str, gate_str, ifname, flag, tag_str, distance_str, label, table_str, false); } DEFUN_NOSH (show_debugging_staticd, show_debugging_staticd_cmd, "show debugging [static]", SHOW_STR DEBUG_STR "Static Information\n") { vty_out(vty, "Static debugging status\n"); return CMD_SUCCESS; } void static_vty_init(void) { 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(VIEW_NODE, &show_debugging_staticd_cmd); static_list = list_new(); static_list->cmp = (int (*)(void *, void *))static_list_compare; static_list->del = (void (*)(void *))static_list_delete; }