frr/staticd/static_vty.c
David Lamparter 89cb86aeb0 build, vtysh: extract vtysh commands from .xref
Rather than running selected source files through the preprocessor and a
bunch of perl regex'ing to get the list of all DEFUNs, use the data
collected in frr.xref.

This not only eliminates issues we've been having with preprocessor
failures due to nonexistent header files, but is also much faster.
Where extract.pl would take 5s, this now finishes in 0.2s.  And since
this is a non-parallelizable build step towards the end of the build
(dependent on a lot of other things being done already), the speedup is
actually noticeable.

Also files containing CLI no longer need to be listed in `vtysh_scan`
since the .xref data covers everything.  `#ifndef VTYSH_EXTRACT_PL`
checks are equally obsolete.

Signed-off-by: David Lamparter <equinox@opensourcerouting.org>
2022-10-26 17:12:34 +01:00

1338 lines
40 KiB
C

/*
* 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 <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 "northbound.h"
#include "libfrr.h"
#include "routing_nb.h"
#include "northbound_cli.h"
#include "static_vrf.h"
#include "static_vty.h"
#include "static_routes.h"
#include "static_debug.h"
#include "staticd/static_vty_clippy.c"
#include "static_nb.h"
#define STATICD_STR "Static route daemon\n"
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);
/* 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 <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_YANG(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")
{
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\
<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")
{
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/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
A.B.C.D$gate \
<INTERFACE|Null0>$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/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
A.B.C.D$gate \
<INTERFACE|Null0>$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\
<A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
<A.B.C.D$gate|<INTERFACE|Null0>$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\
<A.B.C.D/M$prefix|A.B.C.D$prefix A.B.C.D$mask> \
<A.B.C.D$gate|<INTERFACE|Null0>$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] \
<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"
"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] \
<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"
"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 \
<INTERFACE|Null0>$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 \
<INTERFACE|Null0>$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] \
<X:X::X:X$gate|<INTERFACE|Null0>$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] \
<X:X::X:X$gate|<INTERFACE|Null0>$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);
cmd_show_lib_debugs(vty);
return CMD_SUCCESS;
}
static struct cmd_node debug_node = {
.name = "debug",
.node = DEBUG_NODE,
.prompt = "",
.config_write = static_config_write_debug,
};
void static_vty_init(void)
{
install_node(&debug_node);
install_element(CONFIG_NODE, &ip_mroute_dist_cmd);
install_element(CONFIG_NODE, &ip_route_blackhole_cmd);
install_element(VRF_NODE, &ip_route_blackhole_vrf_cmd);
install_element(CONFIG_NODE, &ip_route_address_interface_cmd);
install_element(VRF_NODE, &ip_route_address_interface_vrf_cmd);
install_element(CONFIG_NODE, &ip_route_cmd);
install_element(VRF_NODE, &ip_route_vrf_cmd);
install_element(CONFIG_NODE, &ipv6_route_blackhole_cmd);
install_element(VRF_NODE, &ipv6_route_blackhole_vrf_cmd);
install_element(CONFIG_NODE, &ipv6_route_address_interface_cmd);
install_element(VRF_NODE, &ipv6_route_address_interface_vrf_cmd);
install_element(CONFIG_NODE, &ipv6_route_cmd);
install_element(VRF_NODE, &ipv6_route_vrf_cmd);
install_element(ENABLE_NODE, &show_debugging_static_cmd);
install_element(ENABLE_NODE, &debug_staticd_cmd);
install_element(CONFIG_NODE, &debug_staticd_cmd);
}