diff options
-rw-r--r-- | usr.sbin/bgpctl/bgpctl.c | 250 | ||||
-rw-r--r-- | usr.sbin/bgpctl/parser.c | 154 | ||||
-rw-r--r-- | usr.sbin/bgpctl/parser.h | 4 |
3 files changed, 266 insertions, 142 deletions
diff --git a/usr.sbin/bgpctl/bgpctl.c b/usr.sbin/bgpctl/bgpctl.c index 1c6f24c0ba7..6470ffa6f2c 100644 --- a/usr.sbin/bgpctl/bgpctl.c +++ b/usr.sbin/bgpctl/bgpctl.c @@ -1,4 +1,4 @@ -/* $OpenBSD: bgpctl.c,v 1.238 2019/05/23 14:12:06 claudio Exp $ */ +/* $OpenBSD: bgpctl.c,v 1.239 2019/06/17 11:03:07 claudio Exp $ */ /* * Copyright (c) 2003 Henning Brauer <henning@openbsd.org> @@ -81,6 +81,7 @@ int show_rib_detail_msg(struct imsg *, int, int); void show_rib_brief(struct ctl_show_rib *, u_char *); void show_rib_detail(struct ctl_show_rib *, u_char *, int, int); void show_attr(void *, u_int16_t, int); +void show_communities(u_char *, size_t, int); void show_community(u_char *, u_int16_t); void show_large_community(u_char *, u_int16_t); void show_ext_community(u_char *, u_int16_t); @@ -282,7 +283,7 @@ main(int argc, char *argv[]) } if (res->as.type != AS_UNDEF) ribreq.as = res->as; - if (res->community.type != COMMUNITY_TYPE_NONE) + if (res->community.flags != 0) ribreq.community = res->community; ribreq.neighbor = neighbor; strlcpy(ribreq.rib, res->rib, sizeof(ribreq.rib)); @@ -1252,6 +1253,12 @@ show_rib_detail_msg(struct imsg *imsg, int nodescr, int flag0) asdata += sizeof(struct ctl_show_rib); show_rib_detail(&rib, asdata, nodescr, flag0); break; + case IMSG_CTL_SHOW_RIB_COMMUNITIES: + ilen = imsg->hdr.len - IMSG_HEADER_SIZE; + if (ilen % sizeof(struct community)) + errx(1, "bad IMSG_CTL_SHOW_RIB_COMMUNITIES received"); + show_communities(imsg->data, ilen, flag0); + break; case IMSG_CTL_SHOW_RIB_ATTR: ilen = imsg->hdr.len - IMSG_HEADER_SIZE; if (ilen < 3) @@ -1632,6 +1639,154 @@ show_attr(void *b, u_int16_t len, int flag0) printf("%c", EOL0(flag0)); } +static void +print_community(u_int16_t a, u_int16_t v) +{ + if (a == COMMUNITY_WELLKNOWN) + switch (v) { + case COMMUNITY_GRACEFUL_SHUTDOWN: + printf("GRACEFUL_SHUTDOWN"); + break; + case COMMUNITY_NO_EXPORT: + printf("NO_EXPORT"); + break; + case COMMUNITY_NO_ADVERTISE: + printf("NO_ADVERTISE"); + break; + case COMMUNITY_NO_EXPSUBCONFED: + printf("NO_EXPORT_SUBCONFED"); + break; + case COMMUNITY_NO_PEER: + printf("NO_PEER"); + break; + case COMMUNITY_BLACKHOLE: + printf("BLACKHOLE"); + break; + default: + printf("%hu:%hu", a, v); + break; + } + else + printf("%hu:%hu", a, v); +} + +static void +print_ext_community(u_int8_t *data) +{ + u_int64_t ext; + struct in_addr ip; + u_int32_t as4, u32; + u_int16_t as2, u16; + u_int8_t type, subtype; + + type = data[0]; + subtype = data[1]; + + printf("%s ", log_ext_subtype(type, subtype)); + + switch (type) { + case EXT_COMMUNITY_TRANS_TWO_AS: + memcpy(&as2, data + 2, sizeof(as2)); + memcpy(&u32, data + 4, sizeof(u32)); + printf("%s:%u", log_as(ntohs(as2)), ntohl(u32)); + break; + case EXT_COMMUNITY_TRANS_IPV4: + memcpy(&ip, data + 2, sizeof(ip)); + memcpy(&u16, data + 6, sizeof(u16)); + printf("%s:%hu", inet_ntoa(ip), ntohs(u16)); + break; + case EXT_COMMUNITY_TRANS_FOUR_AS: + memcpy(&as4, data + 2, sizeof(as4)); + memcpy(&u16, data + 6, sizeof(u16)); + printf("%s:%hu", log_as(ntohl(as4)), ntohs(u16)); + break; + case EXT_COMMUNITY_TRANS_OPAQUE: + case EXT_COMMUNITY_TRANS_EVPN: + memcpy(&ext, data, sizeof(ext)); + ext = be64toh(ext) & 0xffffffffffffLL; + printf("0x%llx", (unsigned long long)ext); + break; + case EXT_COMMUNITY_NON_TRANS_OPAQUE: + memcpy(&ext, data, sizeof(ext)); + ext = be64toh(ext) & 0xffffffffffffLL; + switch (ext) { + case EXT_COMMUNITY_OVS_VALID: + printf("valid "); + break; + case EXT_COMMUNITY_OVS_NOTFOUND: + printf("not-found "); + break; + case EXT_COMMUNITY_OVS_INVALID: + printf("invalid "); + break; + default: + printf("0x%llx ", (unsigned long long)ext); + break; + } + break; + default: + memcpy(&ext, data, sizeof(ext)); + printf("0x%llx", (unsigned long long)be64toh(ext)); + } +} + +void +show_communities(u_char *data, size_t len, int flag0) +{ + struct community c; + size_t i; + u_int64_t ext; + u_int8_t type = 0, nt; + + if (len % sizeof(c)) + return; + + for (i = 0; i < len; i += sizeof(c)) { + memcpy(&c, data + i, sizeof(c)); + + nt = c.flags; + if (type != nt) { + if (type != 0) + printf("%c", EOL0(flag0)); + printf(" %s:", print_attr(nt, + ATTR_OPTIONAL | ATTR_TRANSITIVE)); + type = nt; + } + printf(" "); + + switch (nt) { + case COMMUNITY_TYPE_BASIC: + print_community(c.data1, c.data2); + break; + case COMMUNITY_TYPE_LARGE: + printf("%u:%u:%u", c.data1, c.data2, c.data3); + break; + case COMMUNITY_TYPE_EXT: + ext = (u_int64_t)c.data3 << 48; + switch (c.data3 >> 8) { + case EXT_COMMUNITY_TRANS_TWO_AS: + case EXT_COMMUNITY_TRANS_OPAQUE: + case EXT_COMMUNITY_TRANS_EVPN: + case EXT_COMMUNITY_NON_TRANS_OPAQUE: + ext |= ((u_int64_t)c.data1 & 0xffff) << 32; + ext |= (u_int64_t)c.data2; + break; + case EXT_COMMUNITY_TRANS_FOUR_AS: + case EXT_COMMUNITY_TRANS_IPV4: + ext |= (u_int64_t)c.data1 << 16; + ext |= (u_int64_t)c.data2 & 0xffff; + break; + } + ext = htobe64(ext); + + print_ext_community((void *)&ext); + break; + } + } + + printf("%c", EOL0(flag0)); +} + void show_community(u_char *data, u_int16_t len) { @@ -1646,33 +1801,7 @@ show_community(u_char *data, u_int16_t len) memcpy(&v, data + i + 2, sizeof(v)); a = ntohs(a); v = ntohs(v); - if (a == COMMUNITY_WELLKNOWN) - switch (v) { - case COMMUNITY_GRACEFUL_SHUTDOWN: - printf("GRACEFUL_SHUTDOWN"); - break; - case COMMUNITY_NO_EXPORT: - printf("NO_EXPORT"); - break; - case COMMUNITY_NO_ADVERTISE: - printf("NO_ADVERTISE"); - break; - case COMMUNITY_NO_EXPSUBCONFED: - printf("NO_EXPORT_SUBCONFED"); - break; - case COMMUNITY_NO_PEER: - printf("NO_PEER"); - break; - case COMMUNITY_BLACKHOLE: - printf("BLACKHOLE"); - break; - default: - printf("%hu:%hu", a, v); - break; - } - else - printf("%hu:%hu", a, v); - + print_community(a, v); if (i + 4 < len) printf(" "); } @@ -1704,67 +1833,16 @@ show_large_community(u_char *data, u_int16_t len) void show_ext_community(u_char *data, u_int16_t len) { - u_int64_t ext; - struct in_addr ip; - u_int32_t as4, u32; - u_int16_t i, as2, u16; - u_int8_t type, subtype; + u_int16_t i; if (len & 0x7) return; for (i = 0; i < len; i += 8) { - type = data[i]; - subtype = data[i + 1]; - - printf("%s ", log_ext_subtype(type, subtype)); + print_ext_community(data + i); - switch (type) { - case EXT_COMMUNITY_TRANS_TWO_AS: - memcpy(&as2, data + i + 2, sizeof(as2)); - memcpy(&u32, data + i + 4, sizeof(u32)); - printf("%s:%u", log_as(ntohs(as2)), ntohl(u32)); - break; - case EXT_COMMUNITY_TRANS_IPV4: - memcpy(&ip, data + i + 2, sizeof(ip)); - memcpy(&u16, data + i + 6, sizeof(u16)); - printf("%s:%hu", inet_ntoa(ip), ntohs(u16)); - break; - case EXT_COMMUNITY_TRANS_FOUR_AS: - memcpy(&as4, data + i + 2, sizeof(as4)); - memcpy(&u16, data + i + 6, sizeof(u16)); - printf("%s:%hu", log_as(ntohl(as4)), ntohs(u16)); - break; - case EXT_COMMUNITY_TRANS_OPAQUE: - case EXT_COMMUNITY_TRANS_EVPN: - memcpy(&ext, data + i, sizeof(ext)); - ext = be64toh(ext) & 0xffffffffffffLL; - printf("0x%llx", (unsigned long long)ext); - break; - case EXT_COMMUNITY_NON_TRANS_OPAQUE: - memcpy(&ext, data + i, sizeof(ext)); - ext = be64toh(ext) & 0xffffffffffffLL; - switch (ext) { - case EXT_COMMUNITY_OVS_VALID: - printf("valid "); - break; - case EXT_COMMUNITY_OVS_NOTFOUND: - printf("not-found "); - break; - case EXT_COMMUNITY_OVS_INVALID: - printf("invalid "); - break; - default: - printf("0x%llx ", (unsigned long long)ext); - break; - } - break; - default: - memcpy(&ext, data + i, sizeof(ext)); - printf("0x%llx", (unsigned long long)be64toh(ext)); - } if (i + 8 < len) - printf(", "); + printf(" "); } } @@ -1817,6 +1895,12 @@ show_rib_memory_msg(struct imsg *imsg) "%s of memory\n\t and holding %lld references\n", stats.aspath_cnt, fmt_mem(stats.aspath_size), stats.aspath_refs); + printf("%10lld entries for %lld BGP communities " + "using %s of memory\n", stats.comm_cnt, stats.comm_nmemb, + fmt_mem(stats.comm_cnt * sizeof(struct rde_community) + + stats.comm_size * sizeof(struct community))); + printf("\t and holding %lld references\n", + stats.comm_refs); printf("%10lld BGP attributes entries using %s of memory\n", stats.attr_cnt, fmt_mem(stats.attr_cnt * sizeof(struct attr))); diff --git a/usr.sbin/bgpctl/parser.c b/usr.sbin/bgpctl/parser.c index 3bb427cd623..63e1efd9aa7 100644 --- a/usr.sbin/bgpctl/parser.c +++ b/usr.sbin/bgpctl/parser.c @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.c,v 1.94 2019/05/23 14:12:06 claudio Exp $ */ +/* $OpenBSD: parser.c,v 1.95 2019/06/17 11:03:07 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -506,8 +506,8 @@ void show_valid_args(const struct token []); int parse_addr(const char *, struct bgpd_addr *); int parse_asnum(const char *, size_t, u_int32_t *); int parse_number(const char *, struct parse_result *, enum token_type); -void parsecommunity(struct filter_community *c, int type, char *s); -int parseextcommunity(struct filter_community *c, const char *t, char *s); +void parsecommunity(struct community *c, int type, char *s); +void parseextcommunity(struct community *c, const char *t, char *s); int parse_nexthop(const char *, struct parse_result *); int bgpctl_getopt(int *, char **[], int); @@ -733,7 +733,7 @@ match_token(int *argc, char **argv[], const struct token table[]) case RD: if (word != NULL && wordlen > 0) { char *p = strdup(word); - struct filter_community ext; + struct community ext; u_int64_t rd; if (p == NULL) @@ -741,21 +741,22 @@ match_token(int *argc, char **argv[], const struct token table[]) parseextcommunity(&ext, "rt", p); free(p); - switch (ext.c.e.type) { + switch (ext.data3 >> 8) { case EXT_COMMUNITY_TRANS_TWO_AS: rd = (0ULL << 48); - rd |= (u_int64_t)ext.c.e.data1 << 32; - rd |= ext.c.e.data2 & 0xffffffff; - break; + rd |= ((u_int64_t)ext.data1 & 0xffff) + << 32; + rd |= (u_int64_t)ext.data2; + break; case EXT_COMMUNITY_TRANS_IPV4: rd = (1ULL << 48); - rd |= (u_int64_t)ext.c.e.data1 << 16; - rd |= ext.c.e.data2 & 0xffff; + rd |= (u_int64_t)ext.data1 << 16; + rd |= (u_int64_t)ext.data2 & 0xffff; break; case EXT_COMMUNITY_TRANS_FOUR_AS: rd = (2ULL << 48); - rd |= (u_int64_t)ext.c.e.data1 << 16; - rd |= ext.c.e.data2 & 0xffff; + rd |= (u_int64_t)ext.data1 << 16; + rd |= (u_int64_t)ext.data2 & 0xffff; break; default: errx(1, "bad encoding of rd"); @@ -1098,7 +1099,7 @@ parse_number(const char *word, struct parse_result *r, enum token_type type) } static void -getcommunity(char *s, int large, u_int32_t *val, u_int8_t *flag) +getcommunity(char *s, int large, u_int32_t *val, u_int32_t *flag) { long long max = USHRT_MAX; const char *errstr; @@ -1123,21 +1124,22 @@ getcommunity(char *s, int large, u_int32_t *val, u_int8_t *flag) } static void -setcommunity(struct filter_community *c, u_int32_t as, u_int32_t data, - u_int8_t asflag, u_int8_t dataflag) +setcommunity(struct community *c, u_int32_t as, u_int32_t data, + u_int32_t asflag, u_int32_t dataflag) { - memset(c, 0, sizeof(*c)); - c->type = COMMUNITY_TYPE_BASIC; - c->dflag1 = asflag; - c->dflag2 = dataflag; - c->c.b.data1 = as; - c->c.b.data2 = data; + c->flags = COMMUNITY_TYPE_BASIC; + c->flags |= asflag << 8; + c->flags |= dataflag << 16; + c->data1 = as; + c->data2 = data; + c->data3 = 0; } static void -parselargecommunity(struct filter_community *c, char *s) +parselargecommunity(struct community *c, char *s) { char *p, *q; + u_int32_t dflag1, dflag2, dflag3; if ((p = strchr(s, ':')) == NULL) errx(1, "Bad community syntax"); @@ -1147,19 +1149,21 @@ parselargecommunity(struct filter_community *c, char *s) errx(1, "Bad community syntax"); *q++ = 0; - getcommunity(s, 1, &c->c.l.data1, &c->dflag1); - getcommunity(p, 1, &c->c.l.data2, &c->dflag2); - getcommunity(q, 1, &c->c.l.data3, &c->dflag3); + getcommunity(s, 1, &c->data1, &dflag1); + getcommunity(p, 1, &c->data2, &dflag2); + getcommunity(q, 1, &c->data3, &dflag3); - c->type = COMMUNITY_TYPE_LARGE; + c->flags = COMMUNITY_TYPE_LARGE; + c->flags |= dflag1 << 8;; + c->flags |= dflag2 << 16;; + c->flags |= dflag3 << 24;; } void -parsecommunity(struct filter_community *c, int type, char *s) +parsecommunity(struct community *c, int type, char *s) { char *p; - u_int32_t as, data; - u_int8_t asflag, dataflag; + u_int32_t as, data, asflag, dataflag; if (type == COMMUNITY_TYPE_LARGE) { parselargecommunity(c, s); @@ -1208,7 +1212,6 @@ parsesubtype(const char *name, int *type, int *subtype) const struct ext_comm_pairs *cp; int found = 0; -printf("%s: looking for %s\n", __func__, name); for (cp = iana_ext_comms; cp->subname != NULL; cp++) { if (strcmp(name, cp->subname) == 0) { if (found == 0) { @@ -1224,7 +1227,7 @@ printf("%s: looking for %s\n", __func__, name); } static int -parseextvalue(int type, char *s, u_int32_t *v) +parseextvalue(int type, char *s, u_int32_t *v, u_int32_t *flag) { const char *errstr; char *p; @@ -1233,6 +1236,14 @@ parseextvalue(int type, char *s, u_int32_t *v) if (type != -1) { /* nothing */ + } else if (strcmp(s, "neighbor-as") == 0) { + *flag = COMMUNITY_NEIGHBOR_AS; + *v = 0; + return EXT_COMMUNITY_TRANS_FOUR_AS; + } else if (strcmp(s, "local-as") == 0) { + *flag = COMMUNITY_LOCAL_AS; + *v = 0; + return EXT_COMMUNITY_TRANS_FOUR_AS; } else if ((p = strchr(s, '.')) == NULL) { /* AS_PLAIN number (4 or 2 byte) */ strtonum(s, 0, USHRT_MAX, &errstr); @@ -1284,16 +1295,20 @@ parseextvalue(int type, char *s, u_int32_t *v) return (type); } -int -parseextcommunity(struct filter_community *c, const char *t, char *s) +void +parseextcommunity(struct community *c, const char *t, char *s) { const struct ext_comm_pairs *cp; - const char *errstr; - u_int64_t ullval; - u_int32_t uval; char *p, *ep; + u_int64_t ullval; + u_int32_t uval, uval2, dflag1 = 0, dflag2 = 0; int type = 0, subtype = 0; + if (strcmp(t, "*") == 0 && strcmp(s, "*") == 0) { + c->flags = COMMUNITY_TYPE_EXT; + c->flags |= COMMUNITY_ANY << 24; + return; + } if (parsesubtype(t, &type, &subtype) == 0) errx(1, "Bad ext-community unknown type"); @@ -1302,55 +1317,80 @@ parseextcommunity(struct filter_community *c, const char *t, char *s) case EXT_COMMUNITY_TRANS_FOUR_AS: case EXT_COMMUNITY_TRANS_IPV4: case -1: + if (strcmp(s, "*") == 0) { + dflag1 = COMMUNITY_ANY; + break; + } if ((p = strchr(s, ':')) == NULL) errx(1, "Bad ext-community %s", s); *p++ = '\0'; - type = parseextvalue(type, s, &uval); + type = parseextvalue(type, s, &uval, &dflag1); + switch (type) { case EXT_COMMUNITY_TRANS_TWO_AS: - ullval = strtonum(p, 0, UINT_MAX, &errstr); + getcommunity(p, 1, &uval2, &dflag2); break; case EXT_COMMUNITY_TRANS_IPV4: case EXT_COMMUNITY_TRANS_FOUR_AS: - ullval = strtonum(p, 0, USHRT_MAX, &errstr); + getcommunity(p, 0, &uval2, &dflag2); break; default: errx(1, "parseextcommunity: unexpected result"); } - if (errstr) - errx(1, "Bad ext-community %s is %s", p, errstr); - c->c.e.data1 = uval; - c->c.e.data2 = ullval; + + c->data1 = uval; + c->data2 = uval2; break; case EXT_COMMUNITY_TRANS_OPAQUE: case EXT_COMMUNITY_TRANS_EVPN: + if (strcmp(s, "*") == 0) { + dflag1 = COMMUNITY_ANY; + break; + } errno = 0; ullval = strtoull(s, &ep, 0); if (s[0] == '\0' || *ep != '\0') errx(1, "Bad ext-community bad value"); if (errno == ERANGE && ullval > EXT_COMMUNITY_OPAQUE_MAX) errx(1, "Bad ext-community value too big"); - c->c.e.data2 = ullval; + c->data1 = ullval >> 32; + c->data2 = ullval; break; case EXT_COMMUNITY_NON_TRANS_OPAQUE: - if (strcmp(s, "valid") == 0) - c->c.e.data2 = EXT_COMMUNITY_OVS_VALID; - else if (strcmp(s, "invalid") == 0) - c->c.e.data2 = EXT_COMMUNITY_OVS_INVALID; - else if (strcmp(s, "not-found") == 0) - c->c.e.data2 = EXT_COMMUNITY_OVS_NOTFOUND; - else - errx(1, "Bad ext-community %s", s); - break; + if (subtype == EXT_COMMUNITY_SUBTYPE_OVS) { + if (strcmp(s, "valid") == 0) { + c->data2 = EXT_COMMUNITY_OVS_VALID; + break; + } else if (strcmp(s, "invalid") == 0) { + c->data2 = EXT_COMMUNITY_OVS_INVALID; + break; + } else if (strcmp(s, "not-found") == 0) { + c->data2 = EXT_COMMUNITY_OVS_NOTFOUND; + break; + } else if (strcmp(s, "*") == 0) { + dflag1 = COMMUNITY_ANY; + break; + } + } + errx(1, "Bad ext-community %s", s); } - c->c.e.type = type; - c->c.e.subtype = subtype; + c->data3 = type << 8 | subtype; + + /* special handling of ext-community rt * since type is not known */ + if (dflag1 == COMMUNITY_ANY && type == -1) { + c->flags = COMMUNITY_TYPE_EXT; + c->flags |= dflag1 << 8; + return; + } + /* verify type/subtype combo */ for (cp = iana_ext_comms; cp->subname != NULL; cp++) { if (cp->type == type && cp->subtype == subtype) { - c->type = COMMUNITY_TYPE_EXT; - return (0); + c->flags = COMMUNITY_TYPE_EXT; + c->flags |= dflag1 << 8; + c->flags |= dflag2 << 16; + return; } } diff --git a/usr.sbin/bgpctl/parser.h b/usr.sbin/bgpctl/parser.h index 4981054cdab..bfafb336405 100644 --- a/usr.sbin/bgpctl/parser.h +++ b/usr.sbin/bgpctl/parser.h @@ -1,4 +1,4 @@ -/* $OpenBSD: parser.h,v 1.35 2019/02/11 15:47:55 claudio Exp $ */ +/* $OpenBSD: parser.h,v 1.36 2019/06/17 11:03:07 claudio Exp $ */ /* * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> @@ -62,7 +62,7 @@ struct parse_result { struct bgpd_addr peeraddr; struct filter_as as; struct filter_set_head set; - struct filter_community community; + struct community community; char peerdesc[PEER_DESCR_LEN]; char rib[PEER_DESCR_LEN]; char shutcomm[SHUT_COMM_LEN]; |