Hello, Here is a patch to allow usage of extended communities with bgpctl :
bgpctl show ip bgp ext-community rt 10:10 bgpctl netw add 2001:db8:1::/64 ext soo 10:50 Denis Index: bgpctl/bgpctl.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v retrieving revision 1.195 diff -u -p -r1.195 bgpctl.c --- bgpctl/bgpctl.c 31 May 2017 10:48:06 -0000 1.195 +++ bgpctl/bgpctl.c 3 Jun 2017 11:25:00 -0000 @@ -261,6 +261,11 @@ main(int argc, char *argv[]) sizeof(res->community)); type = IMSG_CTL_SHOW_RIB_COMMUNITY; } + if (res->extcommunity.flags == EXT_COMMUNITY_FLAG_VALID) { + memcpy(&ribreq.extcommunity, &res->extcommunity, + sizeof(res->extcommunity)); + type = IMSG_CTL_SHOW_RIB_EXTCOMMUNITY; + } if (res->large_community.as != COMMUNITY_UNSET && res->large_community.ld1 != COMMUNITY_UNSET && res->large_community.ld2 != COMMUNITY_UNSET) { Index: bgpctl/parser.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/parser.c,v retrieving revision 1.77 diff -u -p -r1.77 parser.c --- bgpctl/parser.c 14 Feb 2017 13:13:23 -0000 1.77 +++ bgpctl/parser.c 3 Jun 2017 11:25:00 -0000 @@ -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_communi { 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,6 +469,9 @@ int parse_number(const char *, struct 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_nexthop(const char *, struct parse_result *); @@ -595,6 +652,24 @@ match_token(int *argc, char **argv[], co 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 +768,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 +793,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; @@ -1011,6 +1090,180 @@ done: TAILQ_INSERT_TAIL(&r->set, fs, entry); 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 Index: bgpctl/parser.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/parser.h,v retrieving revision 1.29 diff -u -p -r1.29 parser.h --- bgpctl/parser.h 13 Jan 2017 18:59:12 -0000 1.29 +++ bgpctl/parser.h 3 Jun 2017 11:25:00 -0000 @@ -63,6 +63,7 @@ struct parse_result { struct filter_as as; struct filter_set_head set; struct filter_community community; + struct filter_extcommunity extcommunity; struct filter_largecommunity large_community; char peerdesc[PEER_DESCR_LEN]; char rib[PEER_DESCR_LEN]; Index: bgpd/bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.308 diff -u -p -r1.308 bgpd.h --- bgpd/bgpd.h 31 May 2017 10:44:00 -0000 1.308 +++ bgpd/bgpd.h 3 Jun 2017 11:25:00 -0000 @@ -383,6 +383,7 @@ enum imsg_type { IMSG_CTL_SHOW_RIB_PREFIX, IMSG_CTL_SHOW_RIB_ATTR, IMSG_CTL_SHOW_RIB_COMMUNITY, + IMSG_CTL_SHOW_RIB_EXTCOMMUNITY, IMSG_CTL_SHOW_RIB_LARGECOMMUNITY, IMSG_CTL_SHOW_NETWORK, IMSG_CTL_SHOW_RIB_MEM, @@ -697,6 +698,7 @@ struct ctl_show_rib_request { struct bgpd_addr prefix; struct filter_as as; struct filter_community community; + struct filter_extcommunity extcommunity; struct filter_largecommunity large_community; u_int32_t peerid; pid_t pid; Index: bgpd/control.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/control.c,v retrieving revision 1.88 diff -u -p -r1.88 control.c --- bgpd/control.c 28 May 2017 12:21:36 -0000 1.88 +++ bgpd/control.c 3 Jun 2017 11:25:00 -0000 @@ -252,6 +252,7 @@ control_dispatch_msg(struct pollfd *pfd, case IMSG_CTL_SHOW_RIB_PREFIX: case IMSG_CTL_SHOW_RIB_MEM: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY: case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_NETWORK: case IMSG_CTL_SHOW_TERSE: @@ -480,6 +481,7 @@ control_dispatch_msg(struct pollfd *pfd, break; case IMSG_CTL_SHOW_RIB_MEM: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY: case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_NETWORK: c->ibuf.pid = imsg.hdr.pid; Index: bgpd/rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.368 diff -u -p -r1.368 rde.c --- bgpd/rde.c 29 May 2017 13:10:40 -0000 1.368 +++ bgpd/rde.c 3 Jun 2017 11:25:00 -0000 @@ -571,6 +571,7 @@ badnet: case IMSG_CTL_SHOW_RIB: case IMSG_CTL_SHOW_RIB_AS: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY: case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: case IMSG_CTL_SHOW_RIB_PREFIX: if (imsg.hdr.len != IMSG_HEADER_SIZE + sizeof(req)) { @@ -2308,6 +2309,9 @@ rde_dump_filter(struct prefix *p, struct !community_match(p->aspath, req->community.as, req->community.type)) return; + if (req->type == IMSG_CTL_SHOW_RIB_EXTCOMMUNITY && + !community_ext_match(p->aspath, &req->extcommunity, 0)) + return; if (req->type == IMSG_CTL_SHOW_RIB_LARGECOMMUNITY && !community_large_match(p->aspath, req->large_community.as, req->large_community.ld1, req->large_community.ld2)) @@ -2394,6 +2398,7 @@ rde_dump_ctx_new(struct ctl_show_rib_req case IMSG_CTL_SHOW_RIB: case IMSG_CTL_SHOW_RIB_AS: case IMSG_CTL_SHOW_RIB_COMMUNITY: + case IMSG_CTL_SHOW_RIB_EXTCOMMUNITY: case IMSG_CTL_SHOW_RIB_LARGECOMMUNITY: ctx->ribctx.ctx_upcall = rde_dump_upcall; break;