summaryrefslogtreecommitdiffstats
path: root/usr.sbin/bgpctl/parser.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/bgpctl/parser.c')
-rw-r--r--usr.sbin/bgpctl/parser.c262
1 files changed, 260 insertions, 2 deletions
diff --git a/usr.sbin/bgpctl/parser.c b/usr.sbin/bgpctl/parser.c
index a52d281f8c9..502a71e6aa5 100644
--- a/usr.sbin/bgpctl/parser.c
+++ b/usr.sbin/bgpctl/parser.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parser.c,v 1.79 2017/06/26 10:08:06 phessler Exp $ */
+/* $OpenBSD: parser.c,v 1.80 2017/08/10 14:12:34 benno Exp $ */
/*
* Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
@@ -47,6 +47,8 @@ enum token_type {
RIBNAME,
SHUTDOWN_COMMUNICATION,
COMMUNITY,
+ EXTCOMMUNITY,
+ EXTCOM_SUBTYPE,
LARGE_COMMUNITY,
LOCALPREF,
MED,
@@ -94,12 +96,16 @@ static const struct token t_show_mrt_as[];
static const struct token t_show_prefix[];
static const struct token t_show_ip[];
static const struct token t_show_community[];
+static const struct token t_show_extcommunity[];
+static const struct token t_show_ext_subtype[];
static const struct token t_show_largecommunity[];
static const struct token t_network[];
static const struct token t_network_show[];
static const struct token t_prefix[];
static const struct token t_set[];
static const struct token t_community[];
+static const struct token t_extcommunity[];
+static const struct token t_ext_subtype[];
static const struct token t_largecommunity[];
static const struct token t_localpref[];
static const struct token t_med[];
@@ -166,6 +172,7 @@ static const struct token t_show_rib[] = {
{ ASTYPE, "peer-as", AS_PEER, t_show_rib_as},
{ ASTYPE, "empty-as", AS_EMPTY, t_show_rib},
{ KEYWORD, "community", NONE, t_show_community},
+ { KEYWORD, "ext-community", NONE, t_show_extcommunity},
{ KEYWORD, "large-community", NONE, t_show_largecommunity},
{ FLAG, "best", F_CTL_ACTIVE, t_show_rib},
{ FLAG, "selected", F_CTL_ACTIVE, t_show_rib},
@@ -288,6 +295,29 @@ static const struct token t_show_community[] = {
{ ENDTOKEN, "", NONE, NULL}
};
+static const struct token t_show_extcommunity[] = {
+ { EXTCOM_SUBTYPE, "bdc", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "defgw", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "esi-lab", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "esi-rt", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "l2vid", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "mac-mob", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "odi", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "ort", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "ori", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "ovs", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "rt", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "soo", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "srcas", NONE, t_show_ext_subtype},
+ { EXTCOM_SUBTYPE, "vrfri", NONE, t_show_ext_subtype},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_show_ext_subtype[] = {
+ { EXTCOMMUNITY, "", NONE, t_show_rib},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
static const struct token t_show_largecommunity[] = {
{ LARGE_COMMUNITY, "", NONE, t_show_rib},
{ ENDTOKEN, "", NONE, NULL}
@@ -317,6 +347,7 @@ static const struct token t_network_show[] = {
static const struct token t_set[] = {
{ NOTOKEN, "", NONE, NULL},
{ KEYWORD, "community", NONE, t_community},
+ { KEYWORD, "ext-community", NONE, t_extcommunity},
{ KEYWORD, "large-community", NONE, t_largecommunity},
{ KEYWORD, "localpref", NONE, t_localpref},
{ KEYWORD, "med", NONE, t_med},
@@ -336,6 +367,29 @@ static const struct token t_community[] = {
{ ENDTOKEN, "", NONE, NULL}
};
+static const struct token t_extcommunity[] = {
+ { EXTCOM_SUBTYPE, "bdc", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "defgw", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "esi-lab", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "esi-rt", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "l2vid", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "mac-mob", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "odi", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "ort", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "ori", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "ovs", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "rt", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "soo", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "srcas", NONE, t_ext_subtype},
+ { EXTCOM_SUBTYPE, "vrfri", NONE, t_ext_subtype},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
+static const struct token t_ext_subtype[] = {
+ { EXTCOMMUNITY, "", NONE, t_set},
+ { ENDTOKEN, "", NONE, NULL}
+};
+
static const struct token t_largecommunity[] = {
{ LARGE_COMMUNITY, "", NONE, t_set},
{ ENDTOKEN, "", NONE, NULL}
@@ -415,8 +469,12 @@ int parse_number(const char *, struct parse_result *,
enum token_type);
int getcommunity(const char *);
int parse_community(const char *, struct parse_result *);
+int parsesubtype(const char *, u_int8_t *, u_int8_t *);
+int parseextvalue(const char *, u_int32_t *);
+u_int parseextcommunity(const char *, struct parse_result *);
u_int getlargecommunity(const char *);
-int parse_largecommunity(const char *, struct parse_result *);
+int parse_largecommunity(const char *,
+ struct parse_result *);
int parse_nexthop(const char *, struct parse_result *);
int bgpctl_getopt(int *, char **[], int);
@@ -595,6 +653,24 @@ match_token(int *argc, char **argv[], const struct token table[])
t = &table[i];
}
break;
+ case EXTCOM_SUBTYPE:
+ if (word != NULL && strncmp(word, table[i].keyword,
+ wordlen) == 0) {
+ if (parsesubtype(word, &res.extcommunity.type,
+ &res.extcommunity.subtype) == 0)
+ errx(1, "Bad ext-community unknown "
+ "type");
+ match++;
+ t = &table[i];
+ }
+ break;
+ case EXTCOMMUNITY:
+ if (word != NULL && wordlen > 0 &&
+ parseextcommunity(word, &res)) {
+ match++;
+ t = &table[i];
+ }
+ break;
case LARGE_COMMUNITY:
if (word != NULL && wordlen > 0 &&
parse_largecommunity(word, &res)) {
@@ -693,6 +769,7 @@ show_valid_args(const struct token table[])
case KEYWORD:
case FLAG:
case ASTYPE:
+ case EXTCOM_SUBTYPE:
fprintf(stderr, " %s\n", table[i].keyword);
break;
case ADDRESS:
@@ -717,6 +794,9 @@ show_valid_args(const struct token table[])
case COMMUNITY:
fprintf(stderr, " <community>\n");
break;
+ case EXTCOMMUNITY:
+ fprintf(stderr, " <extended-community>\n");
+ break;
case LARGE_COMMUNITY:
fprintf(stderr, " <large-community>\n");
break;
@@ -1014,6 +1094,184 @@ done:
return (1);
}
+int
+parsesubtype(const char *name, u_int8_t *type, u_int8_t *subtype)
+{
+ const struct ext_comm_pairs *cp;
+ int found = 0;
+
+ for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
+ if (strcmp(name, cp->subname) == 0) {
+ if (found == 0) {
+ *type = cp->type;
+ *subtype = cp->subtype;
+ }
+ found++;
+ }
+ }
+ if (found > 1)
+ *type = -1;
+ return (found);
+}
+
+int
+parseextvalue(const char *s, u_int32_t *v)
+{
+ const char *errstr;
+ char *p;
+ struct in_addr ip;
+ u_int32_t uvalh = 0, uval;
+
+ if ((p = strchr(s, '.')) == NULL) {
+ /* AS_PLAIN number (4 or 2 byte) */
+ uval = strtonum(s, 0, UINT_MAX, &errstr);
+ if (errstr) {
+ fprintf(stderr, "Bad ext-community: %s is %s\n", s,
+ errstr);
+ return (-1);
+ }
+ *v = uval;
+ if (uval <= USHRT_MAX)
+ return (EXT_COMMUNITY_TRANS_TWO_AS);
+ else
+ return (EXT_COMMUNITY_TRANS_FOUR_AS);
+ } else if (strchr(p + 1, '.') == NULL) {
+ /* AS_DOT number (4-byte) */
+ *p++ = '\0';
+ uvalh = strtonum(s, 0, USHRT_MAX, &errstr);
+ if (errstr) {
+ fprintf(stderr, "Bad ext-community: %s is %s\n", s,
+ errstr);
+ return (-1);
+ }
+ uval = strtonum(p, 0, USHRT_MAX, &errstr);
+ if (errstr) {
+ fprintf(stderr, "Bad ext-community: %s is %s\n", p,
+ errstr);
+ return (-1);
+ }
+ *v = uval | (uvalh << 16);
+ return (EXT_COMMUNITY_TRANS_FOUR_AS);
+ } else {
+ /* more than one dot -> IP address */
+ if (inet_aton(s, &ip) == 0) {
+ fprintf(stderr, "Bad ext-community: %s not parseable\n",
+ s);
+ return (-1);
+ }
+ *v = ip.s_addr;
+ return (EXT_COMMUNITY_TRANS_IPV4);
+ }
+ return (-1);
+}
+
+u_int
+parseextcommunity(const char *word, struct parse_result *r)
+{
+ struct filter_set *fs;
+ const struct ext_comm_pairs *cp;
+ const char *errstr;
+ u_int64_t ullval;
+ u_int32_t uval;
+ char *p, *ep;
+ int type;
+
+ type = r->extcommunity.type;
+
+ switch (type) {
+ case 0xff:
+ if ((p = strchr(word, ':')) == NULL) {
+ fprintf(stderr, "Bad ext-community: %s\n", word);
+ return (0);
+ }
+ *p++ = '\0';
+ if ((type = parseextvalue(word, &uval)) == -1)
+ return (0);
+ switch (type) {
+ case EXT_COMMUNITY_TRANS_TWO_AS:
+ ullval = strtonum(p, 0, UINT_MAX, &errstr);
+ break;
+ case EXT_COMMUNITY_TRANS_IPV4:
+ case EXT_COMMUNITY_TRANS_FOUR_AS:
+ ullval = strtonum(p, 0, USHRT_MAX, &errstr);
+ break;
+ default:
+ fprintf(stderr, "parseextcommunity: unexpected "
+ "result\n");
+ return (0);
+ }
+ if (errstr) {
+ fprintf(stderr, "Bad ext-community: %s is %s\n", p,
+ errstr);
+ return (0);
+ }
+ switch (type) {
+ case EXT_COMMUNITY_TRANS_TWO_AS:
+ r->extcommunity.data.ext_as.as = uval;
+ r->extcommunity.data.ext_as.val = ullval;
+ break;
+ case EXT_COMMUNITY_TRANS_IPV4:
+ r->extcommunity.data.ext_ip.addr.s_addr = uval;
+ r->extcommunity.data.ext_ip.val = ullval;
+ break;
+ case EXT_COMMUNITY_TRANS_FOUR_AS:
+ r->extcommunity.data.ext_as4.as4 = uval;
+ r->extcommunity.data.ext_as4.val = ullval;
+ break;
+ }
+ break;
+ case EXT_COMMUNITY_TRANS_OPAQUE:
+ case EXT_COMMUNITY_TRANS_EVPN:
+ errno = 0;
+ ullval = strtoull(word, &ep, 0);
+ if (word[0] == '\0' || *ep != '\0') {
+ fprintf(stderr, "Bad ext-community: bad value\n");
+ return (0);
+ }
+ if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) {
+ fprintf(stderr, "Bad ext-community: too big\n");
+ return (0);
+ }
+ r->extcommunity.data.ext_opaq = ullval;
+ break;
+ case EXT_COMMUNITY_NON_TRANS_OPAQUE:
+ if (strcmp(word, "valid") == 0)
+ r->extcommunity.data.ext_opaq = EXT_COMMUNITY_OVS_VALID;
+ else if (strcmp(word, "invalid") == 0)
+ r->extcommunity.data.ext_opaq =
+ EXT_COMMUNITY_OVS_INVALID;
+ else if (strcmp(word, "not-found") == 0)
+ r->extcommunity.data.ext_opaq =
+ EXT_COMMUNITY_OVS_NOTFOUND;
+ else {
+ fprintf(stderr, "Bad ext-community value: %s\n", word);
+ return (0);
+ }
+ break;
+ }
+ r->extcommunity.type = type;
+
+ /* verify type/subtype combo */
+ for (cp = iana_ext_comms; cp->subname != NULL; cp++) {
+ if (cp->type == r->extcommunity.type &&
+ cp->subtype == r->extcommunity.subtype) {
+ r->extcommunity.flags |= EXT_COMMUNITY_FLAG_VALID;
+ if ((fs = calloc(1, sizeof(struct filter_set))) == NULL)
+ err(1, NULL);
+
+ fs->type = ACTION_SET_EXT_COMMUNITY;
+ memcpy(&fs->action.ext_community, &r->extcommunity,
+ sizeof(struct filter_extcommunity));
+
+ TAILQ_INSERT_TAIL(&r->set, fs, entry);
+ return (1);
+ }
+ }
+
+ fprintf(stderr, "Bad ext-community: bad format for type\n");
+ return (0);
+}
+
u_int
getlargecommunity(const char *s)
{