This diff ads enough extra code to parse and display mrt update messages. Some code in bgpd needs to be moved to be reachable by bgpctl. bgpctl code gets reshuffled so that mrt printing works without a running bgpd and also to make the pledge more strict. This is nice to see stuff like the full notification message or other issues with bgp sessions.
If this is considered useful I would like to push the bgpd changes into the tree and split up and work on the bgpctl code more. Current output looks like this: 153.5.146.105[23456] -> 62.48.0.253[0]: Connect -> OpenSent 62.48.0.253[0] -> 153.5.146.105[23456]: size 53 OPEN Version: 4 AS: 8271 Holdtime: 90 BGP Id: 62.48.0.253 Paramlen: 24 Capabilities: size 22 multiprotocol capability: IPv4 unicast route refresh capability graceful restart capability 4-byte AS num capability: AS 8271 153.5.146.105[196618] -> 62.48.0.253[8271]: OpenSent -> OpenConfirm 62.48.0.253[8271] -> 153.5.146.105[196618]: size 19 KEEPALIVE 153.5.146.105[196618] -> 62.48.0.253[8271]: OpenConfirm -> Established 62.48.0.253[8271] -> 153.5.146.105[196618]: size 98 UPDATE Origin: 0 AS-Path: 8271 13030 1299 2914 1280 23599 23599 23599 Nexthop: 62.48.0.253 Communities: 1299:20000 13030:2 13030:1299 13030:7186 13030:51102 withdraw 0 nlri 4 -- :wq Claudio Index: usr.sbin/bgpctl/bgpctl.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v retrieving revision 1.204 diff -u -p -r1.204 bgpctl.c --- usr.sbin/bgpctl/bgpctl.c 11 Jul 2018 16:35:37 -0000 1.204 +++ usr.sbin/bgpctl/bgpctl.c 11 Jul 2018 16:36:38 -0000 @@ -157,19 +157,50 @@ main(int argc, char *argv[]) if ((res = parse(argc, argv)) == NULL) exit(1); - if (res->action == IRRFILTER) { + memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr)); + strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr)); + strlcpy(neighbor.shutcomm, res->shutcomm, sizeof(neighbor.shutcomm)); + + switch (res->action) { + case IRRFILTER: if (!(res->flags & (F_IPV4|F_IPV6))) res->flags |= (F_IPV4|F_IPV6); irr_main(res->as.as, res->flags, res->irr_outdir); + break; + case SHOW_MRT: + if (pledge("stdio", NULL) == -1) + err(1, "pledge"); + + bzero(&ribreq, sizeof(ribreq)); + if (res->as.type != AS_NONE) + ribreq.as = res->as; + if (res->addr.aid) { + ribreq.prefix = res->addr; + ribreq.prefixlen = res->prefixlen; + } + if (res->community.as != COMMUNITY_UNSET && + res->community.type != COMMUNITY_UNSET) + ribreq.community = res->community; + if (res->large_community.as != COMMUNITY_UNSET && + res->large_community.ld1 != COMMUNITY_UNSET && + res->large_community.ld2 != COMMUNITY_UNSET) + ribreq.large_community = res->large_community; + /* XXX extended communities missing? */ + ribreq.neighbor = neighbor; + ribreq.aid = res->aid; + ribreq.flags = res->flags; + show_mrt.arg = &ribreq; + if (!(res->flags & F_CTL_DETAIL)) + show_rib_summary_head(); + mrt_parse(res->mrtfd, &show_mrt, 1); + exit(0); + default: + break; } if (pledge("stdio rpath wpath unix", NULL) == -1) err(1, "pledge"); - memcpy(&neighbor.addr, &res->peeraddr, sizeof(neighbor.addr)); - strlcpy(neighbor.descr, res->peerdesc, sizeof(neighbor.descr)); - strlcpy(neighbor.shutcomm, res->shutcomm, sizeof(neighbor.shutcomm)); - if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) err(1, "control_init: socket"); @@ -181,7 +212,7 @@ main(int argc, char *argv[]) if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) err(1, "connect: %s", sockname); - if (pledge("stdio rpath wpath", NULL) == -1) + if (pledge("stdio", NULL) == -1) err(1, "pledge"); if ((ibuf = malloc(sizeof(struct imsgbuf))) == NULL) @@ -192,6 +223,7 @@ main(int argc, char *argv[]) switch (res->action) { case NONE: case IRRFILTER: + case SHOW_MRT: usage(); /* NOTREACHED */ case SHOW: @@ -281,31 +313,6 @@ main(int argc, char *argv[]) if (!(res->flags & F_CTL_DETAIL)) show_rib_summary_head(); break; - case SHOW_MRT: - close(fd); - bzero(&ribreq, sizeof(ribreq)); - if (res->as.type != AS_NONE) - ribreq.as = res->as; - if (res->addr.aid) { - ribreq.prefix = res->addr; - ribreq.prefixlen = res->prefixlen; - } - if (res->community.as != COMMUNITY_UNSET && - res->community.type != COMMUNITY_UNSET) - ribreq.community = res->community; - if (res->large_community.as != COMMUNITY_UNSET && - res->large_community.ld1 != COMMUNITY_UNSET && - res->large_community.ld2 != COMMUNITY_UNSET) - ribreq.large_community = res->large_community; - /* XXX extended communities missing? */ - ribreq.neighbor = neighbor; - ribreq.aid = res->aid; - ribreq.flags = res->flags; - show_mrt.arg = &ribreq; - if (!(res->flags & F_CTL_DETAIL)) - show_rib_summary_head(); - mrt_parse(res->mrtfd, &show_mrt, 1); - exit(0); case SHOW_RIB_MEM: imsg_compose(ibuf, IMSG_CTL_SHOW_RIB_MEM, 0, 0, -1, NULL, 0); break; @@ -1425,15 +1432,111 @@ show_rib_detail(struct ctl_show_rib *r, fmt_timeframe_core(now), EOL0(flag0)); } +static const char * +print_attr(u_int8_t type, u_int8_t flags) +{ +#define CHECK_FLAGS(s, t, m) \ + if (((s) & ~(ATTR_DEFMASK | (m))) != (t)) pflags = 1 + + static char cstr[48]; + int pflags = 0; + + switch (type) { + case ATTR_ORIGIN: + CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); + strlcpy(cstr, "Origin", sizeof(cstr)); + break; + case ATTR_ASPATH: + CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); + strlcpy(cstr, "AS-Path", sizeof(cstr)); + break; + case ATTR_AS4_PATH: + CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); + strlcpy(cstr, "AS4-Path", sizeof(cstr)); + break; + case ATTR_NEXTHOP: + CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); + strlcpy(cstr, "Nexthop", sizeof(cstr)); + break; + case ATTR_MED: + CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); + strlcpy(cstr, "Med", sizeof(cstr)); + break; + case ATTR_LOCALPREF: + CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); + strlcpy(cstr, "Localpref", sizeof(cstr)); + break; + case ATTR_ATOMIC_AGGREGATE: + CHECK_FLAGS(flags, ATTR_WELL_KNOWN, 0); + strlcpy(cstr, "Atomic Aggregate", sizeof(cstr)); + break; + case ATTR_AGGREGATOR: + CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); + strlcpy(cstr, "Aggregator", sizeof(cstr)); + break; + case ATTR_AS4_AGGREGATOR: + CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); + strlcpy(cstr, "AS4-Aggregator", sizeof(cstr)); + break; + case ATTR_COMMUNITIES: + CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); + strlcpy(cstr, "Communities", sizeof(cstr)); + break; + case ATTR_ORIGINATOR_ID: + CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); + strlcpy(cstr, "Originator Id", sizeof(cstr)); + break; + case ATTR_CLUSTER_LIST: + CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); + strlcpy(cstr, "Cluster Id List", sizeof(cstr)); + break; + case ATTR_MP_REACH_NLRI: + CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); + strlcpy(cstr, "MP Reach NLRI", sizeof(cstr)); + break; + case ATTR_MP_UNREACH_NLRI: + CHECK_FLAGS(flags, ATTR_OPTIONAL, 0); + strlcpy(cstr, "MP Unreach NLRI", sizeof(cstr)); + break; + case ATTR_EXT_COMMUNITIES: + CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); + strlcpy(cstr, "Ext. Communities", sizeof(cstr)); + break; + case ATTR_LARGE_COMMUNITIES: + CHECK_FLAGS(flags, ATTR_OPTIONAL|ATTR_TRANSITIVE, ATTR_PARTIAL); + strlcpy(cstr, "Large Communities", sizeof(cstr)); + break; + default: + /* ignore unknown attributes */ + snprintf(cstr, sizeof(cstr), "Unknown Attribute #%u", type); + pflags = 1; + break; + } + if (pflags) { + strlcat(cstr, " flags [", sizeof(cstr)); + if (flags & ATTR_OPTIONAL) + strlcat(cstr, "O", sizeof(cstr)); + if (flags & ATTR_TRANSITIVE) + strlcat(cstr, "T", sizeof(cstr)); + if (flags & ATTR_PARTIAL) + strlcat(cstr, "P", sizeof(cstr)); + strlcat(cstr, "]", sizeof(cstr)); + } + return (cstr); + +#undef CHECK_FLAGS +} + void show_attr(void *b, u_int16_t len, int flag0) { - char *data = b; + u_char *data = b, *path; struct in_addr id; + char *aspath; u_int32_t as; - u_int16_t alen, ioff; + u_int16_t alen, ioff, short_as; u_int8_t flags, type; - int i; + int i, e2, e4; if (len < 3) errx(1, "show_attr: too short bgp attr"); @@ -1459,66 +1562,105 @@ show_attr(void *b, u_int16_t len, int fl if (alen > len) errx(1, "show_attr: bad length"); + printf(" %s: ", print_attr(type, flags)); + switch (type) { - case ATTR_COMMUNITIES: - printf(" Communities: "); - show_community(data, alen); - printf("%c", EOL0(flag0)); + case ATTR_ORIGIN: + if (alen == 1) + printf("%u", *data); + else + printf("bad length"); break; - case ATTR_LARGE_COMMUNITIES: - printf(" Large Communities: "); - show_large_community(data, alen); - printf("%c", EOL0(flag0)); + case ATTR_ASPATH: + case ATTR_AS4_PATH: + /* prefer 4-byte AS here */ + e4 = aspath_verify(data, alen, 1); + e2 = aspath_verify(data, alen, 0); + if (e4 == 0 || e4 == AS_ERR_SOFT) { + path = data; + } else if (e2 == 0 || e2 == AS_ERR_SOFT) { + path = aspath_inflate(data, alen, &alen); + if (path == NULL) + errx(1, "aspath_inflate failed"); + } else { + printf("bad AS-Path"); + break; + } + if (aspath_asprint(&aspath, path, alen) == -1) + err(1, NULL); + printf("%s", aspath); + free(aspath); + if (path != data) + free(path); + break; + case ATTR_NEXTHOP: + if (alen == 4) { + memcpy(&id, data, sizeof(id)); + printf("%s", inet_ntoa(id)); + } else + printf("bad length"); + break; + case ATTR_MED: + case ATTR_LOCALPREF: + if (alen == 4) { + u_int32_t val; + memcpy(&val, data, sizeof(val)); + val = ntohl(val); + printf("%u", val); + } else + printf("bad length"); break; case ATTR_AGGREGATOR: - memcpy(&as, data, sizeof(as)); - memcpy(&id, data + sizeof(as), sizeof(id)); - printf(" Aggregator: %s [%s]%c", - log_as(ntohl(as)), inet_ntoa(id), EOL0(flag0)); + case ATTR_AS4_AGGREGATOR: + if (alen == 8) { + memcpy(&as, data, sizeof(as)); + memcpy(&id, data + sizeof(as), sizeof(id)); + as = ntohl(as); + } else if (alen == 6) { + memcpy(&short_as, data, sizeof(short_as)); + memcpy(&id, data + sizeof(short_as), sizeof(id)); + as = ntohs(short_as); + } else { + printf("bad length"); + break; + } + printf("%s [%s]", log_as(as), inet_ntoa(id)); + break; + case ATTR_COMMUNITIES: + show_community(data, alen); break; case ATTR_ORIGINATOR_ID: memcpy(&id, data, sizeof(id)); - printf(" Originator Id: %s%c", inet_ntoa(id), EOL0(flag0)); + printf("%s", inet_ntoa(id)); break; case ATTR_CLUSTER_LIST: - printf(" Cluster ID List:"); for (ioff = 0; ioff + sizeof(id) <= alen; ioff += sizeof(id)) { memcpy(&id, data + ioff, sizeof(id)); printf(" %s", inet_ntoa(id)); } - printf("%c", EOL0(flag0)); + break; + case ATTR_MP_REACH_NLRI: + case ATTR_MP_UNREACH_NLRI: + printf("nothing here yet"); break; case ATTR_EXT_COMMUNITIES: - printf(" Ext. communities: "); show_ext_community(data, alen); - printf("%c", EOL0(flag0)); break; - case ATTR_ATOMIC_AGGREGATE: - /* ignore */ + case ATTR_LARGE_COMMUNITIES: + show_large_community(data, alen); break; + case ATTR_ATOMIC_AGGREGATE: default: - /* ignore unknown attributes */ - printf(" Unknown Attribute #%u", type); - if (flags) { - printf(" flags ["); - if (flags & ATTR_OPTIONAL) - printf("O"); - if (flags & ATTR_TRANSITIVE) - printf("T"); - if (flags & ATTR_PARTIAL) - printf("P"); - printf("]"); - } printf(" len %u", alen); if (alen) { printf(":"); for (i=0; i < alen; i++) - printf(" %02x", *(data+i) & 0xFF); + printf(" %02x", *(data+i)); } - printf("%c", EOL0(flag0)); break; } + printf("%c", EOL0(flag0)); } void @@ -2021,15 +2163,430 @@ show_mrt_state(struct mrt_bgp_state *ms, statenames[ms->old_state], statenames[ms->new_state]); } +static void +print_afi(u_char *p, u_int8_t len) +{ + u_int16_t afi; + u_int8_t safi, aid; + + if (len != 4) { + printf("bad length"); + return; + } + + /* afi, 2 byte */ + memcpy(&afi, p, sizeof(afi)); + afi = ntohs(afi); + p += 2; + /* reserved, 1 byte */ + p += 1; + /* safi, 1 byte */ + memcpy(&safi, p, sizeof(safi)); + if (afi2aid(afi, safi, &aid) == -1) + printf("unkown afi %u safi %u", afi, safi); + else + printf("%s", aid2str(aid)); +} + +static void +print_capability(u_int8_t capa_code, u_char *p, u_int8_t len) +{ + switch (capa_code) { + case CAPA_MP: + printf("multiprotocol capability: "); + print_afi(p, len); + break; + case CAPA_REFRESH: + printf("route refresh capability"); + break; + case CAPA_RESTART: + printf("graceful restart capability"); + /* XXX there is more needed here */ + break; + case CAPA_AS4BYTE: + printf("4-byte AS num capability: "); + if (len == 4) { + u_int32_t as; + memcpy(&as, p, sizeof(as)); + as = ntohl(as); + printf("AS %u", as); + } else + printf("bad length"); + break; + default: + printf("unknown capability %u length %u", capa_code, len); + break; + } +} + +static void +print_notification(u_int8_t errcode, u_int8_t subcode) +{ + const char *suberrname = NULL; + int uk = 0; + + switch (errcode) { + case ERR_HEADER: + if (subcode >= sizeof(suberr_header_names)/sizeof(char *)) + uk = 1; + else + suberrname = suberr_header_names[subcode]; + break; + case ERR_OPEN: + if (subcode >= sizeof(suberr_open_names)/sizeof(char *)) + uk = 1; + else + suberrname = suberr_open_names[subcode]; + break; + case ERR_UPDATE: + if (subcode >= sizeof(suberr_update_names)/sizeof(char *)) + uk = 1; + else + suberrname = suberr_update_names[subcode]; + break; + case ERR_CEASE: + if (subcode >= sizeof(suberr_cease_names)/sizeof(char *)) + uk = 1; + else + suberrname = suberr_cease_names[subcode]; + break; + case ERR_HOLDTIMEREXPIRED: + if (subcode != 0) + uk = 1; + break; + case ERR_FSM: + if (subcode >= sizeof(suberr_fsm_names)/sizeof(char *)) + uk = 1; + else + suberrname = suberr_fsm_names[subcode]; + break; + default: + printf("unknown errcode %u, subcode %u", + errcode, subcode); + return; + } + + if (uk) + printf("%s, unknown subcode %u", errnames[errcode], subcode); + else { + if (suberrname == NULL) + printf("%s", errnames[errcode]); + else + printf("%s, %s", errnames[errcode], suberrname); + } +} + +static int +show_mrt_capabilities(u_char *p, u_int16_t len) +{ + u_int16_t totlen = len; + u_int8_t capa_code, capa_len; + + while (len > 2) { + memcpy(&capa_code, p, sizeof(capa_code)); + p += sizeof(capa_code); + len -= sizeof(capa_code); + memcpy(&capa_len, p, sizeof(capa_len)); + p += sizeof(capa_len); + len -= sizeof(capa_len); + if (len < capa_len) { + printf("capa_len %u exceeds remaining length", + capa_len); + return (-1); + } + printf("\n "); + print_capability(capa_code, p, capa_len); + p += capa_len; + len -= capa_len; + } + if (len != 0) { + printf("length missmatch while capability parsing"); + return (-1); + } + return (totlen); +} + +static void +show_mrt_open(u_char *p, u_int16_t len) +{ + u_int8_t version, optparamlen; + u_int16_t short_as, holdtime; + struct in_addr bgpid; + + /* length check up to optparamlen already happened */ + memcpy(&version, p, sizeof(version)); + p += sizeof(version); + len -= sizeof(version); + memcpy(&short_as, p, sizeof(short_as)); + p += sizeof(short_as); + len -= sizeof(short_as); + short_as = ntohs(short_as); + memcpy(&holdtime, p, sizeof(holdtime)); + holdtime = ntohs(holdtime); + p += sizeof(holdtime); + len -= sizeof(holdtime); + memcpy(&bgpid, p, sizeof(bgpid)); + p += sizeof(bgpid); + len -= sizeof(bgpid); + memcpy(&optparamlen, p, sizeof(optparamlen)); + p += sizeof(optparamlen); + len -= sizeof(optparamlen); + + printf("\n "); + printf("Version: %d AS: %u Holdtime: %u BGP Id: %s Paramlen: %u", + version, short_as, holdtime, inet_ntoa(bgpid), optparamlen); + if (optparamlen != len) { + printf("optional parameter length mismatch"); + return; + } + while (len > 2) { + u_int8_t op_type, op_len; + int r; + + memcpy(&op_type, p, sizeof(op_type)); + p += sizeof(op_type); + len -= sizeof(op_type); + memcpy(&op_len, p, sizeof(op_len)); + p += sizeof(op_len); + len -= sizeof(op_len); + + printf("\n "); + switch (op_type) { + case OPT_PARAM_CAPABILITIES: + printf("Capabilities: size %u", op_len); + r = show_mrt_capabilities(p, op_len); + if (r == -1) + return; + p += r; + len -= r; + break; + case OPT_PARAM_AUTH: + default: + printf("unsupported optional parameter: type %u", + op_type); + return; + } + } + if (len != 0) { + printf("optional parameter encoding error"); + return; + } +} + +static void +show_mrt_notification(u_char *p, u_int16_t len) +{ + u_int16_t i; + u_int8_t errcode, subcode, shutcomm_len; + char shutcomm[SHUT_COMM_LEN]; + + memcpy(&errcode, p, sizeof(errcode)); + p += sizeof(errcode); + len -= sizeof(errcode); + + memcpy(&subcode, p, sizeof(subcode)); + p += sizeof(subcode); + len -= sizeof(subcode); + + printf("\n "); + print_notification(errcode, subcode); + + if (errcode == ERR_CEASE && (subcode == ERR_CEASE_ADMIN_DOWN || + subcode == ERR_CEASE_ADMIN_RESET)) { + if (len >= sizeof(shutcomm_len)) { + memcpy(&shutcomm_len, p, sizeof(shutcomm_len)); + p += sizeof(shutcomm_len); + len -= sizeof(shutcomm_len); + if(len < shutcomm_len) { + printf("truncated shutdown reason"); + return; + } + if (shutcomm_len > (SHUT_COMM_LEN-1)) { + printf("overly long shutdown reason"); + return; + } + memcpy(shutcomm, p, shutcomm_len); + shutcomm[shutcomm_len] = '\0'; + printf("shutdown reason: \"%s\"", + log_shutcomm(shutcomm)); + p += shutcomm_len; + len -= shutcomm_len; + } + } + if (errcode == ERR_OPEN && subcode == ERR_OPEN_CAPA) { + int r; + + r = show_mrt_capabilities(p, len); + if (r == -1) + return; + p += r; + len -= r; + } + + if (len > 0) { + printf("\n additional data %u bytes", len); + for (i = 0; i < len; i++) { + if (i % 16 == 0) + printf("\n "); + if (i % 8 == 0) + printf(" "); + printf(" %02X", *p++); + } + } +} + +static void +show_mrt_update(u_char *p, u_int16_t len) +{ + u_int16_t wlen, alen; + + if (len < sizeof(wlen)) { + printf("bad length"); + return; + } + memcpy(&wlen, p, sizeof(wlen)); + wlen = ntohs(wlen); + p += sizeof(wlen); + len -= sizeof(wlen); + + if (len < wlen) { + printf("bad withdraw length"); + return; + } + /* wlen withdraw prefixes here */ + p += wlen; + len -= wlen; + + if (len < sizeof(alen)) { + printf("bad length"); + return; + } + memcpy(&alen, p, sizeof(alen)); + alen = ntohs(alen); + p += sizeof(alen); + len -= sizeof(alen); + + if (len < alen) { + printf("bad attribute length"); + return; + } + printf("\n"); + /* alen attributes here */ + while (alen > 3) { + u_int8_t flags, type; + u_int16_t attrlen; + + flags = p[0]; + type = p[1]; + + /* get the attribute length */ + if (flags & ATTR_EXTLEN) { + if (len < sizeof(attrlen) + 2) + printf("bad attribute length"); + memcpy(&attrlen, &p[2], sizeof(attrlen)); + attrlen = ntohs(attrlen); + attrlen += sizeof(attrlen) + 2; + } else { + attrlen = p[2]; + attrlen += 1 + 2; + } + + show_attr(p, attrlen, 0); + p += attrlen; + alen -= attrlen; + len -= attrlen; + } + + printf("withdraw %u nlri %u", wlen, len); +} + void show_mrt_msg(struct mrt_bgp_msg *mm, void *arg) { + static const u_int8_t marker[MSGSIZE_HEADER_MARKER] = { + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; struct bgpd_addr src, dst; + u_char *p; + u_int16_t len; + u_int8_t type; mrt_to_bgpd_addr(&mm->src, &src); mrt_to_bgpd_addr(&mm->dst, &dst); printf("%s[%u] -> ", log_addr(&src), mm->src_as); - printf("%s[%u]: size %u\n", log_addr(&dst), mm->dst_as, mm->msg_len); + printf("%s[%u]: size %u ", log_addr(&dst), mm->dst_as, mm->msg_len); + p = mm->msg; + len = mm->msg_len; + + if (len < MSGSIZE_HEADER) { + printf("illegal header length: %u byte\n", len); + return; + } + + /* parse BGP message header */ + if (memcmp(p, marker, sizeof(marker))) { + printf("incorrect marker in BGP message\n"); + return; + } + p += MSGSIZE_HEADER_MARKER; + + memcpy(&len, p, 2); + len = ntohs(len); + p += 2; + memcpy(&type, p, 1); + p += 1; + + if (len < MSGSIZE_HEADER || len > MAX_PKTSIZE) { + printf("illegal header length: %u byte\n", len); + return; + } + + switch (type) { + case OPEN: + printf("%s ", msgtypenames[type]); + if (len < MSGSIZE_OPEN_MIN) { + printf("illegal length: %u byte\n", len); + return; + } + show_mrt_open(p, len - MSGSIZE_HEADER); + break; + case NOTIFICATION: + printf("%s ", msgtypenames[type]); + if (len < MSGSIZE_NOTIFICATION_MIN) { + printf("illegal length: %u byte\n", len); + return; + } + show_mrt_notification(p, len - MSGSIZE_HEADER); + break; + case UPDATE: + printf("%s ", msgtypenames[type]); + if (len < MSGSIZE_UPDATE_MIN) { + printf("illegal length: %u byte\n", len); + return; + } + show_mrt_update(p, len - MSGSIZE_HEADER); + break; + case KEEPALIVE: + printf("%s ", msgtypenames[type]); + if (len != MSGSIZE_KEEPALIVE) { + printf("illegal length: %u byte\n", len); + return; + } + /* nothing */ + break; + case RREFRESH: + printf("%s ", msgtypenames[type]); + if (len != MSGSIZE_RREFRESH) { + printf("illegal length: %u byte\n", len); + return; + } + print_afi(p, len); + break; + default: + printf("unknown type %u\n", type); + return; + } + printf("\n"); } void Index: usr.sbin/bgpd/rde.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde.c,v retrieving revision 1.392 diff -u -p -r1.392 rde.c --- usr.sbin/bgpd/rde.c 11 Jul 2018 17:35:07 -0000 1.392 +++ usr.sbin/bgpd/rde.c 12 Jul 2018 19:44:34 -0000 @@ -1464,8 +1464,11 @@ bad_flags: if (rde_as4byte(peer)) { npath = p; nlen = attr_len; - } else + } else { npath = aspath_inflate(p, attr_len, &nlen); + if (npath == NULL) + fatal("aspath_inflate"); + } a->flags |= F_ATTR_ASPATH; a->aspath = aspath_get(npath, nlen); if (npath != p) Index: usr.sbin/bgpd/rde_attr.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/rde_attr.c,v retrieving revision 1.104 diff -u -p -r1.104 rde_attr.c --- usr.sbin/bgpd/rde_attr.c 11 Jul 2018 19:05:41 -0000 1.104 +++ usr.sbin/bgpd/rde_attr.c 11 Jul 2018 21:29:01 -0000 @@ -456,64 +456,6 @@ SIPHASH_KEY astablekey; #define ASPATH_HASH(x) \ &astable.hashtbl[(x) & astable.hashmask] -int -aspath_verify(void *data, u_int16_t len, int as4byte) -{ - u_int8_t *seg = data; - u_int16_t seg_size, as_size = 2; - u_int8_t seg_len, seg_type; - int error = 0; - - if (len & 1) - /* odd length aspath are invalid */ - return (AS_ERR_BAD); - - if (as4byte) - as_size = 4; - - for (; len > 0; len -= seg_size, seg += seg_size) { - const u_char *ptr; - int pos; - - if (len < 2) /* header length check */ - return (AS_ERR_BAD); - seg_type = seg[0]; - seg_len = seg[1]; - - /* - * BGP confederations should not show up but consider them - * as a soft error which invalidates the path but keeps the - * bgp session running. - */ - if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET) - error = AS_ERR_SOFT; - if (seg_type != AS_SET && seg_type != AS_SEQUENCE && - seg_type != AS_CONFED_SEQUENCE && seg_type != AS_CONFED_SET) - return (AS_ERR_TYPE); - - seg_size = 2 + as_size * seg_len; - - if (seg_size > len) - return (AS_ERR_LEN); - - if (seg_size == 0) - /* empty aspath segments are not allowed */ - return (AS_ERR_BAD); - - /* RFC 7607 - AS 0 is considered malformed */ - ptr = seg + 2; - for (pos = 0; pos < seg_len; pos++) { - u_int32_t as = 0; - - ptr += as_size; - memcpy(&as, ptr, as_size); - if (as == 0) - return (AS_ERR_SOFT); - } - } - return (error); /* aspath is valid but probably not loop free */ -} - void aspath_init(u_int32_t hashsize) { @@ -621,46 +563,10 @@ aspath_put(struct aspath *aspath) free(aspath); } -u_char * -aspath_inflate(void *data, u_int16_t len, u_int16_t *newlen) -{ - u_int8_t *seg, *nseg, *ndata; - u_int16_t seg_size, olen, nlen; - u_int8_t seg_len; - - /* first calculate the length of the aspath */ - seg = data; - nlen = 0; - for (olen = len; olen > 0; olen -= seg_size, seg += seg_size) { - seg_len = seg[1]; - seg_size = 2 + sizeof(u_int16_t) * seg_len; - nlen += 2 + sizeof(u_int32_t) * seg_len; - - if (seg_size > olen) - fatalx("aspath_inflate: would overflow"); - } - - *newlen = nlen; - if ((ndata = malloc(nlen)) == NULL) - fatal("aspath_inflate"); - - /* then copy the aspath */ - seg = data; - for (nseg = ndata; nseg < ndata + nlen; ) { - *nseg++ = *seg++; - *nseg++ = seg_len = *seg++; - for (; seg_len > 0; seg_len--) { - *nseg++ = 0; - *nseg++ = 0; - *nseg++ = *seg++; - *nseg++ = *seg++; - } - } - - return (ndata); -} - -/* convert a 4 byte aspath to a 2byte one. data is freed by aspath_deflate */ +/* + * convert a 4 byte aspath to a 2 byte one. + * data is freed by aspath_deflate + */ u_char * aspath_deflate(u_char *data, u_int16_t *len, int *flagnew) { Index: usr.sbin/bgpd/util.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/util.c,v retrieving revision 1.25 diff -u -p -r1.25 util.c --- usr.sbin/bgpd/util.c 31 May 2017 10:44:00 -0000 1.25 +++ usr.sbin/bgpd/util.c 12 Jul 2018 15:19:18 -0000 @@ -20,6 +20,7 @@ #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> +#include <errno.h> #include <netdb.h> #include <stdlib.h> #include <stdio.h> @@ -396,6 +397,111 @@ aspath_extract(const void *seg, int pos) ptr += 2 + sizeof(u_int32_t) * pos; memcpy(&as, ptr, sizeof(u_int32_t)); return (ntohl(as)); +} + +/* + * Verify that the aspath is correctly encoded. + */ +int +aspath_verify(void *data, u_int16_t len, int as4byte) +{ + u_int8_t *seg = data; + u_int16_t seg_size, as_size = 2; + u_int8_t seg_len, seg_type; + int error = 0; + + if (len & 1) + /* odd length aspath are invalid */ + return (AS_ERR_BAD); + + if (as4byte) + as_size = 4; + + for (; len > 0; len -= seg_size, seg += seg_size) { + const u_char *ptr; + int pos; + + if (len < 2) /* header length check */ + return (AS_ERR_BAD); + seg_type = seg[0]; + seg_len = seg[1]; + + /* + * BGP confederations should not show up but consider them + * as a soft error which invalidates the path but keeps the + * bgp session running. + */ + if (seg_type == AS_CONFED_SEQUENCE || seg_type == AS_CONFED_SET) + error = AS_ERR_SOFT; + if (seg_type != AS_SET && seg_type != AS_SEQUENCE && + seg_type != AS_CONFED_SEQUENCE && seg_type != AS_CONFED_SET) + return (AS_ERR_TYPE); + + seg_size = 2 + as_size * seg_len; + + if (seg_size > len) + return (AS_ERR_LEN); + + if (seg_size == 0) + /* empty aspath segments are not allowed */ + return (AS_ERR_BAD); + + /* RFC 7607 - AS 0 is considered malformed */ + ptr = seg + 2; + for (pos = 0; pos < seg_len; pos++) { + u_int32_t as = 0; + + ptr += as_size; + memcpy(&as, ptr, as_size); + if (as == 0) + return (AS_ERR_SOFT); + } + } + return (error); /* aspath is valid but probably not loop free */ +} + +/* + * convert a 2 byte aspath to a 4 byte one. + */ +u_char * +aspath_inflate(void *data, u_int16_t len, u_int16_t *newlen) +{ + u_int8_t *seg, *nseg, *ndata; + u_int16_t seg_size, olen, nlen; + u_int8_t seg_len; + + /* first calculate the length of the aspath */ + seg = data; + nlen = 0; + for (olen = len; olen > 0; olen -= seg_size, seg += seg_size) { + seg_len = seg[1]; + seg_size = 2 + sizeof(u_int16_t) * seg_len; + nlen += 2 + sizeof(u_int32_t) * seg_len; + + if (seg_size > olen) { + errno = ERANGE; + return (NULL); + } + } + + *newlen = nlen; + if ((ndata = malloc(nlen)) == NULL) + return (NULL); + + /* then copy the aspath */ + seg = data; + for (nseg = ndata; nseg < ndata + nlen; ) { + *nseg++ = *seg++; + *nseg++ = seg_len = *seg++; + for (; seg_len > 0; seg_len--) { + *nseg++ = 0; + *nseg++ = 0; + *nseg++ = *seg++; + *nseg++ = *seg++; + } + } + + return (ndata); } /*