ok
Claudio Jeker([email protected]) on 2021.07.13 15:37:36 +0200:
> 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.
> */
>
> /*
>