Implemnt the RFC8050 in the MRT parser. With this MRT dumps containing
authorclaudio <claudio@openbsd.org>
Tue, 27 Jul 2021 07:42:37 +0000 (07:42 +0000)
committerclaudio <claudio@openbsd.org>
Tue, 27 Jul 2021 07:42:37 +0000 (07:42 +0000)
add-path information will be dumped properly.

There is one bit where this implementation is not in line with the RFC.
The encoding of DUMP_V2_RIB_GENERIC_ADDPATH for non IPv4 or IPv6 prefixes
is implemented the way gobgp did it (which seems to be the only other BGP
implementation that supports generic encoding). The RFC puts the path_id
in the NLRI for generic encoding instead of adding it to RIB entry struct
like in the IPv4 and IPv6 encoding. This is an open discussion point with
the RFC author.

OK benno@

usr.sbin/bgpctl/bgpctl.c
usr.sbin/bgpctl/bgpctl.h
usr.sbin/bgpctl/mrtparser.c
usr.sbin/bgpctl/mrtparser.h
usr.sbin/bgpctl/output.c
usr.sbin/bgpctl/output_json.c

index 8386f0b..f50ef36 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: bgpctl.c,v 1.270 2021/07/20 12:08:53 claudio Exp $ */
+/*     $OpenBSD: bgpctl.c,v 1.271 2021/07/27 07:42:37 claudio Exp $ */
 
 /*
  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
@@ -470,7 +470,7 @@ show(struct imsg *imsg, struct parse_result *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 mrt_peer *mp, void *arg)
                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 mrt_peer *mp, void *arg)
                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, struct mrt_peer *mp, void *arg)
        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_t len)
 
 /* 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, int reqflags)
        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, int reqflags)
                        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, int reqflags)
        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, void *arg)
                        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 cf544df..6ccdf4b 100644 (file)
@@ -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 822e64e..3297ed5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mrtparser.c,v 1.14 2021/01/18 12:16:09 claudio Exp $ */
+/*     $OpenBSD: mrtparser.c,v 1.15 2021/07/27 07:42:37 claudio Exp $ */
 /*
  * Copyright (c) 2011 Claudio Jeker <claudio@openbsd.org>
  *
@@ -103,8 +103,10 @@ mrt_parse(int fd, struct mrt_parser *p, int verbose)
        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, int verbose)
                        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, int verbose)
                        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, void *msg, int verbose)
        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, void *msg, int verbose)
        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, void *msg, int verbose)
                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, void *msg, int verbose)
                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, void *msg, int verbose)
                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, void *msg, int verbose)
                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 *msg, int verbose)
        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 *msg, int verbose)
        }
 
        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 *msg, int verbose)
                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 *msg, int verbose)
        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 7da4d62..4a1b2e9 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: mrtparser.h,v 1.3 2019/02/25 11:51:58 claudio Exp $ */
+/*     $OpenBSD: mrtparser.h,v 1.4 2021/07/27 07:42:37 claudio Exp $ */
 /*
  * Copyright (c) 2011 Claudio Jeker <claudio@openbsd.org>
  *
@@ -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 f817c71..6d2f846 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: output.c,v 1.17 2021/05/27 08:29:07 claudio Exp $ */
+/*     $OpenBSD: output.c,v 1.18 2021/07/27 07:42:37 claudio Exp $ */
 
 /*
  * Copyright (c) 2003 Henning Brauer <henning@openbsd.org>
@@ -666,13 +666,13 @@ show_ext_community(u_char *data, u_int16_t len)
 }
 
 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 reqflags)
                }
 
                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 reqflags)
                                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, u_char *asdata, size_t aslen,
        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 03e7025..8a0441b 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: output_json.c,v 1.11 2021/05/27 08:29:07 claudio Exp $ */
+/*     $OpenBSD: output_json.c,v 1.12 2021/07/27 07:42:37 claudio Exp $ */
 
 /*
  * Copyright (c) 2020 Claudio Jeker <claudio@openbsd.org>
@@ -586,13 +586,13 @@ json_do_ext_community(u_char *data, uint16_t len)
 }
 
 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);
@@ -855,6 +870,9 @@ json_rib(struct ctl_show_rib *r, u_char *asdata, size_t aslen,
        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);
        if (r->flags & F_PREF_ACTIVE)