This adds the missing parser for messages aka updates. It is reusing some of bgpd's functions which I already moved to util.c for that and reimplements a few other bits. It also extends the current attribute printing code to support all attributes even those that are not seen in the ususal show rib case.
With this it is possible to look at mrt dump files bgpd (or other bgp speakers) are producing and printing their content. For me this is a good debugging tool since it will print more information (e.g. when handling notifications). There are a few things in the output I would like to do better but I think those can follow once this is committed. -- :wq Claudio Index: bgpctl.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v retrieving revision 1.207 diff -u -p -r1.207 bgpctl.c --- bgpctl.c 20 Jul 2018 12:49:49 -0000 1.207 +++ bgpctl.c 21 Jul 2018 08:39:20 -0000 @@ -1434,15 +1434,112 @@ 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; + struct bgpd_addr prefix; + char *aspath; u_int32_t as; - u_int16_t alen, ioff; - u_int8_t flags, type; - int i; + u_int16_t alen, ioff, short_as, afi; + u_int8_t flags, type, safi, aid, prefixlen; + int i, pos, e2, e4; if (len < 3) errx(1, "show_attr: too short bgp attr"); @@ -1468,66 +1565,179 @@ 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: + if (alen < 3) { + bad_len: + printf("bad length"); + break; + } + memcpy(&afi, data, 2); + data += 2; + alen -= 2; + afi = ntohs(afi); + safi = *data++; + alen--; + + if (afi2aid(afi, safi, &aid) == -1) { + printf("bad AFI/SAFI pair"); + break; + } + printf(" %s", aid2str(aid)); + + if (type == ATTR_MP_REACH_NLRI) { + struct bgpd_addr nexthop; + u_int8_t nhlen; + if (len == 0) + goto bad_len; + nhlen = *data++; + alen--; + if (nhlen > len) + goto bad_len; + bzero(&nexthop, sizeof(nexthop)); + switch (aid) { + case AID_INET6: + nexthop.aid = aid; + if (nhlen != 16 && nhlen != 32) + goto bad_len; + memcpy(&nexthop.v6.s6_addr, data, 16); + break; + case AID_VPN_IPv4: + if (nhlen != 12) + goto bad_len; + nexthop.aid = AID_INET; + memcpy(&nexthop.v4, data + sizeof(u_int64_t), + sizeof(nexthop.v4)); + default: + printf("unhandled AID #%u", aid); + goto done; + } + /* ignore reserved (old SNPA) field as per RFC4760 */ + data += nhlen + 1; + alen -= nhlen + 1; + + printf(" nexthop: %s", log_addr(&nexthop)); + } + + while (alen > 0) { + switch (aid) { + case AID_INET6: + pos = nlri_get_prefix6(data, alen, &prefix, + &prefixlen); + break; + case AID_VPN_IPv4: + pos = nlri_get_vpn4(data, alen, &prefix, + &prefixlen, 1); + break; + default: + printf("unhandled AID #%u", aid); + goto done; + } + if (pos == -1) { + printf("bad %s prefix", aid2str(aid)); + break; + } + printf(" %s/%u", log_addr(&prefix), prefixlen); + data += pos; + alen -= pos; + } 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; } + done: + printf("%c", EOL0(flag0)); } void @@ -2045,16 +2255,457 @@ 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) +{ + struct bgpd_addr prefix; + int pos; + u_int16_t wlen, alen; + u_int8_t prefixlen; + + 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; + } + if (wlen > 0) { + printf("\n Withdrawn prefixes:"); + while (wlen > 0) { + if ((pos = nlri_get_prefix(p, wlen, &prefix, + &prefixlen)) == -1) { + printf("bad withdraw prefix"); + return; + } + printf(" %s/%u", log_addr(&prefix), prefixlen); + p += pos; + len -= pos; + wlen -= pos; + } + } + + 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; + } + + if (len > 0) { + printf(" NLRI prefixes:"); + while (len > 0) { + if ((pos = nlri_get_prefix(p, len, &prefix, + &prefixlen)) == -1) { + printf("bad withdraw prefix"); + return; + } + printf(" %s/%u", log_addr(&prefix), prefixlen); + p += pos; + len -= pos; + } + } +} + 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 %s[%u] -> ", print_time(&mm->time), 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