summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bgpd/bgp_routemap.c24
-rw-r--r--bgpd/bgpd.c13
-rw-r--r--doc/cli.md8
-rw-r--r--lib/command_graph.c2
-rw-r--r--lib/command_graph.h10
-rw-r--r--lib/command_lex.l4
-rw-r--r--lib/command_match.c55
-rw-r--r--lib/command_parse.y12
-rw-r--r--lib/command_py.c2
-rw-r--r--lib/grammar_sandbox.c2
-rw-r--r--lib/routemap.c18
-rw-r--r--python/clidef.py8
12 files changed, 131 insertions, 27 deletions
diff --git a/bgpd/bgp_routemap.c b/bgpd/bgp_routemap.c
index a8e111d361..5a5d2a5d5d 100644
--- a/bgpd/bgp_routemap.c
+++ b/bgpd/bgp_routemap.c
@@ -3499,26 +3499,38 @@ DEFUN (no_match_origin,
DEFUN (set_ip_nexthop_peer,
set_ip_nexthop_peer_cmd,
- "set ip next-hop peer-address",
+ "[no] set ip next-hop peer-address",
+ NO_STR
SET_STR
IP_STR
"Next hop address\n"
"Use peer address (for BGP only)\n")
{
- return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index),
- "ip next-hop", "peer-address");
+ int (*func)(struct vty *, struct route_map_index *, const char *,
+ const char *) = strmatch(argv[0]->text, "no")
+ ? generic_set_delete
+ : generic_set_add;
+
+ return func(vty, VTY_GET_CONTEXT(route_map_index), "ip next-hop",
+ "peer-address");
}
DEFUN (set_ip_nexthop_unchanged,
set_ip_nexthop_unchanged_cmd,
- "set ip next-hop unchanged",
+ "[no] set ip next-hop unchanged",
+ NO_STR
SET_STR
IP_STR
"Next hop address\n"
"Don't modify existing Next hop address\n")
{
- return generic_set_add(vty, VTY_GET_CONTEXT(route_map_index),
- "ip next-hop", "unchanged");
+ int (*func)(struct vty *, struct route_map_index *, const char *,
+ const char *) = strmatch(argv[0]->text, "no")
+ ? generic_set_delete
+ : generic_set_add;
+
+ return func(vty, VTY_GET_CONTEXT(route_map_index), "ip next-hop",
+ "unchanged");
}
diff --git a/bgpd/bgpd.c b/bgpd/bgpd.c
index f9dbac2f71..d7733fbacd 100644
--- a/bgpd/bgpd.c
+++ b/bgpd/bgpd.c
@@ -6190,11 +6190,14 @@ static void bgp_config_write_filter(struct vty *vty, struct peer *peer,
addr, filter->plist[in].name);
}
- if (filter->plist[out].name && !gfilter) {
- afi_header_vty_out(vty, afi, safi, write,
- " neighbor %s prefix-list %s out\n", addr,
- filter->plist[out].name);
- }
+ if (filter->plist[out].name)
+ if (!gfilter || !gfilter->plist[out].name
+ || strcmp(filter->plist[out].name, gfilter->plist[out].name)
+ != 0) {
+ afi_header_vty_out(vty, afi, safi, write,
+ " neighbor %s prefix-list %s out\n",
+ addr, filter->plist[out].name);
+ }
/* route-map. */
if (filter->map[RMAP_IN].name)
diff --git a/doc/cli.md b/doc/cli.md
index cfb4d629f9..ef867362c3 100644
--- a/doc/cli.md
+++ b/doc/cli.md
@@ -30,15 +30,17 @@ Characters allowed in each token type:
Tokens
------
* `WORD` -- A token that begins with +, -, or a lowercase letter. It is
- an unchanging part of the command and will only match itself.
- Example: "show ip bgp", every token is a WORD.
+ an unchanging part of the command and will only match itself.
+ Example: "show ip bgp", every token is a WORD.
* `IPV4` -- 'A.B.C.D', matches an IPv4 address.
* `IPV6` -- 'X:X::X:X', matches an IPv6 address.
* `IPV4_PREFIX` -- 'A.B.C.D/M', matches an IPv4 prefix in CIDR notation.
* `IPV6_PREFIX` -- 'X:X::X:X/M', matches an IPv6 prefix in CIDR notation.
+* `MAC` -- 'M:A:C', matches a 48-bit mac address
+* `MAC_PREFIX` -- 'M:A:C/M', matches a 48-bit mac address with a mask
* `VARIABLE` -- Begins with a capital letter. Matches any input.
* `RANGE` -- Numeric range delimited by parentheses, e.g. (-100 - 100) or
- (10-20). Will only match numbers in the range.
+ (10-20). Will only match numbers in the range.
Rules
-----
diff --git a/lib/command_graph.c b/lib/command_graph.c
index dc7233c1fe..fce11a70cc 100644
--- a/lib/command_graph.c
+++ b/lib/command_graph.c
@@ -379,6 +379,8 @@ static void cmd_node_names(struct graph_node *gn, struct graph_node *join,
case IPV4_PREFIX_TKN:
case IPV6_TKN:
case IPV6_PREFIX_TKN:
+ case MAC_TKN:
+ case MAC_PREFIX_TKN:
if (!tok->varname && prevname)
cmd_token_varname_set(tok, prevname);
prevname = NULL;
diff --git a/lib/command_graph.h b/lib/command_graph.h
index 879148844c..ec68e284ed 100644
--- a/lib/command_graph.h
+++ b/lib/command_graph.h
@@ -42,14 +42,17 @@ struct vty;
* The type determines what kind of data the token can match (in the
* matching use case) or hold (in the argv use case).
*/
+/* clang-format off */
enum cmd_token_type {
- WORD_TKN, // words
+ WORD_TKN, // words
VARIABLE_TKN, // almost anything
RANGE_TKN, // integer range
- IPV4_TKN, // IPV4 addresses
+ IPV4_TKN, // IPV4 addresses
IPV4_PREFIX_TKN, // IPV4 network prefixes
- IPV6_TKN, // IPV6 prefixes
+ IPV6_TKN, // IPV6 prefixes
IPV6_PREFIX_TKN, // IPV6 network prefixes
+ MAC_TKN, // Ethernet address
+ MAC_PREFIX_TKN, // Ethernet address w/ CIDR mask
/* plumbing types */
FORK_TKN, // marks subgraph beginning
@@ -59,6 +62,7 @@ enum cmd_token_type {
SPECIAL_TKN = FORK_TKN,
};
+/* clang-format on */
#define IS_VARYING_TOKEN(x) ((x) >= VARIABLE_TKN && (x) < FORK_TKN)
diff --git a/lib/command_lex.l b/lib/command_lex.l
index fddbf7b287..59d0d840a8 100644
--- a/lib/command_lex.l
+++ b/lib/command_lex.l
@@ -40,6 +40,8 @@ IPV4 A\.B\.C\.D
IPV4_PREFIX A\.B\.C\.D\/M
IPV6 X:X::X:X
IPV6_PREFIX X:X::X:X\/M
+MAC M:A:C
+MAC_PREFIX M:A:C\/M
VARIABLE [A-Z][-_a-zA-Z:0-9]+
NUMBER (\-|\+)?[0-9]{1,20}
RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\)
@@ -67,6 +69,8 @@ RANGE \({NUMBER}[ ]?\-[ ]?{NUMBER}\)
{IPV4_PREFIX} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV4_PREFIX;}
{IPV6} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV6;}
{IPV6_PREFIX} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return IPV6_PREFIX;}
+{MAC} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return MAC;}
+{MAC_PREFIX} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return MAC_PREFIX;}
{VARIABLE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return VARIABLE;}
{RANGE} {yylval->string = XSTRDUP(MTYPE_LEX, yytext); return RANGE;}
. {return yytext[0];}
diff --git a/lib/command_match.c b/lib/command_match.c
index bc54f1bb09..f07448d716 100644
--- a/lib/command_match.c
+++ b/lib/command_match.c
@@ -78,6 +78,8 @@ static enum match_type match_word(struct cmd_token *, const char *);
static enum match_type match_variable(struct cmd_token *, const char *);
+static enum match_type match_mac(const char *, bool);
+
/* matching functions */
static enum matcher_rv matcher_rv;
@@ -537,6 +539,8 @@ static int score_precedence(enum cmd_token_type type)
case IPV4_PREFIX_TKN:
case IPV6_TKN:
case IPV6_PREFIX_TKN:
+ case MAC_TKN:
+ case MAC_PREFIX_TKN:
case RANGE_TKN:
return 2;
case WORD_TKN:
@@ -658,6 +662,10 @@ static enum match_type match_token(struct cmd_token *token, char *input_token)
return match_range(token, input_token);
case VARIABLE_TKN:
return match_variable(token, input_token);
+ case MAC_TKN:
+ return match_mac(input_token, false);
+ case MAC_PREFIX_TKN:
+ return match_mac(input_token, true);
case END_TKN:
default:
return no_match;
@@ -964,3 +972,50 @@ static enum match_type match_variable(struct cmd_token *token, const char *word)
assert(token->type == VARIABLE_TKN);
return exact_match;
}
+
+#define MAC_CHARS "ABCDEFabcdef0123456789:"
+
+static enum match_type match_mac(const char *word, bool prefix)
+{
+ /* 6 2-digit hex numbers separated by 5 colons */
+ size_t mac_explen = 6 * 2 + 5;
+ /* '/' + 2-digit integer */
+ size_t mask_len = 1 + 2;
+ unsigned int i;
+ char *eptr;
+ unsigned int maskval;
+
+ /* length check */
+ if (strlen(word) > mac_explen + (prefix ? mask_len : 0))
+ return no_match;
+
+ /* address check */
+ for (i = 0; i < mac_explen; i++) {
+ if (word[i] == '\0' || !strchr(MAC_CHARS, word[i]))
+ break;
+ if (((i + 1) % 3 == 0) != (word[i] == ':'))
+ return no_match;
+ }
+
+ /* incomplete address */
+ if (i < mac_explen && word[i] == '\0')
+ return partly_match;
+ else if (i < mac_explen)
+ return no_match;
+
+ /* mask check */
+ if (prefix && word[i] == '/') {
+ if (word[++i] == '\0')
+ return partly_match;
+
+ maskval = strtoul(&word[i], &eptr, 10);
+ if (*eptr != '\0' || maskval > 48)
+ return no_match;
+ } else if (prefix && word[i] == '\0') {
+ return partly_match;
+ } else if (prefix) {
+ return no_match;
+ }
+
+ return exact_match;
+}
diff --git a/lib/command_parse.y b/lib/command_parse.y
index 1bc8ea1a44..0f3e42219e 100644
--- a/lib/command_parse.y
+++ b/lib/command_parse.y
@@ -101,6 +101,8 @@
%token <string> IPV6_PREFIX
%token <string> VARIABLE
%token <string> RANGE
+%token <string> MAC
+%token <string> MAC_PREFIX
/* union types for parsed rules */
%type <node> start
@@ -273,6 +275,16 @@ placeholder_token_real:
XFREE (MTYPE_LEX, $1);
}
+| MAC
+{
+ $$ = new_token_node (ctx, MAC_TKN, $1, doc_next(ctx));
+ XFREE (MTYPE_LEX, $1);
+}
+| MAC_PREFIX
+{
+ $$ = new_token_node (ctx, MAC_PREFIX_TKN, $1, doc_next(ctx));
+ XFREE (MTYPE_LEX, $1);
+}
placeholder_token:
placeholder_token_real varname_token
diff --git a/lib/command_py.c b/lib/command_py.c
index 785d2ffa70..755cfb55ce 100644
--- a/lib/command_py.c
+++ b/lib/command_py.c
@@ -199,6 +199,8 @@ static PyObject *graph_to_pyobj(struct wrap_graph *wgraph,
item(IPV4_PREFIX_TKN) // IPV4 network prefixes
item(IPV6_TKN) // IPV6 prefixes
item(IPV6_PREFIX_TKN) // IPV6 network prefixes
+ item(MAC_TKN) // MAC address
+ item(MAC_PREFIX_TKN) // MAC address with mask
/* plumbing types */
item(FORK_TKN) item(JOIN_TKN) item(START_TKN)
diff --git a/lib/grammar_sandbox.c b/lib/grammar_sandbox.c
index 8dd8d2e2bb..96ecfa44d3 100644
--- a/lib/grammar_sandbox.c
+++ b/lib/grammar_sandbox.c
@@ -498,6 +498,8 @@ struct message tokennames[] = {item(WORD_TKN), // words
item(IPV4_PREFIX_TKN), // IPV4 network prefixes
item(IPV6_TKN), // IPV6 prefixes
item(IPV6_PREFIX_TKN), // IPV6 network prefixes
+ item(MAC_TKN), // MAC address
+ item(MAC_PREFIX_TKN), // MAC address w/ mask
/* plumbing types */
item(FORK_TKN),
diff --git a/lib/routemap.c b/lib/routemap.c
index 3d1add25dc..a70248633c 100644
--- a/lib/routemap.c
+++ b/lib/routemap.c
@@ -2194,24 +2194,24 @@ DEFUN (set_ip_nexthop,
DEFUN (no_set_ip_nexthop,
no_set_ip_nexthop_cmd,
- "no set ip next-hop [<peer-address|A.B.C.D>]",
+ "no set ip next-hop [A.B.C.D]",
NO_STR
SET_STR
IP_STR
"Next hop address\n"
- "Use peer address (for BGP only)\n"
"IP address of next hop\n")
{
- int idx_peer = 4;
+ int idx;
VTY_DECLVAR_CONTEXT(route_map_index, index);
+ const char *arg = NULL;
- if (rmap_match_set_hook.no_set_ip_nexthop) {
- if (argc <= idx_peer)
- return rmap_match_set_hook.no_set_ip_nexthop(
- vty, index, "ip next-hop", NULL);
+ if (argv_find(argv, argc, "A.B.C.D", &idx))
+ arg = argv[idx]->arg;
+
+ if (rmap_match_set_hook.no_set_ip_nexthop)
return rmap_match_set_hook.no_set_ip_nexthop(
- vty, index, "ip next-hop", argv[idx_peer]->arg);
- }
+ vty, index, "ip next-hop", arg);
+
return CMD_SUCCESS;
}
diff --git a/python/clidef.py b/python/clidef.py
index 069d80fb70..8e3c7595b7 100644
--- a/python/clidef.py
+++ b/python/clidef.py
@@ -59,7 +59,7 @@ class PrefixBase(RenderHandler):
def combine(self, other):
if type(self) == type(other):
return other
- if type(other) in [Prefix4Handler, Prefix6Handler, PrefixGenHandler]:
+ if isinstance(other, PrefixBase):
return PrefixGenHandler(None)
return StringHandler(None)
deref = '&'
@@ -71,6 +71,10 @@ class Prefix6Handler(PrefixBase):
argtype = 'const struct prefix_ipv6 *'
decl = Template('struct prefix_ipv6 $varname = { };')
code = Template('_fail = !str2prefix_ipv6(argv[_i]->arg, &$varname);')
+class PrefixEthHandler(PrefixBase):
+ argtype = 'struct prefix_eth *'
+ decl = Template('struct prefix_eth $varname = { };')
+ code = Template('_fail = !str2prefix_eth(argv[_i]->arg, &$varname);')
class PrefixGenHandler(PrefixBase):
argtype = 'const struct prefix *'
decl = Template('struct prefix $varname = { };')
@@ -121,6 +125,8 @@ handlers = {
'IPV4_PREFIX_TKN': Prefix4Handler,
'IPV6_TKN': IP6Handler,
'IPV6_PREFIX_TKN': Prefix6Handler,
+ 'MAC_TKN': PrefixEthHandler,
+ 'MAC_PREFIX_TKN': PrefixEthHandler,
}
# core template invoked for each occurence of DEFPY.