]> The Tcpdump Group git mirrors - tcpdump/blobdiff - print-bgp.c
Add some attribute/TLV length checks.
[tcpdump] / print-bgp.c
index 6f35483bd42cb694714e1c0a08a08cbaf6fbd267..dc16d914104b5fb7c29769aa22a72dc97507c7df 100644 (file)
@@ -95,8 +95,6 @@ struct bgp_opt {
 #define BGP_OPT_SIZE           2       /* some compilers may pad to 4 bytes */
 #define BGP_CAP_HEADER_SIZE    2       /* some compilers may pad to 4 bytes */
 
-#define BGP_UPDATE_MINSIZE      23
-
 struct bgp_notification {
        u_int8_t bgpn_marker[16];
        u_int16_t bgpn_len;
@@ -116,19 +114,10 @@ struct bgp_route_refresh {
 };                    /* EXTRACT_16BITS(&bgp_route_refresh->afi) (sigh)      */ 
 #define BGP_ROUTE_REFRESH_SIZE          23
 
-struct bgp_attr {
-       u_int8_t bgpa_flags;
-       u_int8_t bgpa_type;
-       union {
-               u_int8_t len;
-               u_int16_t elen;
-       } bgpa_len;
-#define bgp_attr_len(p) \
-       (((p)->bgpa_flags & 0x10) ? \
-               EXTRACT_16BITS(&(p)->bgpa_len.elen) : (p)->bgpa_len.len)
-#define bgp_attr_off(p) \
-       (((p)->bgpa_flags & 0x10) ? 4 : 3)
-};
+#define bgp_attr_lenlen(flags, p) \
+       (((flags) & 0x10) ? 2 : 1)
+#define bgp_attr_len(flags, p) \
+       (((flags) & 0x10) ? EXTRACT_16BITS(p) : *(p))
 
 #define BGPTYPE_ORIGIN                 1
 #define BGPTYPE_AS_PATH                        2
@@ -494,38 +483,49 @@ as_printf (char *str, int size, u_int asnum)
        return str;
 }
 
+#define ITEMCHECK(minlen) if (itemlen < minlen) goto badtlv;
+
 int
-decode_prefix4(const u_char *pptr, char *buf, u_int buflen)
+decode_prefix4(const u_char *pptr, u_int itemlen, char *buf, u_int buflen)
 {
        struct in_addr addr;
-       u_int plen;
+       u_int plen, plenbytes;
 
        TCHECK(pptr[0]);
+       ITEMCHECK(1);
        plen = pptr[0];
        if (32 < plen)
                return -1;
+       itemlen -= 1;
 
        memset(&addr, 0, sizeof(addr));
-       TCHECK2(pptr[1], (plen + 7) / 8);
-       memcpy(&addr, &pptr[1], (plen + 7) / 8);
+       plenbytes = (plen + 7) / 8;
+       TCHECK2(pptr[1], plenbytes);
+       ITEMCHECK(plenbytes);
+       memcpy(&addr, &pptr[1], plenbytes);
        if (plen % 8) {
-               ((u_char *)&addr)[(plen + 7) / 8 - 1] &=
+               ((u_char *)&addr)[plenbytes - 1] &=
                        ((0xff00 >> (plen % 8)) & 0xff);
        }
        snprintf(buf, buflen, "%s/%d", getname((u_char *)&addr), plen);
-       return 1 + (plen + 7) / 8;
+       return 1 + plenbytes;
 
 trunc:
        return -2;
+
+badtlv:
+       return -3;
 }
 
 static int
-decode_labeled_prefix4(const u_char *pptr, char *buf, u_int buflen)
+decode_labeled_prefix4(const u_char *pptr, u_int itemlen, char *buf, u_int buflen)
 {
        struct in_addr addr;
-       u_int plen;
+       u_int plen, plenbytes;
 
-       TCHECK(pptr[0]);
+       /* prefix length and label = 4 bytes */
+       TCHECK2(pptr[0], 4);
+       ITEMCHECK(4);
        plen = pptr[0];   /* get prefix length */
 
         /* this is one of the weirdnesses of rfc3107
@@ -543,12 +543,15 @@ decode_labeled_prefix4(const u_char *pptr, char *buf, u_int buflen)
 
        if (32 < plen)
                return -1;
+       itemlen -= 4;
 
        memset(&addr, 0, sizeof(addr));
-       TCHECK2(pptr[4], (plen + 7) / 8);
-       memcpy(&addr, &pptr[4], (plen + 7) / 8);
+       plenbytes = (plen + 7) / 8;
+       TCHECK2(pptr[4], plenbytes);
+       ITEMCHECK(plenbytes);
+       memcpy(&addr, &pptr[4], plenbytes);
        if (plen % 8) {
-               ((u_char *)&addr)[(plen + 7) / 8 - 1] &=
+               ((u_char *)&addr)[plenbytes - 1] &=
                        ((0xff00 >> (plen % 8)) & 0xff);
        }
         /* the label may get offsetted by 4 bits so lets shift it right */
@@ -558,10 +561,13 @@ decode_labeled_prefix4(const u_char *pptr, char *buf, u_int buflen)
                  EXTRACT_24BITS(pptr+1)>>4,
                  ((pptr[3]&1)==0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );
 
-       return 4 + (plen + 7) / 8;
+       return 4 + plenbytes;
 
 trunc:
        return -2;
+
+badtlv:
+       return -3;
 }
 
 /*
@@ -1042,37 +1048,46 @@ trunc:
 
 #ifdef INET6
 int
-decode_prefix6(const u_char *pd, char *buf, u_int buflen)
+decode_prefix6(const u_char *pd, u_int itemlen, char *buf, u_int buflen)
 {
        struct in6_addr addr;
-       u_int plen;
+       u_int plen, plenbytes;
 
        TCHECK(pd[0]);
+       ITEMCHECK(1);
        plen = pd[0];
        if (128 < plen)
                return -1;
+       itemlen -= 1;
 
        memset(&addr, 0, sizeof(addr));
-       TCHECK2(pd[1], (plen + 7) / 8);
-       memcpy(&addr, &pd[1], (plen + 7) / 8);
+       plenbytes = (plen + 7) / 8;
+       TCHECK2(pd[1], plenbytes);
+       ITEMCHECK(plenbytes);
+       memcpy(&addr, &pd[1], plenbytes);
        if (plen % 8) {
-               addr.s6_addr[(plen + 7) / 8 - 1] &=
+               addr.s6_addr[plenbytes - 1] &=
                        ((0xff00 >> (plen % 8)) & 0xff);
        }
        snprintf(buf, buflen, "%s/%d", getname6((u_char *)&addr), plen);
-       return 1 + (plen + 7) / 8;
+       return 1 + plenbytes;
 
 trunc:
        return -2;
+
+badtlv:
+       return -3;
 }
 
 static int
-decode_labeled_prefix6(const u_char *pptr, char *buf, u_int buflen)
+decode_labeled_prefix6(const u_char *pptr, u_int itemlen, char *buf, u_int buflen)
 {
        struct in6_addr addr;
-       u_int plen;
+       u_int plen, plenbytes;
 
-       TCHECK(pptr[0]);
+       /* prefix length and label = 4 bytes */
+       TCHECK2(pptr[0], 4);
+       ITEMCHECK(4);
        plen = pptr[0]; /* get prefix length */
 
        if (24 > plen)
@@ -1082,12 +1097,14 @@ decode_labeled_prefix6(const u_char *pptr, char *buf, u_int buflen)
 
        if (128 < plen)
                return -1;
+       itemlen -= 4;
 
        memset(&addr, 0, sizeof(addr));
-       TCHECK2(pptr[4], (plen + 7) / 8);
-       memcpy(&addr, &pptr[4], (plen + 7) / 8);
+       plenbytes = (plen + 7) / 8;
+       TCHECK2(pptr[4], plenbytes);
+       memcpy(&addr, &pptr[4], plenbytes);
        if (plen % 8) {
-               addr.s6_addr[(plen + 7) / 8 - 1] &=
+               addr.s6_addr[plenbytes - 1] &=
                        ((0xff00 >> (plen % 8)) & 0xff);
        }
         /* the label may get offsetted by 4 bits so lets shift it right */
@@ -1097,10 +1114,13 @@ decode_labeled_prefix6(const u_char *pptr, char *buf, u_int buflen)
                  EXTRACT_24BITS(pptr+1)>>4,
                  ((pptr[3]&1)==0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );
 
-       return 4 + (plen + 7) / 8;
+       return 4 + plenbytes;
 
 trunc:
        return -2;
+
+badtlv:
+       return -3;
 }
 
 static int
@@ -1267,7 +1287,7 @@ trunc:
 }
 
 static int
-bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
+bgp_attr_print(u_int atype, const u_char *pptr, u_int len)
 {
        int i;
        u_int16_t af;
@@ -1277,7 +1297,7 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
             u_int32_t i;
         } bw;
        int advance;
-       int tlen;
+       u_int tlen;
        const u_char *tptr;
        char buf[MAXHOSTNAMELEN + 100];
        char tokbuf[TOKBUFSIZE];
@@ -1286,7 +1306,7 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
         tptr = pptr;
         tlen=len;
 
-       switch (attr->bgpa_type) {
+       switch (atype) {
        case BGPTYPE_ORIGIN:
                if (len != 1)
                        printf("invalid len");
@@ -1322,7 +1342,7 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
                  * 2 bytes first, and it does not pass, assume that ASs are
                  * encoded in 4 bytes format and move on.
                  */
-                as_size = bgp_attr_get_as_size(attr->bgpa_type, pptr, len);
+                as_size = bgp_attr_get_as_size(atype, pptr, len);
 
                while (tptr < pptr + len) {
                        TCHECK(tptr[0]);
@@ -1658,20 +1678,24 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
                     case (AFNUM_INET<<8 | SAFNUM_UNICAST):
                     case (AFNUM_INET<<8 | SAFNUM_MULTICAST):
                     case (AFNUM_INET<<8 | SAFNUM_UNIMULTICAST):
-                        advance = decode_prefix4(tptr, buf, sizeof(buf));
+                        advance = decode_prefix4(tptr, len, buf, sizeof(buf));
                         if (advance == -1)
                             printf("\n\t    (illegal prefix length)");
                         else if (advance == -2)
                             goto trunc;
+                        else if (advance == -3)
+                            break; /* bytes left, but not enough */
                         else
                             printf("\n\t      %s", buf);
                         break;
                     case (AFNUM_INET<<8 | SAFNUM_LABUNICAST):
-                        advance = decode_labeled_prefix4(tptr, buf, sizeof(buf));
+                        advance = decode_labeled_prefix4(tptr, len, buf, sizeof(buf));
                         if (advance == -1)
                             printf("\n\t    (illegal prefix length)");
                         else if (advance == -2)
                             goto trunc;
+                        else if (advance == -3)
+                            break; /* bytes left, but not enough */
                         else
                             printf("\n\t      %s", buf);
                         break;
@@ -1719,20 +1743,24 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
                     case (AFNUM_INET6<<8 | SAFNUM_UNICAST):
                     case (AFNUM_INET6<<8 | SAFNUM_MULTICAST):
                     case (AFNUM_INET6<<8 | SAFNUM_UNIMULTICAST):
-                        advance = decode_prefix6(tptr, buf, sizeof(buf));
+                        advance = decode_prefix6(tptr, len, buf, sizeof(buf));
                         if (advance == -1)
                             printf("\n\t    (illegal prefix length)");
                         else if (advance == -2)
                             goto trunc;
+                        else if (advance == -3)
+                            break; /* bytes left, but not enough */
                         else
                             printf("\n\t      %s", buf);
                         break;
                     case (AFNUM_INET6<<8 | SAFNUM_LABUNICAST):
-                        advance = decode_labeled_prefix6(tptr, buf, sizeof(buf));
+                        advance = decode_labeled_prefix6(tptr, len, buf, sizeof(buf));
                         if (advance == -1)
                             printf("\n\t    (illegal prefix length)");
                         else if (advance == -2)
                             goto trunc;
+                        else if (advance == -3)
+                            break; /* bytes left, but not enough */
                         else
                             printf("\n\t      %s", buf);
                         break;
@@ -1822,20 +1850,24 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
                     case (AFNUM_INET<<8 | SAFNUM_UNICAST):
                     case (AFNUM_INET<<8 | SAFNUM_MULTICAST):
                     case (AFNUM_INET<<8 | SAFNUM_UNIMULTICAST):
-                        advance = decode_prefix4(tptr, buf, sizeof(buf));
+                        advance = decode_prefix4(tptr, len, buf, sizeof(buf));
                         if (advance == -1)
                             printf("\n\t    (illegal prefix length)");
                         else if (advance == -2)
                             goto trunc;
+                        else if (advance == -3)
+                            break; /* bytes left, but not enough */
                         else
                             printf("\n\t      %s", buf);
                         break;
                     case (AFNUM_INET<<8 | SAFNUM_LABUNICAST):
-                        advance = decode_labeled_prefix4(tptr, buf, sizeof(buf));
+                        advance = decode_labeled_prefix4(tptr, len, buf, sizeof(buf));
                         if (advance == -1)
                             printf("\n\t    (illegal prefix length)");
                         else if (advance == -2)
                             goto trunc;
+                        else if (advance == -3)
+                            break; /* bytes left, but not enough */
                         else
                             printf("\n\t      %s", buf);
                         break;
@@ -1854,20 +1886,24 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
                     case (AFNUM_INET6<<8 | SAFNUM_UNICAST):
                     case (AFNUM_INET6<<8 | SAFNUM_MULTICAST):
                     case (AFNUM_INET6<<8 | SAFNUM_UNIMULTICAST):
-                        advance = decode_prefix6(tptr, buf, sizeof(buf));
+                        advance = decode_prefix6(tptr, len, buf, sizeof(buf));
                         if (advance == -1)
                             printf("\n\t    (illegal prefix length)");
                         else if (advance == -2)
                             goto trunc;
+                        else if (advance == -3)
+                            break; /* bytes left, but not enough */
                         else
                             printf("\n\t      %s", buf);
                         break;
                     case (AFNUM_INET6<<8 | SAFNUM_LABUNICAST):
-                        advance = decode_labeled_prefix6(tptr, buf, sizeof(buf));
+                        advance = decode_labeled_prefix6(tptr, len, buf, sizeof(buf));
                         if (advance == -1)
                             printf("\n\t    (illegal prefix length)");
                         else if (advance == -2)
                             goto trunc;
+                        else if (advance == -3)
+                            break; /* bytes left, but not enough */
                         else
                             printf("\n\t      %s", buf);
                         break;
@@ -2098,40 +2134,50 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
         }
         case BGPTYPE_ATTR_SET:
                 TCHECK2(tptr[0], 4);
+                if (len < 4)
+                       goto trunc;
                printf("\n\t    Origin AS: %s",
                    as_printf(astostr, sizeof(astostr), EXTRACT_32BITS(tptr)));
                tptr+=4;
                 len -=4;
 
-                while (len >= 2 ) {
-                    int alen;
-                    struct bgp_attr bgpa;
+                while (len) {
+                    u_int aflags, atype, alenlen, alen;
                     
-                    TCHECK2(tptr[0], sizeof(bgpa));
-                    memcpy(&bgpa, tptr, sizeof(bgpa));
-                    alen = bgp_attr_len(&bgpa);
-                    tptr += bgp_attr_off(&bgpa);
-                    len -= bgp_attr_off(&bgpa);
+                    TCHECK2(tptr[0], 2);
+                    if (len < 2)
+                        goto trunc;
+                    aflags = *tptr;
+                    atype = *(tptr + 1);
+                    tptr += 2;
+                    len -= 2;
+                    alenlen = bgp_attr_lenlen(aflags, tptr);
+                    TCHECK2(tptr[0], alenlen);
+                    if (len < alenlen)
+                        goto trunc;
+                    alen = bgp_attr_len(aflags, tptr);
+                    tptr += alenlen;
+                    len -= alenlen;
                     
                     printf("\n\t      %s (%u), length: %u",
                            tok2strbuf(bgp_attr_values,
-                                     "Unknown Attribute", bgpa.bgpa_type,
-                                     tokbuf, sizeof(tokbuf)),
-                           bgpa.bgpa_type,
+                                      "Unknown Attribute", atype,
+                                      tokbuf, sizeof(tokbuf)),
+                           atype,
                            alen);
                     
-                    if (bgpa.bgpa_flags) {
+                    if (aflags) {
                         printf(", Flags [%s%s%s%s",
-                               bgpa.bgpa_flags & 0x80 ? "O" : "",
-                               bgpa.bgpa_flags & 0x40 ? "T" : "",
-                               bgpa.bgpa_flags & 0x20 ? "P" : "",
-                               bgpa.bgpa_flags & 0x10 ? "E" : "");
-                        if (bgpa.bgpa_flags & 0xf)
-                            printf("+%x", bgpa.bgpa_flags & 0xf);
+                               aflags & 0x80 ? "O" : "",
+                               aflags & 0x40 ? "T" : "",
+                               aflags & 0x20 ? "P" : "",
+                               aflags & 0x10 ? "E" : "");
+                        if (aflags & 0xf)
+                            printf("+%x", aflags & 0xf);
                         printf("]: ");
                     }
                     /* FIXME check for recursion */
-                    if (!bgp_attr_print(&bgpa, tptr, alen))
+                    if (!bgp_attr_print(atype, tptr, alen))
                         return 0;
                     tptr += alen;
                     len -= alen;
@@ -2141,7 +2187,7 @@ bgp_attr_print(const struct bgp_attr *attr, const u_char *pptr, int len)
 
        default:
            TCHECK2(*pptr,len);
-            printf("\n\t    no Attribute %u decoder",attr->bgpa_type); /* we have no decoder for the attribute */
+            printf("\n\t    no Attribute %u decoder",atype); /* we have no decoder for the attribute */
             if (vflag <= 1)
                 print_unknown_data(pptr,"\n\t    ",len);
             break;
@@ -2308,107 +2354,162 @@ static void
 bgp_update_print(const u_char *dat, int length)
 {
        struct bgp bgp;
-       struct bgp_attr bgpa;
        const u_char *p;
+       int withdrawn_routes_len;
        int len;
        int i;
        char tokbuf[TOKBUFSIZE];
 
        TCHECK2(dat[0], BGP_SIZE);
+       if (length < BGP_SIZE)
+               goto trunc;
        memcpy(&bgp, dat, BGP_SIZE);
        p = dat + BGP_SIZE;     /*XXX*/
+       length -= BGP_SIZE;
 
        /* Unfeasible routes */
-       len = EXTRACT_16BITS(p);
-       if (len) {
+       TCHECK2(p[0], 2);
+       if (length < 2)
+               goto trunc;
+       withdrawn_routes_len = EXTRACT_16BITS(p);
+       p += 2;
+       length -= 2;
+       if (withdrawn_routes_len) {
                /*
                 * Without keeping state from the original NLRI message,
                 * it's not possible to tell if this a v4 or v6 route,
                 * so only try to decode it if we're not v6 enabled.
                 */
+               TCHECK2(p[0], withdrawn_routes_len);
+               if (length < withdrawn_routes_len)
+                       goto trunc;
 #ifdef INET6
-               printf("\n\t  Withdrawn routes: %d bytes", len);
+               printf("\n\t  Withdrawn routes: %d bytes", withdrawn_routes_len);
+               p += withdrawn_routes_len;
+               length -= withdrawn_routes_len;
 #else
                char buf[MAXHOSTNAMELEN + 100];
                int wpfx;
 
-               TCHECK2(p[2], len);
-               i = 2;
+               if (withdrawn_routes_len < 2)
+                       goto trunc;
+               length -= 2;
+               withdrawn_routes_len -= 2;
+
 
                printf("\n\t  Withdrawn routes:");
 
-               while(i < 2 + len) {
-                       wpfx = decode_prefix4(&p[i], buf, sizeof(buf));
+               while(withdrawn_routes_len > 0) {
+                       wpfx = decode_prefix4(p, withdrawn_routes_len, buf, sizeof(buf));
                        if (wpfx == -1) {
                                printf("\n\t    (illegal prefix length)");
                                break;
                        } else if (wpfx == -2)
                                goto trunc;
+                       } else if (wpfx == -3)
+                               goto trunc; /* bytes left, but not enough */
                        else {
-                               i += wpfx;
                                printf("\n\t    %s", buf);
+                               p += wpfx;
+                               length -= wpfx;
+                               withdrawn_routes_len -= wpfx;
                        }
                }
 #endif
        }
-       p += 2 + len;
 
        TCHECK2(p[0], 2);
+       if (length < 2)
+               goto trunc;
        len = EXTRACT_16BITS(p);
+       p += 2;
+       length -= 2;
 
-        if (len == 0 && length == BGP_UPDATE_MINSIZE) {
+        if (withdrawn_routes_len == 0 && len == 0 && length == 0) {
+            /* No withdrawn routes, no path attributes, no NLRI */
             printf("\n\t  End-of-Rib Marker (empty NLRI)");
             return;
         }
 
        if (len) {
                /* do something more useful!*/
-               i = 2;
-               while (i < 2 + len) {
-                       int alen, aoff;
-
-                       TCHECK2(p[i], sizeof(bgpa));
-                       memcpy(&bgpa, &p[i], sizeof(bgpa));
-                       alen = bgp_attr_len(&bgpa);
-                       aoff = bgp_attr_off(&bgpa);
-
-                      printf("\n\t  %s (%u), length: %u",
+               while (len) {
+                       int aflags, atype, alenlen, alen;
+
+                       TCHECK2(p[0], 2);
+                       if (len < 2)
+                           goto trunc;
+                       if (length < 2)
+                           goto trunc;
+                       aflags = *p;
+                       atype = *(p + 1);
+                       p += 2;
+                       len -= 2;
+                       length -= 2;
+                       alenlen = bgp_attr_lenlen(aflags, p);
+                       TCHECK2(p[0], alenlen);
+                       if (len < alenlen)
+                           goto trunc;
+                       if (length < alenlen)
+                           goto trunc;
+                       alen = bgp_attr_len(aflags, p);
+                       p += alenlen;
+                       len -= alenlen;
+                       length -= alenlen;
+
+                       printf("\n\t  %s (%u), length: %u",
                               tok2strbuf(bgp_attr_values, "Unknown Attribute",
-                                        bgpa.bgpa_type,
+                                        atype,
                                         tokbuf, sizeof(tokbuf)),
-                              bgpa.bgpa_type,
+                              atype,
                               alen);
 
-                       if (bgpa.bgpa_flags) {
+                       if (aflags) {
                                printf(", Flags [%s%s%s%s",
-                                       bgpa.bgpa_flags & 0x80 ? "O" : "",
-                                       bgpa.bgpa_flags & 0x40 ? "T" : "",
-                                       bgpa.bgpa_flags & 0x20 ? "P" : "",
-                                       bgpa.bgpa_flags & 0x10 ? "E" : "");
-                               if (bgpa.bgpa_flags & 0xf)
-                                       printf("+%x", bgpa.bgpa_flags & 0xf);
+                                       aflags & 0x80 ? "O" : "",
+                                       aflags & 0x40 ? "T" : "",
+                                       aflags & 0x20 ? "P" : "",
+                                       aflags & 0x10 ? "E" : "");
+                               if (aflags & 0xf)
+                                       printf("+%x", aflags & 0xf);
                                printf("]: ");
                        }
-                       if (!bgp_attr_print(&bgpa, &p[i + aoff], alen))
+                       if (len < alen)
+                               goto trunc;
+                       if (length < alen)
                                goto trunc;
-                       i += aoff + alen;
+                       if (!bgp_attr_print(atype, p, alen))
+                               goto trunc;
+                       p += alen;
+                       len -= alen;
+                       length -= alen;
                }
        } 
-       p += 2 + len;
 
-       if (dat + length > p) {
+       if (length) {
+               /*
+                * XXX - what if they're using the "Advertisement of
+                * Multiple Paths in BGP" feature:
+                *
+                * https://datatracker.ietf.org/doc/draft-ietf-idr-add-paths/
+                *
+                * http://tools.ietf.org/html/draft-ietf-idr-add-paths-06
+                */
                printf("\n\t  Updated routes:");
-               while (dat + length > p) {
+               while (length) {
                        char buf[MAXHOSTNAMELEN + 100];
-                       i = decode_prefix4(p, buf, sizeof(buf));
+                       i = decode_prefix4(p, length, buf, sizeof(buf));
                        if (i == -1) {
                                printf("\n\t    (illegal prefix length)");
                                break;
                        } else if (i == -2)
                                goto trunc;
+                       else if (i == -3)
+                               goto trunc; /* bytes left, but not enough */
                        else {
                                printf("\n\t    %s", buf);
                                p += i;
+                               length -= i;
                        }
                }
        }