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);
 }
 
 /*

Reply via email to