summaryrefslogtreecommitdiff
path: root/staticd/static_vty.c
diff options
context:
space:
mode:
Diffstat (limited to 'staticd/static_vty.c')
-rw-r--r--staticd/static_vty.c1414
1 files changed, 1414 insertions, 0 deletions
diff --git a/staticd/static_vty.c b/staticd/static_vty.c
new file mode 100644
index 0000000000..1f54e9bff7
--- /dev/null
+++ b/staticd/static_vty.c
@@ -0,0 +1,1414 @@
+/*
+ * STATICd - vty code
+ * Copyright (C) 2018 Cumulus Networks, Inc.
+ * Donald Sharp
+ *
+ * FRR 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, or (at your option) any
+ * later version.
+ *
+ * FRR 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 <zebra.h>
+
+#include "command.h"
+#include "vty.h"
+#include "vrf.h"
+#include "prefix.h"
+#include "nexthop.h"
+#include "table.h"
+#include "srcdest_table.h"
+#include "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;
+
+ /* 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)
+{
+ /* Are Both NULL */
+ if (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)
+{
+ 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;
+ 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)
+{
+ 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);
+ }
+
+ 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);
+ /* 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);
+}
+
+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);
+
+ 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);
+ 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);
+
+ 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 <A.B.C.D$gate|INTERFACE$ifname> [(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\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ VRF_CMD_HELP_STR
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ 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;
+ }
+
+ 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\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR IP_STR
+ "Establish static routes\n"
+ "IP destination prefix (e.g. 10.0.0.0/8)\n"
+ "IP destination prefix\n"
+ "IP destination prefix mask\n"
+ "Emit an ICMP unreachable when matched\n"
+ "Silently discard pkts when matched\n"
+ "Set tag for this route\n"
+ "Tag value\n"
+ "Distance value for this route\n"
+ MPLS_LABEL_HELPSTR
+ "Table to configure\n"
+ "The table number to configure\n")
+{
+ 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);
+}
+
+DEFPY(ip_route_address_interface,
+ ip_route_address_interface_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ A.B.C.D$gate \
+ INTERFACE$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. Specify 'Null0' (case-insensitive) for a \
+ null route.\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 (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_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);
+}
+
+DEFPY(ip_route_address_interface_vrf,
+ ip_route_address_interface_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ A.B.C.D$gate \
+ INTERFACE$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. Specify 'Null0' (case-insensitive) for a \
+ null route.\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);
+ 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);
+}
+
+DEFPY(ip_route,
+ ip_route_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <A.B.C.D$gate|INTERFACE$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"
+ "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_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);
+}
+
+DEFPY(ip_route_vrf,
+ ip_route_vrf_cmd,
+ "[no] ip route\
+ <A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
+ <A.B.C.D$gate|INTERFACE$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"
+ "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);
+}
+
+DEFPY(ipv6_route_blackhole,
+ ipv6_route_blackhole_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <Null0|reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |vrf NAME \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "Null interface\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_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] \
+ <Null0|reject|blackhole>$flag \
+ [{ \
+ tag (1-4294967295) \
+ |(1-255)$distance \
+ |label WORD \
+ |table (1-4294967295) \
+ }]",
+ NO_STR
+ IPV6_STR
+ "Establish static routes\n"
+ "IPv6 destination prefix (e.g. 3ffe:506::/32)\n"
+ "IPv6 source-dest route\n"
+ "IPv6 source prefix\n"
+ "Null interface\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);
+}
+
+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 \
+ INTERFACE$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"
+ "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;
+
+ 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;
+ }
+
+ 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_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+ from_str, gate_str, ifname, NULL, tag_str, distance_str, label,
+ table_str);
+}
+
+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 \
+ INTERFACE$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"
+ "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;
+
+ 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;
+ }
+
+ return static_route_leak(
+ vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+ from_str, gate_str, ifname, NULL, tag_str, distance_str, label,
+ table_str);
+}
+
+DEFPY(ipv6_route,
+ ipv6_route_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <X:X::X:X$gate|INTERFACE$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"
+ "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;
+
+ 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;
+ }
+
+ 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_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+ from_str, gate_str, ifname, NULL, tag_str, distance_str, label,
+ table_str);
+}
+
+DEFPY(ipv6_route_vrf,
+ ipv6_route_vrf_cmd,
+ "[no] ipv6 route X:X::X:X/M$prefix [from X:X::X:X/M] \
+ <X:X::X:X$gate|INTERFACE$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"
+ "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;
+
+ 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;
+ }
+
+ return static_route_leak(
+ vty, svrf, nh_svrf, AFI_IP6, SAFI_UNICAST, no, prefix_str, NULL,
+ from_str, gate_str, ifname, NULL, tag_str, distance_str, label,
+ table_str);
+}
+
+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);
+
+ static_list = list_new();
+ static_list->cmp = (int (*)(void *, void *))static_list_compare;
+ static_list->del = (void (*)(void *))static_list_delete;
+}