diff options
Diffstat (limited to 'usr.sbin/bgpctl/parser.c')
| -rw-r--r-- | usr.sbin/bgpctl/parser.c | 262 |
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) { |
