This diff adds support to read MRT files using the new introduced _ADDPATH types as defined in RFC8050. I also started adding MRT support to bgpd but that depends on ADD-PATH itself.
There are a few gotchas, especially the MRT_DUMP_V2 RIB_GENERIC_ADDPATH handling is different from all other RIB entry handling. This is a major pain point for bgpd less so for the bgpctl parser. Some MRT update dumps that can be downloaded and use ADDPATH do actually use the BGP4MP_MESSAGE _ADDPATH variant for non-addpath enabled sessions. The update messages can not be parsed because the NLRI encoding is incorrect. I tested with a few RIB and UPDATE dumps from RIS, route-views and other open collectors and it works for me. -- :wq Claudio Index: usr.sbin/bgpctl/bgpctl.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.c,v retrieving revision 1.269 diff -u -p -r1.269 bgpctl.c --- usr.sbin/bgpctl/bgpctl.c 16 Jun 2021 16:24:11 -0000 1.269 +++ usr.sbin/bgpctl/bgpctl.c 13 Jul 2021 13:20:51 -0000 @@ -470,7 +470,7 @@ show(struct imsg *imsg, struct parse_res warnx("bad IMSG_CTL_SHOW_RIB_ATTR received"); break; } - output->attr(imsg->data, ilen, res->flags); + output->attr(imsg->data, ilen, res->flags, 0); break; case IMSG_CTL_SHOW_RIB_MEM: if (imsg->hdr.len < IMSG_HEADER_SIZE + sizeof(stats)) @@ -1150,6 +1150,10 @@ show_mrt_dump(struct mrt_rib *mr, struct ctl.local_pref = mre->local_pref; ctl.med = mre->med; /* weight is not part of the mrt dump so it can't be set */ + if (mr->add_path) { + ctl.flags |= F_PREF_PATH_ID; + ctl.path_id = mre->path_id; + } if (mre->peer_idx < mp->npeers) { ctl.remote_addr = mp->peers[mre->peer_idx].addr; @@ -1195,7 +1199,7 @@ show_mrt_dump(struct mrt_rib *mr, struct if (req->flags & F_CTL_DETAIL) { for (j = 0; j < mre->nattrs; j++) output->attr(mre->attrs[j].attr, - mre->attrs[j].attr_len, req->flags); + mre->attrs[j].attr_len, req->flags, 0); } } } @@ -1211,6 +1215,10 @@ network_mrt_dump(struct mrt_rib *mr, str time_t now; u_int16_t i, j; + /* can't announce more than one path so ignore add-path */ + if (mr->add_path) + return; + now = time(NULL); for (i = 0; i < mr->nentries; i++) { mre = &mr->entries[i]; @@ -1586,10 +1594,11 @@ show_mrt_notification(u_char *p, u_int16 /* XXX this function does not handle JSON output */ static void -show_mrt_update(u_char *p, u_int16_t len, int reqflags) +show_mrt_update(u_char *p, u_int16_t len, int reqflags, int addpath) { struct bgpd_addr prefix; int pos; + u_int32_t pathid; u_int16_t wlen, alen; u_int8_t prefixlen; @@ -1609,12 +1618,25 @@ show_mrt_update(u_char *p, u_int16_t len if (wlen > 0) { printf("\n Withdrawn prefixes:"); while (wlen > 0) { + if (addpath) { + if (wlen <= sizeof(pathid)) { + printf("bad withdraw prefix"); + return; + } + memcpy(&pathid, p, sizeof(pathid)); + pathid = ntohl(pathid); + p += sizeof(pathid); + len -= sizeof(pathid); + wlen -= sizeof(pathid); + } if ((pos = nlri_get_prefix(p, wlen, &prefix, &prefixlen)) == -1) { printf("bad withdraw prefix"); return; } printf(" %s/%u", log_addr(&prefix), prefixlen); + if (addpath) + printf(" path-id %u", pathid); p += pos; len -= pos; wlen -= pos; @@ -1655,7 +1677,7 @@ show_mrt_update(u_char *p, u_int16_t len attrlen += 1 + 2; } - output->attr(p, attrlen, reqflags); + output->attr(p, attrlen, reqflags, addpath); p += attrlen; alen -= attrlen; len -= attrlen; @@ -1664,12 +1686,24 @@ show_mrt_update(u_char *p, u_int16_t len if (len > 0) { printf(" NLRI prefixes:"); while (len > 0) { + if (addpath) { + if (len <= sizeof(pathid)) { + printf(" bad nlri prefix: pathid, len %d", len); + return; + } + memcpy(&pathid, p, sizeof(pathid)); + pathid = ntohl(pathid); + p += sizeof(pathid); + len -= sizeof(pathid); + } if ((pos = nlri_get_prefix(p, len, &prefix, &prefixlen)) == -1) { - printf("bad withdraw prefix"); + printf(" bad nlri prefix"); return; } printf(" %s/%u", log_addr(&prefix), prefixlen); + if (addpath) + printf(" path-id %u", pathid); p += pos; len -= pos; } @@ -1739,7 +1773,8 @@ show_mrt_msg(struct mrt_bgp_msg *mm, voi printf("illegal length: %u byte\n", len); return; } - show_mrt_update(p, len - MSGSIZE_HEADER, req->flags); + show_mrt_update(p, len - MSGSIZE_HEADER, req->flags, + mm->add_path); break; case KEEPALIVE: printf("%s ", msgtypenames[type]); Index: usr.sbin/bgpctl/bgpctl.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/bgpctl.h,v retrieving revision 1.11 diff -u -p -r1.11 bgpctl.h --- usr.sbin/bgpctl/bgpctl.h 3 May 2021 14:01:56 -0000 1.11 +++ usr.sbin/bgpctl/bgpctl.h 13 Jul 2021 13:20:51 -0000 @@ -24,7 +24,7 @@ struct output { void (*fib_table)(struct ktable *); void (*nexthop)(struct ctl_show_nexthop *); void (*interface)(struct ctl_show_interface *); - void (*attr)(u_char *, size_t, int); + void (*attr)(u_char *, size_t, int, int); void (*communities)(u_char *, size_t, struct parse_result *); void (*rib)(struct ctl_show_rib *, u_char *, size_t, struct parse_result *); Index: usr.sbin/bgpctl/mrtparser.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/mrtparser.c,v retrieving revision 1.14 diff -u -p -r1.14 mrtparser.c --- usr.sbin/bgpctl/mrtparser.c 18 Jan 2021 12:16:09 -0000 1.14 +++ usr.sbin/bgpctl/mrtparser.c 13 Jul 2021 13:20:51 -0000 @@ -103,8 +103,10 @@ mrt_parse(int fd, struct mrt_parser *p, struct mrt_bgp_state *s; struct mrt_bgp_msg *m; void *msg; + int addpath; while ((msg = mrt_read_msg(fd, &h))) { + addpath = 0; switch (ntohs(h.type)) { case MSG_NULL: case MSG_START: @@ -163,6 +165,11 @@ mrt_parse(int fd, struct mrt_parser *p, case MRT_DUMP_V2_RIB_IPV6_UNICAST: case MRT_DUMP_V2_RIB_IPV6_MULTICAST: case MRT_DUMP_V2_RIB_GENERIC: + case MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH: + case MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH: + case MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH: + case MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH: + case MRT_DUMP_V2_RIB_GENERIC_ADDPATH: if (p->dump == NULL) break; r = mrt_parse_v2_rib(&h, msg, verbose); @@ -194,6 +201,10 @@ mrt_parse(int fd, struct mrt_parser *p, case BGP4MP_MESSAGE_AS4: case BGP4MP_MESSAGE_LOCAL: case BGP4MP_MESSAGE_AS4_LOCAL: + case BGP4MP_MESSAGE_ADDPATH: + case BGP4MP_MESSAGE_AS4_ADDPATH: + case BGP4MP_MESSAGE_LOCAL_ADDPATH: + case BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH: if ((m = mrt_parse_msg(&h, msg, verbose))) { if (p->message) p->message(m, p->arg); @@ -362,10 +373,10 @@ mrt_parse_v2_rib(struct mrt_hdr *hdr, vo struct mrt_rib *r; u_int8_t *b = msg; u_int len = ntohl(hdr->length); - u_int32_t snum; + u_int32_t snum, path_id = 0; u_int16_t cnt, i, afi; u_int8_t safi, aid; - int ret; + int ret, addpath = 0; if (len < sizeof(snum) + 1) return NULL; @@ -381,6 +392,10 @@ mrt_parse_v2_rib(struct mrt_hdr *hdr, vo r->seqnum = ntohl(snum); switch (ntohs(hdr->subtype)) { + case MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH: + case MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH: + r->add_path = 1; + /* FALLTHROUGH */ case MRT_DUMP_V2_RIB_IPV4_UNICAST: case MRT_DUMP_V2_RIB_IPV4_MULTICAST: /* prefix */ @@ -389,6 +404,10 @@ mrt_parse_v2_rib(struct mrt_hdr *hdr, vo if (ret == 1) goto fail; break; + case MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH: + case MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH: + r->add_path = 1; + /* FALLTHROUGH */ case MRT_DUMP_V2_RIB_IPV6_UNICAST: case MRT_DUMP_V2_RIB_IPV6_MULTICAST: /* prefix */ @@ -397,8 +416,13 @@ mrt_parse_v2_rib(struct mrt_hdr *hdr, vo if (ret == 1) goto fail; break; + case MRT_DUMP_V2_RIB_GENERIC_ADDPATH: + r->add_path = 1; + /* FALLTHROUGH */ case MRT_DUMP_V2_RIB_GENERIC: /* fetch AFI/SAFI pair */ + if (len < 3) + goto fail; memcpy(&afi, b, sizeof(afi)); b += sizeof(afi); len -= sizeof(afi); @@ -410,6 +434,16 @@ mrt_parse_v2_rib(struct mrt_hdr *hdr, vo if ((aid = mrt_afi2aid(afi, safi, verbose)) == AID_UNSPEC) goto fail; + /* RFC8050 handling for add-path */ + if (r->add_path) { + if (len < sizeof(path_id)) + goto fail; + memcpy(&path_id, b, sizeof(path_id)); + b += sizeof(path_id); + len -= sizeof(path_id); + path_id = ntohl(path_id); + } + /* prefix */ ret = mrt_extract_prefix(b, len, aid, &r->prefix, &r->prefixlen, verbose); @@ -453,6 +487,19 @@ mrt_parse_v2_rib(struct mrt_hdr *hdr, vo len -= sizeof(otm); entries[i].originated = ntohl(otm); + /* RFC8050 handling for add-path */ + if (r->add_path && + ntohs(hdr->subtype) != MRT_DUMP_V2_RIB_GENERIC_ADDPATH) { + if (len < sizeof(path_id) + sizeof(alen)) + goto fail; + addpath = 0; + memcpy(&path_id, b, sizeof(path_id)); + b += sizeof(path_id); + len -= sizeof(path_id); + path_id = ntohl(path_id); + } + entries[i].path_id = path_id; + /* attr_len */ memcpy(&alen, b, sizeof(alen)); b += sizeof(alen); @@ -1141,7 +1188,7 @@ mrt_parse_msg(struct mrt_hdr *hdr, void u_int len = ntohl(hdr->length); u_int32_t sas, das, usec; u_int16_t tmp16, afi; - int r; + int r, addpath = 0; u_int8_t aid; t.tv_sec = ntohl(hdr->timestamp); @@ -1156,7 +1203,12 @@ mrt_parse_msg(struct mrt_hdr *hdr, void } switch (ntohs(hdr->subtype)) { + case BGP4MP_MESSAGE_ADDPATH: + case BGP4MP_MESSAGE_LOCAL_ADDPATH: + addpath = 1; + /* FALLTHROUGH */ case BGP4MP_MESSAGE: + case BGP4MP_MESSAGE_LOCAL: if (len < 8) return (0); /* source as */ @@ -1178,7 +1230,12 @@ mrt_parse_msg(struct mrt_hdr *hdr, void len -= sizeof(tmp16); afi = ntohs(tmp16); break; + case BGP4MP_MESSAGE_AS4_ADDPATH: + case BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH: + addpath = 1; + /* FALLTHROUGH */ case BGP4MP_MESSAGE_AS4: + case BGP4MP_MESSAGE_AS4_LOCAL: if (len < 12) return (0); /* source as */ @@ -1213,6 +1270,7 @@ mrt_parse_msg(struct mrt_hdr *hdr, void m->time = t; m->src_as = sas; m->dst_as = das; + m->add_path = addpath; if ((r = mrt_extract_addr(b, len, &m->src, aid)) == -1) goto fail; Index: usr.sbin/bgpctl/mrtparser.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/mrtparser.h,v retrieving revision 1.3 diff -u -p -r1.3 mrtparser.h --- usr.sbin/bgpctl/mrtparser.h 25 Feb 2019 11:51:58 -0000 1.3 +++ usr.sbin/bgpctl/mrtparser.h 13 Jul 2021 13:20:51 -0000 @@ -43,6 +43,7 @@ struct mrt_rib_entry { time_t originated; u_int32_t local_pref; u_int32_t med; + u_int32_t path_id; u_int16_t peer_idx; u_int16_t aspath_len; u_int16_t nattrs; @@ -55,6 +56,7 @@ struct mrt_rib { u_int32_t seqnum; u_int16_t nentries; u_int8_t prefixlen; + u_int8_t add_path; }; /* data structures for the BGP4MP MESSAGE and STATE types */ @@ -75,6 +77,7 @@ struct mrt_bgp_msg { u_int32_t src_as; u_int32_t dst_as; u_int16_t msg_len; + u_int8_t add_path; void *msg; }; Index: usr.sbin/bgpctl/output.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/output.c,v retrieving revision 1.17 diff -u -p -r1.17 output.c --- usr.sbin/bgpctl/output.c 27 May 2021 08:29:07 -0000 1.17 +++ usr.sbin/bgpctl/output.c 13 Jul 2021 13:20:51 -0000 @@ -666,13 +666,13 @@ show_ext_community(u_char *data, u_int16 } static void -show_attr(u_char *data, size_t len, int reqflags) +show_attr(u_char *data, size_t len, int reqflags, int addpath) { u_char *path; struct in_addr id; struct bgpd_addr prefix; char *aspath; - u_int32_t as; + u_int32_t as, pathid; u_int16_t alen, ioff, short_as, afi; u_int8_t flags, type, safi, aid, prefixlen; int i, pos, e2, e4; @@ -851,6 +851,16 @@ show_attr(u_char *data, size_t len, int } while (alen > 0) { + if (addpath) { + if (alen <= sizeof(pathid)) { + printf("bad nlri prefix"); + return; + } + memcpy(&pathid, data, sizeof(pathid)); + pathid = ntohl(pathid); + data += sizeof(pathid); + alen -= sizeof(pathid); + } switch (aid) { case AID_INET6: pos = nlri_get_prefix6(data, alen, &prefix, @@ -873,6 +883,8 @@ show_attr(u_char *data, size_t len, int break; } printf(" %s/%u", log_addr(&prefix), prefixlen); + if (addpath) + printf(" path-id %u", pathid); data += pos; alen -= pos; } @@ -940,7 +952,10 @@ show_rib_detail(struct ctl_show_rib *r, printf("(via %s) Neighbor %s (", log_addr(&r->true_nexthop), s); free(s); id.s_addr = htonl(r->remote_id); - printf("%s)%c", inet_ntoa(id), EOL0(flag0)); + + if (r->flags & F_PREF_PATH_ID) + printf("%s) Path-Id: %u%c", inet_ntoa(id), r->path_id, + EOL0(flag0)); printf(" Origin %s, metric %u, localpref %u, weight %u, ovs %s, ", fmt_origin(r->origin, 0), r->med, r->local_pref, r->weight, Index: usr.sbin/bgpctl/output_json.c =================================================================== RCS file: /cvs/src/usr.sbin/bgpctl/output_json.c,v retrieving revision 1.11 diff -u -p -r1.11 output_json.c --- usr.sbin/bgpctl/output_json.c 27 May 2021 08:29:07 -0000 1.11 +++ usr.sbin/bgpctl/output_json.c 13 Jul 2021 13:20:51 -0000 @@ -586,13 +586,13 @@ json_do_ext_community(u_char *data, uint } static void -json_attr(u_char *data, size_t len, int reqflags) +json_attr(u_char *data, size_t len, int reqflags, int addpath) { struct bgpd_addr prefix; struct in_addr id; char *aspath; u_char *path; - uint32_t as; + uint32_t as, pathid; uint16_t alen, afi, off, short_as; uint8_t flags, type, safi, aid, prefixlen; int e4, e2, pos; @@ -783,6 +783,17 @@ bad_len: json_do_array("NLRI"); while (alen > 0) { + json_do_object("prefix"); + if (addpath) { + if (alen <= sizeof(pathid)) { + json_do_printf("error", "bad path-id"); + break; + } + memcpy(&pathid, data, sizeof(pathid)); + pathid = ntohl(pathid); + data += sizeof(pathid); + alen -= sizeof(pathid); + } switch (aid) { case AID_INET6: pos = nlri_get_prefix6(data, alen, &prefix, @@ -808,9 +819,13 @@ bad_len: } json_do_printf("prefix", "%s/%u", log_addr(&prefix), prefixlen); + if (addpath) + json_do_uint("path_id", pathid); data += pos; alen -= pos; + json_do_end(); } + json_do_end(); break; case ATTR_EXT_COMMUNITIES: json_do_ext_community(data, alen); @@ -854,6 +869,9 @@ json_rib(struct ctl_show_rib *r, u_char id.s_addr = htonl(r->remote_id); json_do_printf("bgp_id", "%s", inet_ntoa(id)); json_do_end(); + + if (r->flags & F_PREF_PATH_ID) + json_do_uint("path_id", r->path_id); /* flags */ json_do_bool("valid", r->flags & F_PREF_ELIGIBLE); Index: usr.sbin/bgpd/bgpd.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/bgpd.h,v retrieving revision 1.415 diff -u -p -r1.415 bgpd.h --- usr.sbin/bgpd/bgpd.h 17 Jun 2021 16:05:26 -0000 1.415 +++ usr.sbin/bgpd/bgpd.h 13 Jul 2021 13:20:51 -0000 @@ -791,6 +791,7 @@ struct ctl_neighbor { #define F_PREF_ANNOUNCE 0x08 #define F_PREF_STALE 0x10 #define F_PREF_INVALID 0x20 +#define F_PREF_PATH_ID 0x40 struct ctl_show_rib { struct bgpd_addr true_nexthop; @@ -800,6 +801,7 @@ struct ctl_show_rib { char descr[PEER_DESCR_LEN]; time_t age; u_int32_t remote_id; + u_int32_t path_id; u_int32_t local_pref; u_int32_t med; u_int32_t weight; Index: usr.sbin/bgpd/mrt.h =================================================================== RCS file: /cvs/src/usr.sbin/bgpd/mrt.h,v retrieving revision 1.35 diff -u -p -r1.35 mrt.h --- usr.sbin/bgpd/mrt.h 31 Dec 2019 15:09:40 -0000 1.35 +++ usr.sbin/bgpd/mrt.h 13 Jul 2021 13:20:51 -0000 @@ -84,7 +84,11 @@ enum MRT_BGP4MP_SUBTYPES { BGP4MP_MESSAGE_AS4, /* same as BGP4MP_MESSAGE with 4byte AS */ BGP4MP_STATE_CHANGE_AS4, BGP4MP_MESSAGE_LOCAL, /* same as BGP4MP_MESSAGE but for self */ - BGP4MP_MESSAGE_AS4_LOCAL /* originated updates. Not implemented */ + BGP4MP_MESSAGE_AS4_LOCAL, /* originated updates. Not implemented */ + BGP4MP_MESSAGE_ADDPATH, /* same as above but for add-path peers */ + BGP4MP_MESSAGE_AS4_ADDPATH, + BGP4MP_MESSAGE_LOCAL_ADDPATH, + BGP4MP_MESSAGE_AS4_LOCAL_ADDPATH, }; /* size of the BGP4MP headers without payload */ @@ -178,7 +182,12 @@ enum MRT_DUMP_V2_SUBTYPES { MRT_DUMP_V2_RIB_IPV4_MULTICAST=3, MRT_DUMP_V2_RIB_IPV6_UNICAST=4, MRT_DUMP_V2_RIB_IPV6_MULTICAST=5, - MRT_DUMP_V2_RIB_GENERIC=6 + MRT_DUMP_V2_RIB_GENERIC=6, + MRT_DUMP_V2_RIB_IPV4_UNICAST_ADDPATH=8, + MRT_DUMP_V2_RIB_IPV4_MULTICAST_ADDPATH=9, + MRT_DUMP_V2_RIB_IPV6_UNICAST_ADDPATH=10, + MRT_DUMP_V2_RIB_IPV6_MULTICAST_ADDPATH=11, + MRT_DUMP_V2_RIB_GENERIC_ADDPATH=12, }; /* @@ -228,7 +237,7 @@ enum MRT_DUMP_V2_SUBTYPES { * | #entry | rib entries (variable) * +--------+--------+--------+--------+ * - * The RIB_GENERIC subtype is needed for the less common AFI/SAFI pairs + * The RIB_GENERIC subtype is needed for the less common AFI/SAFI pairs. * * +--------+--------+--------+--------+ * | seq_num | @@ -249,6 +258,8 @@ enum MRT_DUMP_V2_SUBTYPES { * +--------+--------+--------+--------+ * | originated_time | * +--------+--------+--------+--------+ + * [ path_id in _ADDPATH variants ] + * +--------+--------+--------+--------+ * | attr_len | bgp_attrs * +--------+--------+--------+--------+ * bgp_attrs (variable) ... @@ -257,6 +268,10 @@ enum MRT_DUMP_V2_SUBTYPES { * Some BGP path attributes need special encoding: * - the AS_PATH attribute MUST be encoded as 4-Byte AS * - the MP_REACH_NLRI only consists of the nexthop len and nexthop address + * + * The non generic ADDPATH variants add the path-identifier between + * originated_time and attr_len. For RIB_GENERIC_ADDPATH the path_id should + * be part of the NLRI. */ /*