rsvp_obj_print(netdissect_options *ndo,
               const u_char *pptr, u_int plen, const u_char *tptr,
               const char *ident, u_int tlen,
               const struct rsvp_common_header *rsvp_com_header)
{
    const struct rsvp_object_header *rsvp_obj_header;
    const u_char *obj_tptr;
    union {
        const struct rsvp_obj_integrity_t *rsvp_obj_integrity;
        const struct rsvp_obj_frr_t *rsvp_obj_frr;
    } obj_ptr;

    u_short rsvp_obj_len,rsvp_obj_ctype,obj_tlen,intserv_serv_tlen;
    int hexdump,processed,padbytes,error_code,error_value,i,sigcheck;
    union {
	float f;
	uint32_t i;
    } bw;
    uint8_t namelen;

    u_int action, subchannel;

    while(tlen>=sizeof(struct rsvp_object_header)) {
        /* did we capture enough for fully decoding the object header ? */
        ND_TCHECK2(*tptr, sizeof(struct rsvp_object_header));

        rsvp_obj_header = (const struct rsvp_object_header *)tptr;
        rsvp_obj_len=EXTRACT_16BITS(rsvp_obj_header->length);
        rsvp_obj_ctype=rsvp_obj_header->ctype;

        if(rsvp_obj_len % 4) {
            ND_PRINT((ndo, "%sERROR: object header size %u not a multiple of 4", ident, rsvp_obj_len));
            return -1;
        }
        if(rsvp_obj_len < sizeof(struct rsvp_object_header)) {
            ND_PRINT((ndo, "%sERROR: object header too short %u < %lu", ident, rsvp_obj_len,
                   (unsigned long)sizeof(const struct rsvp_object_header)));
            return -1;
        }

        ND_PRINT((ndo, "%s%s Object (%u) Flags: [%s",
               ident,
               tok2str(rsvp_obj_values,
                       "Unknown",
                       rsvp_obj_header->class_num),
               rsvp_obj_header->class_num,
               ((rsvp_obj_header->class_num) & 0x80) ? "ignore" : "reject"));

        if (rsvp_obj_header->class_num > 128)
            ND_PRINT((ndo, " %s",
                   ((rsvp_obj_header->class_num) & 0x40) ? "and forward" : "silently"));

        ND_PRINT((ndo, " if unknown], Class-Type: %s (%u), length: %u",
               tok2str(rsvp_ctype_values,
                       "Unknown",
                       ((rsvp_obj_header->class_num)<<8)+rsvp_obj_ctype),
               rsvp_obj_ctype,
               rsvp_obj_len));

        if(tlen < rsvp_obj_len) {
            ND_PRINT((ndo, "%sERROR: object goes past end of objects TLV", ident));
            return -1;
        }

        obj_tptr=tptr+sizeof(struct rsvp_object_header);
        obj_tlen=rsvp_obj_len-sizeof(struct rsvp_object_header);

        /* did we capture enough for fully decoding the object ? */
        if (!ND_TTEST2(*tptr, rsvp_obj_len))
            return -1;
        hexdump=FALSE;

        switch(rsvp_obj_header->class_num) {
        case RSVP_OBJ_SESSION:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_IPV4:
                if (obj_tlen < 8)
                    return -1;
                ND_PRINT((ndo, "%s  IPv4 DestAddress: %s, Protocol ID: 0x%02x",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       *(obj_tptr + sizeof(struct in_addr))));
                ND_PRINT((ndo, "%s  Flags: [0x%02x], DestPort %u",
                       ident,
                       *(obj_tptr+5),
                       EXTRACT_16BITS(obj_tptr + 6)));
                obj_tlen-=8;
                obj_tptr+=8;
                break;
            case RSVP_CTYPE_IPV6:
                if (obj_tlen < 20)
                    return -1;
                ND_PRINT((ndo, "%s  IPv6 DestAddress: %s, Protocol ID: 0x%02x",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       *(obj_tptr + sizeof(struct in6_addr))));
                ND_PRINT((ndo, "%s  Flags: [0x%02x], DestPort %u",
                       ident,
                       *(obj_tptr+sizeof(struct in6_addr)+1),
                       EXTRACT_16BITS(obj_tptr + sizeof(struct in6_addr) + 2)));
                obj_tlen-=20;
                obj_tptr+=20;
                break;

            case RSVP_CTYPE_TUNNEL_IPV6:
                if (obj_tlen < 36)
                    return -1;
                ND_PRINT((ndo, "%s  IPv6 Tunnel EndPoint: %s, Tunnel ID: 0x%04x, Extended Tunnel ID: %s",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr+18),
                       ip6addr_string(ndo, obj_tptr + 20)));
                obj_tlen-=36;
                obj_tptr+=36;
                break;

            case RSVP_CTYPE_14: /* IPv6 p2mp LSP Tunnel */
                if (obj_tlen < 26)
                    return -1;
                ND_PRINT((ndo, "%s  IPv6 P2MP LSP ID: 0x%08x, Tunnel ID: 0x%04x, Extended Tunnel ID: %s",
                       ident,
                       EXTRACT_32BITS(obj_tptr),
                       EXTRACT_16BITS(obj_tptr+6),
                       ip6addr_string(ndo, obj_tptr + 8)));
                obj_tlen-=26;
                obj_tptr+=26;
                break;
            case RSVP_CTYPE_13: /* IPv4 p2mp LSP Tunnel */
                if (obj_tlen < 12)
                    return -1;
                ND_PRINT((ndo, "%s  IPv4 P2MP LSP ID: %s, Tunnel ID: 0x%04x, Extended Tunnel ID: %s",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr+6),
                       ipaddr_string(ndo, obj_tptr + 8)));
                obj_tlen-=12;
                obj_tptr+=12;
                break;
            case RSVP_CTYPE_TUNNEL_IPV4:
            case RSVP_CTYPE_UNI_IPV4:
                if (obj_tlen < 12)
                    return -1;
                ND_PRINT((ndo, "%s  IPv4 Tunnel EndPoint: %s, Tunnel ID: 0x%04x, Extended Tunnel ID: %s",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr+6),
                       ipaddr_string(ndo, obj_tptr + 8)));
                obj_tlen-=12;
                obj_tptr+=12;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_CONFIRM:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_IPV4:
                if (obj_tlen < sizeof(struct in_addr))
                    return -1;
                ND_PRINT((ndo, "%s  IPv4 Receiver Address: %s",
                       ident,
                       ipaddr_string(ndo, obj_tptr)));
                obj_tlen-=sizeof(struct in_addr);
                obj_tptr+=sizeof(struct in_addr);
                break;
            case RSVP_CTYPE_IPV6:
                if (obj_tlen < sizeof(struct in6_addr))
                    return -1;
                ND_PRINT((ndo, "%s  IPv6 Receiver Address: %s",
                       ident,
                       ip6addr_string(ndo, obj_tptr)));
                obj_tlen-=sizeof(struct in6_addr);
                obj_tptr+=sizeof(struct in6_addr);
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_NOTIFY_REQ:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_IPV4:
                if (obj_tlen < sizeof(struct in_addr))
                    return -1;
                ND_PRINT((ndo, "%s  IPv4 Notify Node Address: %s",
                       ident,
                       ipaddr_string(ndo, obj_tptr)));
                obj_tlen-=sizeof(struct in_addr);
                obj_tptr+=sizeof(struct in_addr);
                break;
            case RSVP_CTYPE_IPV6:
                if (obj_tlen < sizeof(struct in6_addr))
                    return-1;
                ND_PRINT((ndo, "%s  IPv6 Notify Node Address: %s",
                       ident,
                       ip6addr_string(ndo, obj_tptr)));
                obj_tlen-=sizeof(struct in6_addr);
                obj_tptr+=sizeof(struct in6_addr);
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_SUGGESTED_LABEL: /* fall through */
        case RSVP_OBJ_UPSTREAM_LABEL:  /* fall through */
        case RSVP_OBJ_RECOVERY_LABEL:  /* fall through */
        case RSVP_OBJ_LABEL:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                while(obj_tlen >= 4 ) {
                    ND_PRINT((ndo, "%s  Label: %u", ident, EXTRACT_32BITS(obj_tptr)));
                    obj_tlen-=4;
                    obj_tptr+=4;
                }
                break;
            case RSVP_CTYPE_2:
                if (obj_tlen < 4)
                    return-1;
                ND_PRINT((ndo, "%s  Generalized Label: %u",
                       ident,
                       EXTRACT_32BITS(obj_tptr)));
                obj_tlen-=4;
                obj_tptr+=4;
                break;
            case RSVP_CTYPE_3:
                if (obj_tlen < 12)
                    return-1;
                ND_PRINT((ndo, "%s  Waveband ID: %u%s  Start Label: %u, Stop Label: %u",
                       ident,
                       EXTRACT_32BITS(obj_tptr),
                       ident,
                       EXTRACT_32BITS(obj_tptr+4),
                       EXTRACT_32BITS(obj_tptr + 8)));
                obj_tlen-=12;
                obj_tptr+=12;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_STYLE:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                if (obj_tlen < 4)
                    return-1;
                ND_PRINT((ndo, "%s  Reservation Style: %s, Flags: [0x%02x]",
                       ident,
                       tok2str(rsvp_resstyle_values,
                               "Unknown",
                               EXTRACT_24BITS(obj_tptr+1)),
                       *(obj_tptr)));
                obj_tlen-=4;
                obj_tptr+=4;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_SENDER_TEMPLATE:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_IPV4:
                if (obj_tlen < 8)
                    return-1;
                ND_PRINT((ndo, "%s  Source Address: %s, Source Port: %u",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr + 6)));
                obj_tlen-=8;
                obj_tptr+=8;
                break;
            case RSVP_CTYPE_IPV6:
                if (obj_tlen < 20)
                    return-1;
                ND_PRINT((ndo, "%s  Source Address: %s, Source Port: %u",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr + 18)));
                obj_tlen-=20;
                obj_tptr+=20;
                break;
            case RSVP_CTYPE_13: /* IPv6 p2mp LSP tunnel */
                if (obj_tlen < 40)
                    return-1;
                ND_PRINT((ndo, "%s  IPv6 Tunnel Sender Address: %s, LSP ID: 0x%04x"
                       "%s  Sub-Group Originator ID: %s, Sub-Group ID: 0x%04x",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr+18),
                       ident,
                       ip6addr_string(ndo, obj_tptr+20),
                       EXTRACT_16BITS(obj_tptr + 38)));
                obj_tlen-=40;
                obj_tptr+=40;
                break;
            case RSVP_CTYPE_TUNNEL_IPV4:
                if (obj_tlen < 8)
                    return-1;
                ND_PRINT((ndo, "%s  IPv4 Tunnel Sender Address: %s, LSP-ID: 0x%04x",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr + 6)));
                obj_tlen-=8;
                obj_tptr+=8;
                break;
            case RSVP_CTYPE_12: /* IPv4 p2mp LSP tunnel */
                if (obj_tlen < 16)
                    return-1;
                ND_PRINT((ndo, "%s  IPv4 Tunnel Sender Address: %s, LSP ID: 0x%04x"
                       "%s  Sub-Group Originator ID: %s, Sub-Group ID: 0x%04x",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr+6),
                       ident,
                       ipaddr_string(ndo, obj_tptr+8),
                       EXTRACT_16BITS(obj_tptr + 12)));
                obj_tlen-=16;
                obj_tptr+=16;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_LABEL_REQ:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                while(obj_tlen >= 4 ) {
                    ND_PRINT((ndo, "%s  L3 Protocol ID: %s",
                           ident,
                           tok2str(ethertype_values,
                                   "Unknown Protocol (0x%04x)",
                                   EXTRACT_16BITS(obj_tptr + 2))));
                    obj_tlen-=4;
                    obj_tptr+=4;
                }
                break;
            case RSVP_CTYPE_2:
                if (obj_tlen < 12)
                    return-1;
                ND_PRINT((ndo, "%s  L3 Protocol ID: %s",
                       ident,
                       tok2str(ethertype_values,
                               "Unknown Protocol (0x%04x)",
                               EXTRACT_16BITS(obj_tptr + 2))));
                ND_PRINT((ndo, ",%s merge capability",((*(obj_tptr + 4)) & 0x80) ? "no" : "" ));
                ND_PRINT((ndo, "%s  Minimum VPI/VCI: %u/%u",
                       ident,
                       (EXTRACT_16BITS(obj_tptr+4))&0xfff,
                       (EXTRACT_16BITS(obj_tptr + 6)) & 0xfff));
                ND_PRINT((ndo, "%s  Maximum VPI/VCI: %u/%u",
                       ident,
                       (EXTRACT_16BITS(obj_tptr+8))&0xfff,
                       (EXTRACT_16BITS(obj_tptr + 10)) & 0xfff));
                obj_tlen-=12;
                obj_tptr+=12;
                break;
            case RSVP_CTYPE_3:
                if (obj_tlen < 12)
                    return-1;
                ND_PRINT((ndo, "%s  L3 Protocol ID: %s",
                       ident,
                       tok2str(ethertype_values,
                               "Unknown Protocol (0x%04x)",
                               EXTRACT_16BITS(obj_tptr + 2))));
                ND_PRINT((ndo, "%s  Minimum/Maximum DLCI: %u/%u, %s%s bit DLCI",
                       ident,
                       (EXTRACT_32BITS(obj_tptr+4))&0x7fffff,
                       (EXTRACT_32BITS(obj_tptr+8))&0x7fffff,
                       (((EXTRACT_16BITS(obj_tptr+4)>>7)&3) == 0 ) ? "10" : "",
                       (((EXTRACT_16BITS(obj_tptr + 4) >> 7) & 3) == 2 ) ? "23" : ""));
                obj_tlen-=12;
                obj_tptr+=12;
                break;
            case RSVP_CTYPE_4:
                if (obj_tlen < 4)
                    return-1;
                ND_PRINT((ndo, "%s  LSP Encoding Type: %s (%u)",
                       ident,
                       tok2str(gmpls_encoding_values,
                               "Unknown",
                               *obj_tptr),
		       *obj_tptr));
                ND_PRINT((ndo, "%s  Switching Type: %s (%u), Payload ID: %s (0x%04x)",
                       ident,
                       tok2str(gmpls_switch_cap_values,
                               "Unknown",
                               *(obj_tptr+1)),
		       *(obj_tptr+1),
                       tok2str(gmpls_payload_values,
                               "Unknown",
                               EXTRACT_16BITS(obj_tptr+2)),
		       EXTRACT_16BITS(obj_tptr + 2)));
                obj_tlen-=4;
                obj_tptr+=4;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_RRO:
        case RSVP_OBJ_ERO:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_IPV4:
                while(obj_tlen >= 4 ) {
		    u_char length;

		    ND_TCHECK2(*obj_tptr, 4);
		    length = *(obj_tptr + 1);
                    ND_PRINT((ndo, "%s  Subobject Type: %s, length %u",
                           ident,
                           tok2str(rsvp_obj_xro_values,
                                   "Unknown %u",
                                   RSVP_OBJ_XRO_MASK_SUBOBJ(*obj_tptr)),
                           length));

                    if (length == 0) { /* prevent infinite loops */
                        ND_PRINT((ndo, "%s  ERROR: zero length ERO subtype", ident));
                        break;
                    }

                    switch(RSVP_OBJ_XRO_MASK_SUBOBJ(*obj_tptr)) {
		    u_char prefix_length;

                    case RSVP_OBJ_XRO_IPV4:
			if (length != 8) {
				ND_PRINT((ndo, " ERROR: length != 8"));
				goto invalid;
			}
			ND_TCHECK2(*obj_tptr, 8);
			prefix_length = *(obj_tptr+6);
			if (prefix_length != 32) {
				ND_PRINT((ndo, " ERROR: Prefix length %u != 32",
					  prefix_length));
				goto invalid;
			}
                        ND_PRINT((ndo, ", %s, %s/%u, Flags: [%s]",
                               RSVP_OBJ_XRO_MASK_LOOSE(*obj_tptr) ? "Loose" : "Strict",
                               ipaddr_string(ndo, obj_tptr+2),
                               *(obj_tptr+6),
                               bittok2str(rsvp_obj_rro_flag_values,
                                   "none",
                                   *(obj_tptr + 7)))); /* rfc3209 says that this field is rsvd. */
                    break;
                    case RSVP_OBJ_XRO_LABEL:
			if (length != 8) {
				ND_PRINT((ndo, " ERROR: length != 8"));
				goto invalid;
			}
			ND_TCHECK2(*obj_tptr, 8);
                        ND_PRINT((ndo, ", Flags: [%s] (%#x), Class-Type: %s (%u), %u",
                               bittok2str(rsvp_obj_rro_label_flag_values,
                                   "none",
                                   *(obj_tptr+2)),
                               *(obj_tptr+2),
                               tok2str(rsvp_ctype_values,
                                       "Unknown",
                                       *(obj_tptr+3) + 256*RSVP_OBJ_RRO),
                               *(obj_tptr+3),
                               EXTRACT_32BITS(obj_tptr + 4)));
                    }
                    obj_tlen-=*(obj_tptr+1);
                    obj_tptr+=*(obj_tptr+1);
                }
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_HELLO:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
            case RSVP_CTYPE_2:
                if (obj_tlen < 8)
                    return-1;
                ND_PRINT((ndo, "%s  Source Instance: 0x%08x, Destination Instance: 0x%08x",
                       ident,
                       EXTRACT_32BITS(obj_tptr),
                       EXTRACT_32BITS(obj_tptr + 4)));
                obj_tlen-=8;
                obj_tptr+=8;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_RESTART_CAPABILITY:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                if (obj_tlen < 8)
                    return-1;
                ND_PRINT((ndo, "%s  Restart  Time: %ums, Recovery Time: %ums",
                       ident,
                       EXTRACT_32BITS(obj_tptr),
                       EXTRACT_32BITS(obj_tptr + 4)));
                obj_tlen-=8;
                obj_tptr+=8;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_SESSION_ATTRIBUTE:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_TUNNEL_IPV4:
                if (obj_tlen < 4)
                    return-1;
                namelen = *(obj_tptr+3);
                if (obj_tlen < 4+namelen)
                    return-1;
                ND_PRINT((ndo, "%s  Session Name: ", ident));
                for (i = 0; i < namelen; i++)
                    safeputchar(ndo, *(obj_tptr + 4 + i));
                ND_PRINT((ndo, "%s  Setup Priority: %u, Holding Priority: %u, Flags: [%s] (%#x)",
                       ident,
                       (int)*obj_tptr,
                       (int)*(obj_tptr+1),
                       bittok2str(rsvp_session_attribute_flag_values,
                                  "none",
                                  *(obj_tptr+2)),
                       *(obj_tptr + 2)));
                obj_tlen-=4+*(obj_tptr+3);
                obj_tptr+=4+*(obj_tptr+3);
                break;
            default:
                hexdump=TRUE;
            }
            break;

	case RSVP_OBJ_GENERALIZED_UNI:
            switch(rsvp_obj_ctype) {
		int subobj_type,af,subobj_len,total_subobj_len;

            case RSVP_CTYPE_1:

                if (obj_tlen < 4)
                    return-1;

 		/* read variable length subobjects */
 		total_subobj_len = obj_tlen;
                 while(total_subobj_len > 0) {
                    /* If RFC 3476 Section 3.1 defined that a sub-object of the
                     * GENERALIZED_UNI RSVP object must have the Length field as
                     * a multiple of 4, instead of the check below it would be
                     * better to test total_subobj_len only once before the loop.
                     * So long as it does not define it and this while loop does
                     * not implement such a requirement, let's accept that within
                     * each iteration subobj_len may happen to be a multiple of 1
                     * and test it and total_subobj_len respectively.
                     */
                    if (total_subobj_len < 4)
                        goto invalid;
                     subobj_len  = EXTRACT_16BITS(obj_tptr);
                     subobj_type = (EXTRACT_16BITS(obj_tptr+2))>>8;
                     af = (EXTRACT_16BITS(obj_tptr+2))&0x00FF;

                    ND_PRINT((ndo, "%s  Subobject Type: %s (%u), AF: %s (%u), length: %u",
                           ident,
                           tok2str(rsvp_obj_generalized_uni_values, "Unknown", subobj_type),
                           subobj_type,
                            tok2str(af_values, "Unknown", af), af,
                            subobj_len));

                    /* In addition to what is explained above, the same spec does not
                     * explicitly say that the same Length field includes the 4-octet
                     * sub-object header, but as long as this while loop implements it
                     * as it does include, let's keep the check below consistent with
                     * the rest of the code.
                     */
                    if(subobj_len < 4 || subobj_len > total_subobj_len)
                         goto invalid;

                     switch(subobj_type) {
                    case RSVP_GEN_UNI_SUBOBJ_SOURCE_TNA_ADDRESS:
                    case RSVP_GEN_UNI_SUBOBJ_DESTINATION_TNA_ADDRESS:

                        switch(af) {
                        case AFNUM_INET:
                            if (subobj_len < 8)
                                return -1;
                            ND_PRINT((ndo, "%s    UNI IPv4 TNA address: %s",
                                   ident, ipaddr_string(ndo, obj_tptr + 4)));
                            break;
                        case AFNUM_INET6:
                            if (subobj_len < 20)
                                return -1;
                            ND_PRINT((ndo, "%s    UNI IPv6 TNA address: %s",
                                   ident, ip6addr_string(ndo, obj_tptr + 4)));
                            break;
                        case AFNUM_NSAP:
                            if (subobj_len) {
                                /* unless we have a TLV parser lets just hexdump */
                                hexdump=TRUE;
                            }
                            break;
                        }
                        break;

                    case RSVP_GEN_UNI_SUBOBJ_DIVERSITY:
                        if (subobj_len) {
                            /* unless we have a TLV parser lets just hexdump */
                            hexdump=TRUE;
                        }
                        break;

                    case RSVP_GEN_UNI_SUBOBJ_EGRESS_LABEL:
                        if (subobj_len < 16) {
                            return -1;
                        }

                        ND_PRINT((ndo, "%s    U-bit: %x, Label type: %u, Logical port id: %u, Label: %u",
                               ident,
                               ((EXTRACT_32BITS(obj_tptr+4))>>31),
                               ((EXTRACT_32BITS(obj_tptr+4))&0xFF),
                               EXTRACT_32BITS(obj_tptr+8),
                               EXTRACT_32BITS(obj_tptr + 12)));
                        break;

                    case RSVP_GEN_UNI_SUBOBJ_SERVICE_LEVEL:
                        if (subobj_len < 8) {
                            return -1;
                        }

                        ND_PRINT((ndo, "%s    Service level: %u",
                               ident, (EXTRACT_32BITS(obj_tptr + 4)) >> 24));
                        break;

                    default:
                        hexdump=TRUE;
                        break;
                    }
                    total_subobj_len-=subobj_len;
                    obj_tptr+=subobj_len;
                    obj_tlen+=subobj_len;
		}

                if (total_subobj_len) {
                    /* unless we have a TLV parser lets just hexdump */
                    hexdump=TRUE;
                }
                break;

            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_RSVP_HOP:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_3: /* fall through - FIXME add TLV parser */
            case RSVP_CTYPE_IPV4:
                if (obj_tlen < 8)
                    return-1;
                ND_PRINT((ndo, "%s  Previous/Next Interface: %s, Logical Interface Handle: 0x%08x",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_32BITS(obj_tptr + 4)));
                obj_tlen-=8;
                obj_tptr+=8;
                if (obj_tlen)
                    hexdump=TRUE; /* unless we have a TLV parser lets just hexdump */
                break;
            case RSVP_CTYPE_4: /* fall through - FIXME add TLV parser */
            case RSVP_CTYPE_IPV6:
                if (obj_tlen < 20)
                    return-1;
                ND_PRINT((ndo, "%s  Previous/Next Interface: %s, Logical Interface Handle: 0x%08x",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       EXTRACT_32BITS(obj_tptr + 16)));
                obj_tlen-=20;
                obj_tptr+=20;
                hexdump=TRUE; /* unless we have a TLV parser lets just hexdump */
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_TIME_VALUES:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                if (obj_tlen < 4)
                    return-1;
                ND_PRINT((ndo, "%s  Refresh Period: %ums",
                       ident,
                       EXTRACT_32BITS(obj_tptr)));
                obj_tlen-=4;
                obj_tptr+=4;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        /* those three objects do share the same semantics */
        case RSVP_OBJ_SENDER_TSPEC:
        case RSVP_OBJ_ADSPEC:
        case RSVP_OBJ_FLOWSPEC:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_2:
                if (obj_tlen < 4)
                    return-1;
                ND_PRINT((ndo, "%s  Msg-Version: %u, length: %u",
                       ident,
                       (*obj_tptr & 0xf0) >> 4,
                       EXTRACT_16BITS(obj_tptr + 2) << 2));
                obj_tptr+=4; /* get to the start of the service header */
                obj_tlen-=4;

                while (obj_tlen >= 4) {
                    intserv_serv_tlen=EXTRACT_16BITS(obj_tptr+2)<<2;
                    ND_PRINT((ndo, "%s  Service Type: %s (%u), break bit %s set, Service length: %u",
                           ident,
                           tok2str(rsvp_intserv_service_type_values,"unknown",*(obj_tptr)),
                           *(obj_tptr),
                           (*(obj_tptr+1)&0x80) ? "" : "not",
                           intserv_serv_tlen));

                    obj_tptr+=4; /* get to the start of the parameter list */
                    obj_tlen-=4;

                    while (intserv_serv_tlen>=4) {
                        processed = rsvp_intserv_print(ndo, obj_tptr, obj_tlen);
                        if (processed == 0)
                            break;
                        obj_tlen-=processed;
                        intserv_serv_tlen-=processed;
                        obj_tptr+=processed;
                    }
                }
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_FILTERSPEC:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_IPV4:
                if (obj_tlen < 8)
                    return-1;
                ND_PRINT((ndo, "%s  Source Address: %s, Source Port: %u",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr + 6)));
                obj_tlen-=8;
                obj_tptr+=8;
                break;
            case RSVP_CTYPE_IPV6:
                if (obj_tlen < 20)
                    return-1;
                ND_PRINT((ndo, "%s  Source Address: %s, Source Port: %u",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr + 18)));
                obj_tlen-=20;
                obj_tptr+=20;
                break;
            case RSVP_CTYPE_3:
                if (obj_tlen < 20)
                    return-1;
                ND_PRINT((ndo, "%s  Source Address: %s, Flow Label: %u",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       EXTRACT_24BITS(obj_tptr + 17)));
                obj_tlen-=20;
                obj_tptr+=20;
                break;
            case RSVP_CTYPE_TUNNEL_IPV6:
                if (obj_tlen < 20)
                    return-1;
                ND_PRINT((ndo, "%s  Source Address: %s, LSP-ID: 0x%04x",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr + 18)));
                obj_tlen-=20;
                obj_tptr+=20;
                break;
            case RSVP_CTYPE_13: /* IPv6 p2mp LSP tunnel */
                if (obj_tlen < 40)
                    return-1;
                ND_PRINT((ndo, "%s  IPv6 Tunnel Sender Address: %s, LSP ID: 0x%04x"
                       "%s  Sub-Group Originator ID: %s, Sub-Group ID: 0x%04x",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr+18),
                       ident,
                       ip6addr_string(ndo, obj_tptr+20),
                       EXTRACT_16BITS(obj_tptr + 38)));
                obj_tlen-=40;
                obj_tptr+=40;
                break;
            case RSVP_CTYPE_TUNNEL_IPV4:
                if (obj_tlen < 8)
                    return-1;
                ND_PRINT((ndo, "%s  Source Address: %s, LSP-ID: 0x%04x",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr + 6)));
                obj_tlen-=8;
                obj_tptr+=8;
                break;
            case RSVP_CTYPE_12: /* IPv4 p2mp LSP tunnel */
                if (obj_tlen < 16)
                    return-1;
                ND_PRINT((ndo, "%s  IPv4 Tunnel Sender Address: %s, LSP ID: 0x%04x"
                       "%s  Sub-Group Originator ID: %s, Sub-Group ID: 0x%04x",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       EXTRACT_16BITS(obj_tptr+6),
                       ident,
                       ipaddr_string(ndo, obj_tptr+8),
                       EXTRACT_16BITS(obj_tptr + 12)));
                obj_tlen-=16;
                obj_tptr+=16;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_FASTREROUTE:
            /* the differences between c-type 1 and 7 are minor */
            obj_ptr.rsvp_obj_frr = (const struct rsvp_obj_frr_t *)obj_tptr;

            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1: /* new style */
                if (obj_tlen < sizeof(struct rsvp_obj_frr_t))
                    return-1;
                bw.i = EXTRACT_32BITS(obj_ptr.rsvp_obj_frr->bandwidth);
                ND_PRINT((ndo, "%s  Setup Priority: %u, Holding Priority: %u, Hop-limit: %u, Bandwidth: %.10g Mbps",
                       ident,
                       (int)obj_ptr.rsvp_obj_frr->setup_prio,
                       (int)obj_ptr.rsvp_obj_frr->hold_prio,
                       (int)obj_ptr.rsvp_obj_frr->hop_limit,
                        bw.f * 8 / 1000000));
                ND_PRINT((ndo, "%s  Include-any: 0x%08x, Exclude-any: 0x%08x, Include-all: 0x%08x",
                       ident,
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_frr->include_any),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_frr->exclude_any),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_frr->include_all)));
                obj_tlen-=sizeof(struct rsvp_obj_frr_t);
                obj_tptr+=sizeof(struct rsvp_obj_frr_t);
                break;

            case RSVP_CTYPE_TUNNEL_IPV4: /* old style */
                if (obj_tlen < 16)
                    return-1;
                bw.i = EXTRACT_32BITS(obj_ptr.rsvp_obj_frr->bandwidth);
                ND_PRINT((ndo, "%s  Setup Priority: %u, Holding Priority: %u, Hop-limit: %u, Bandwidth: %.10g Mbps",
                       ident,
                       (int)obj_ptr.rsvp_obj_frr->setup_prio,
                       (int)obj_ptr.rsvp_obj_frr->hold_prio,
                       (int)obj_ptr.rsvp_obj_frr->hop_limit,
                        bw.f * 8 / 1000000));
                ND_PRINT((ndo, "%s  Include Colors: 0x%08x, Exclude Colors: 0x%08x",
                       ident,
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_frr->include_any),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_frr->exclude_any)));
                obj_tlen-=16;
                obj_tptr+=16;
                break;

            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_DETOUR:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_TUNNEL_IPV4:
                while(obj_tlen >= 8) {
                    ND_PRINT((ndo, "%s  PLR-ID: %s, Avoid-Node-ID: %s",
                           ident,
                           ipaddr_string(ndo, obj_tptr),
                           ipaddr_string(ndo, obj_tptr + 4)));
                    obj_tlen-=8;
                    obj_tptr+=8;
                }
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_CLASSTYPE:
        case RSVP_OBJ_CLASSTYPE_OLD: /* fall through */
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                ND_PRINT((ndo, "%s  CT: %u",
                       ident,
                       EXTRACT_32BITS(obj_tptr) & 0x7));
                obj_tlen-=4;
                obj_tptr+=4;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_ERROR_SPEC:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_3: /* fall through - FIXME add TLV parser */
            case RSVP_CTYPE_IPV4:
                if (obj_tlen < 8)
                    return-1;
                error_code=*(obj_tptr+5);
                error_value=EXTRACT_16BITS(obj_tptr+6);
                ND_PRINT((ndo, "%s  Error Node Address: %s, Flags: [0x%02x]%s  Error Code: %s (%u)",
                       ident,
                       ipaddr_string(ndo, obj_tptr),
                       *(obj_tptr+4),
                       ident,
                       tok2str(rsvp_obj_error_code_values,"unknown",error_code),
                       error_code));
                switch (error_code) {
                case RSVP_OBJ_ERROR_SPEC_CODE_ROUTING:
                    ND_PRINT((ndo, ", Error Value: %s (%u)",
                           tok2str(rsvp_obj_error_code_routing_values,"unknown",error_value),
                           error_value));
                    break;
                case RSVP_OBJ_ERROR_SPEC_CODE_DIFFSERV_TE: /* fall through */
                case RSVP_OBJ_ERROR_SPEC_CODE_DIFFSERV_TE_OLD:
                    ND_PRINT((ndo, ", Error Value: %s (%u)",
                           tok2str(rsvp_obj_error_code_diffserv_te_values,"unknown",error_value),
                           error_value));
                    break;
                default:
                    ND_PRINT((ndo, ", Unknown Error Value (%u)", error_value));
                    break;
                }
                obj_tlen-=8;
                obj_tptr+=8;
                break;
            case RSVP_CTYPE_4: /* fall through - FIXME add TLV parser */
            case RSVP_CTYPE_IPV6:
                if (obj_tlen < 20)
                    return-1;
                error_code=*(obj_tptr+17);
                error_value=EXTRACT_16BITS(obj_tptr+18);
                ND_PRINT((ndo, "%s  Error Node Address: %s, Flags: [0x%02x]%s  Error Code: %s (%u)",
                       ident,
                       ip6addr_string(ndo, obj_tptr),
                       *(obj_tptr+16),
                       ident,
                       tok2str(rsvp_obj_error_code_values,"unknown",error_code),
                       error_code));

                switch (error_code) {
                case RSVP_OBJ_ERROR_SPEC_CODE_ROUTING:
                    ND_PRINT((ndo, ", Error Value: %s (%u)",
                           tok2str(rsvp_obj_error_code_routing_values,"unknown",error_value),
			   error_value));
                    break;
                default:
                    break;
                }
                obj_tlen-=20;
                obj_tptr+=20;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_PROPERTIES:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                if (obj_tlen < 4)
                    return-1;
                padbytes = EXTRACT_16BITS(obj_tptr+2);
                ND_PRINT((ndo, "%s  TLV count: %u, padding bytes: %u",
                       ident,
                       EXTRACT_16BITS(obj_tptr),
                       padbytes));
                obj_tlen-=4;
                obj_tptr+=4;
                /* loop through as long there is anything longer than the TLV header (2) */
                while(obj_tlen >= 2 + padbytes) {
                    ND_PRINT((ndo, "%s    %s TLV (0x%02x), length: %u", /* length includes header */
                           ident,
                           tok2str(rsvp_obj_prop_tlv_values,"unknown",*obj_tptr),
                           *obj_tptr,
                           *(obj_tptr + 1)));
                    if (obj_tlen < *(obj_tptr+1))
                        return-1;
                    if (*(obj_tptr+1) < 2)
                        return -1;
                    print_unknown_data(ndo, obj_tptr + 2, "\n\t\t", *(obj_tptr + 1) - 2);
                    obj_tlen-=*(obj_tptr+1);
                    obj_tptr+=*(obj_tptr+1);
                }
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_MESSAGE_ID:     /* fall through */
        case RSVP_OBJ_MESSAGE_ID_ACK: /* fall through */
        case RSVP_OBJ_MESSAGE_ID_LIST:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
            case RSVP_CTYPE_2:
                if (obj_tlen < 8)
                    return-1;
                ND_PRINT((ndo, "%s  Flags [0x%02x], epoch: %u",
                       ident,
                       *obj_tptr,
                       EXTRACT_24BITS(obj_tptr + 1)));
                obj_tlen-=4;
                obj_tptr+=4;
                /* loop through as long there are no messages left */
                while(obj_tlen >= 4) {
                    ND_PRINT((ndo, "%s    Message-ID 0x%08x (%u)",
                           ident,
                           EXTRACT_32BITS(obj_tptr),
                           EXTRACT_32BITS(obj_tptr)));
                    obj_tlen-=4;
                    obj_tptr+=4;
                }
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_INTEGRITY:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                if (obj_tlen < sizeof(struct rsvp_obj_integrity_t))
                    return-1;
                obj_ptr.rsvp_obj_integrity = (const struct rsvp_obj_integrity_t *)obj_tptr;
                ND_PRINT((ndo, "%s  Key-ID 0x%04x%08x, Sequence 0x%08x%08x, Flags [%s]",
                       ident,
                       EXTRACT_16BITS(obj_ptr.rsvp_obj_integrity->key_id),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_integrity->key_id+2),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_integrity->sequence),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_integrity->sequence+4),
                       bittok2str(rsvp_obj_integrity_flag_values,
                                  "none",
                                  obj_ptr.rsvp_obj_integrity->flags)));
                ND_PRINT((ndo, "%s  MD5-sum 0x%08x%08x%08x%08x ",
                       ident,
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_integrity->digest),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_integrity->digest+4),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_integrity->digest+8),
                       EXTRACT_32BITS(obj_ptr.rsvp_obj_integrity->digest + 12)));

                sigcheck = signature_verify(ndo, pptr, plen,
                                            obj_ptr.rsvp_obj_integrity->digest,
                                            rsvp_clear_checksum,
                                            rsvp_com_header);
                ND_PRINT((ndo, " (%s)", tok2str(signature_check_values, "Unknown", sigcheck)));

                obj_tlen+=sizeof(struct rsvp_obj_integrity_t);
                obj_tptr+=sizeof(struct rsvp_obj_integrity_t);
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_ADMIN_STATUS:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                if (obj_tlen < 4)
                    return-1;
                ND_PRINT((ndo, "%s  Flags [%s]", ident,
                       bittok2str(rsvp_obj_admin_status_flag_values, "none",
                                  EXTRACT_32BITS(obj_tptr))));
                obj_tlen-=4;
                obj_tptr+=4;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_LABEL_SET:
            switch(rsvp_obj_ctype) {
            case RSVP_CTYPE_1:
                if (obj_tlen < 4)
                    return-1;
                action = (EXTRACT_16BITS(obj_tptr)>>8);

                ND_PRINT((ndo, "%s  Action: %s (%u), Label type: %u", ident,
                       tok2str(rsvp_obj_label_set_action_values, "Unknown", action),
                       action, ((EXTRACT_32BITS(obj_tptr) & 0x7F))));

                switch (action) {
                case LABEL_SET_INCLUSIVE_RANGE:
                case LABEL_SET_EXCLUSIVE_RANGE: /* fall through */

		    /* only a couple of subchannels are expected */
		    if (obj_tlen < 12)
			return -1;
		    ND_PRINT((ndo, "%s  Start range: %u, End range: %u", ident,
                           EXTRACT_32BITS(obj_tptr+4),
                           EXTRACT_32BITS(obj_tptr + 8)));
		    obj_tlen-=12;
		    obj_tptr+=12;
                    break;

                default:
                    obj_tlen-=4;
                    obj_tptr+=4;
                    subchannel = 1;
                    while(obj_tlen >= 4 ) {
                        ND_PRINT((ndo, "%s  Subchannel #%u: %u", ident, subchannel,
                               EXTRACT_32BITS(obj_tptr)));
                        obj_tptr+=4;
                        obj_tlen-=4;
                        subchannel++;
                    }
                    break;
                }
                break;
            default:
                hexdump=TRUE;
            }
            break;

        case RSVP_OBJ_S2L:
            switch (rsvp_obj_ctype) {
            case RSVP_CTYPE_IPV4:
                if (obj_tlen < 4)
                    return-1;
                ND_PRINT((ndo, "%s  Sub-LSP destination address: %s",
                       ident, ipaddr_string(ndo, obj_tptr)));

                obj_tlen-=4;
                obj_tptr+=4;
                break;
            case RSVP_CTYPE_IPV6:
                if (obj_tlen < 16)
                    return-1;
                ND_PRINT((ndo, "%s  Sub-LSP destination address: %s",
                       ident, ip6addr_string(ndo, obj_tptr)));

                obj_tlen-=16;
                obj_tptr+=16;
                break;
            default:
                hexdump=TRUE;
            }
            break;

        /*
         *  FIXME those are the defined objects that lack a decoder
         *  you are welcome to contribute code ;-)
         */

        case RSVP_OBJ_SCOPE:
        case RSVP_OBJ_POLICY_DATA:
        case RSVP_OBJ_ACCEPT_LABEL_SET:
        case RSVP_OBJ_PROTECTION:
        default:
            if (ndo->ndo_vflag <= 1)
                print_unknown_data(ndo, obj_tptr, "\n\t    ", obj_tlen); /* FIXME indentation */
            break;
        }
        /* do we also want to see a hex dump ? */
        if (ndo->ndo_vflag > 1 || hexdump == TRUE)
            print_unknown_data(ndo, tptr + sizeof(struct rsvp_object_header), "\n\t    ", /* FIXME indentation */
                               rsvp_obj_len - sizeof(struct rsvp_object_header));

        tptr+=rsvp_obj_len;
        tlen-=rsvp_obj_len;
    }
    return 0;
invalid:
    ND_PRINT((ndo, "%s", istr));
    return -1;
trunc:
    ND_PRINT((ndo, "\n\t\t"));
    ND_PRINT((ndo, "%s", tstr));
    return -1;
}


CVE-2017-13050/RPKI-Router: fix a few bugs

The decoder didn't properly check that the PDU length stored in the PDU
header is correct. The only check in place was in rpki_rtr_print() and it
tested whether the length is zero but that is not sufficient. Make all
necessary length and bounds checks, both generic and type-specific, in
rpki_rtr_pdu_print() and reduce rpki_rtr_print() to a simple loop.

This also fixes a minor bug and PDU type 0 (Serial Notify from RFC 6810
Section 5.2) is valid again.

In rpki_rtr_pdu_print() any protocol version was considered version 0,
fix it to skip the rest of input if the PDU protocol version is unknown.

Ibid, the PDU type 10 (Error Report from RFC 6810 Section 5.10) case
block didn't consider the "Length of Error Text" data element mandatory,
put it right.

Ibid, when printing an encapsulated PDU, give itself (via recursion)
respective buffer length to make it possible to tell whether the
encapsulated PDU fits. Do not recurse deeper than 2nd level.

Update prior RPKI-Router test cases that now stop to decode earlier
because of the stricter checks.

This fixes a buffer over-read discovered by Bhargava Shastry,
SecT/TU Berlin.

Add a test using the capture file supplied by the reporter(s).

rpki_rtr_pdu_print (netdissect_options *ndo, const u_char *tptr, u_int indent)
 {
     const rpki_rtr_pdu *pdu_header;
     u_int pdu_type, pdu_len, hexdump;
     const u_char *msg;

     pdu_header = (const rpki_rtr_pdu *)tptr;
     pdu_type = pdu_header->pdu_type;
     pdu_len = EXTRACT_32BITS(pdu_header->length);
    ND_TCHECK2(*tptr, pdu_len);
     hexdump = FALSE;

     ND_PRINT((ndo, "%sRPKI-RTRv%u, %s PDU (%u), length: %u",
 	   indent_string(8),
 	   pdu_header->version,
 	   tok2str(rpki_rtr_pdu_values, "Unknown", pdu_type),
 	   pdu_type, pdu_len));

     switch (pdu_type) {

	/*
	 * The following PDUs share the message format.
	 */
     case RPKI_RTR_SERIAL_NOTIFY_PDU:
     case RPKI_RTR_SERIAL_QUERY_PDU:
     case RPKI_RTR_END_OF_DATA_PDU:
         msg = (const u_char *)(pdu_header + 1);
 	ND_PRINT((ndo, "%sSession ID: 0x%04x, Serial: %u",
 	       indent_string(indent+2),
	       EXTRACT_16BITS(pdu_header->u.session_id),
	       EXTRACT_32BITS(msg)));
	break;

	/*
	 * The following PDUs share the message format.
 	 */
     case RPKI_RTR_RESET_QUERY_PDU:
     case RPKI_RTR_CACHE_RESET_PDU:

 	/*
 	 * Zero payload PDUs.
 	 */
 	break;

     case RPKI_RTR_CACHE_RESPONSE_PDU:
 	ND_PRINT((ndo, "%sSession ID: 0x%04x",
 	       indent_string(indent+2),
 	       EXTRACT_16BITS(pdu_header->u.session_id)));
	break;

    case RPKI_RTR_IPV4_PREFIX_PDU:
 	{
 	    const rpki_rtr_pdu_ipv4_prefix *pdu;

 	    pdu = (const rpki_rtr_pdu_ipv4_prefix *)tptr;
 	    ND_PRINT((ndo, "%sIPv4 Prefix %s/%u-%u, origin-as %u, flags 0x%02x",
 		   indent_string(indent+2),
		   ipaddr_string(ndo, pdu->prefix),
		   pdu->prefix_length, pdu->max_length,
		   EXTRACT_32BITS(pdu->as), pdu->flags));
	}
	break;

    case RPKI_RTR_IPV6_PREFIX_PDU:
 	{
 	    const rpki_rtr_pdu_ipv6_prefix *pdu;

 	    pdu = (const rpki_rtr_pdu_ipv6_prefix *)tptr;
 	    ND_PRINT((ndo, "%sIPv6 Prefix %s/%u-%u, origin-as %u, flags 0x%02x",
 		   indent_string(indent+2),
		   ip6addr_string(ndo, pdu->prefix),
		   pdu->prefix_length, pdu->max_length,
		   EXTRACT_32BITS(pdu->as), pdu->flags));
	}
	break;

    case RPKI_RTR_ERROR_REPORT_PDU:
	{
 	    const rpki_rtr_pdu_error_report *pdu;
 	    u_int encapsulated_pdu_length, text_length, tlen, error_code;

 	    pdu = (const rpki_rtr_pdu_error_report *)tptr;
 	    encapsulated_pdu_length = EXTRACT_32BITS(pdu->encapsulated_pdu_length);
	    ND_TCHECK2(*tptr, encapsulated_pdu_length);
	    tlen = pdu_len;

 	    error_code = EXTRACT_16BITS(pdu->pdu_header.u.error_code);
 	    ND_PRINT((ndo, "%sError code: %s (%u), Encapsulated PDU length: %u",
 		   indent_string(indent+2),
 		   tok2str(rpki_rtr_error_codes, "Unknown", error_code),
 		   error_code, encapsulated_pdu_length));

	    tptr += sizeof(*pdu);
	    tlen -= sizeof(*pdu);
	    /*
	     * Recurse if there is an encapsulated PDU.
	     */
	    if (encapsulated_pdu_length &&
		(encapsulated_pdu_length <= tlen)) {
		ND_PRINT((ndo, "%s-----encapsulated PDU-----", indent_string(indent+4)));
		if (rpki_rtr_pdu_print(ndo, tptr, indent+2))
			goto trunc;
 	    }

	    tptr += encapsulated_pdu_length;
	    tlen -= encapsulated_pdu_length;

 	    /*
 	     * Extract, trail-zero and print the Error message.
 	     */
	    text_length = 0;
	    if (tlen > 4) {
		text_length = EXTRACT_32BITS(tptr);
		tptr += 4;
		tlen -= 4;
	    }
	    ND_TCHECK2(*tptr, text_length);
	    if (text_length && (text_length <= tlen )) {
 		ND_PRINT((ndo, "%sError text: ", indent_string(indent+2)));
		if (fn_printn(ndo, tptr, text_length, ndo->ndo_snapend))
 			goto trunc;
 	    }
 	}
 	break;

     default:

 	/*
 	 * Unknown data, please hexdump.
	 */
	hexdump = TRUE;
    }

    /* do we also want to see a hex dump ? */
     if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) {
 	print_unknown_data(ndo,tptr,"\n\t  ", pdu_len);
     }
    return 0;

 trunc:
    return 1;
 }

The RPKI-Router parser in tcpdump before 4.9.2 has a buffer over-read in print-rpki-rtr.c:rpki_rtr_pdu_print().


rpki_rtr_pdu_print (netdissect_options *ndo, const u_char *tptr, u_int indent)
static u_int
rpki_rtr_pdu_print (netdissect_options *ndo, const u_char *tptr, const u_int len,
	const u_char recurse, const u_int indent)
 {
     const rpki_rtr_pdu *pdu_header;
     u_int pdu_type, pdu_len, hexdump;
     const u_char *msg;

    /* Protocol Version */
    ND_TCHECK_8BITS(tptr);
    if (*tptr != 0) {
	/* Skip the rest of the input buffer because even if this is
	 * a well-formed PDU of a future RPKI-Router protocol version
	 * followed by a well-formed PDU of RPKI-Router protocol
	 * version 0, there is no way to know exactly how to skip the
	 * current PDU.
	 */
	ND_PRINT((ndo, "%sRPKI-RTRv%u (unknown)", indent_string(8), *tptr));
	return len;
    }
    if (len < sizeof(rpki_rtr_pdu)) {
	ND_PRINT((ndo, "(%u bytes is too few to decode)", len));
	goto invalid;
    }
    ND_TCHECK2(*tptr, sizeof(rpki_rtr_pdu));
     pdu_header = (const rpki_rtr_pdu *)tptr;
     pdu_type = pdu_header->pdu_type;
     pdu_len = EXTRACT_32BITS(pdu_header->length);
    /* Do not check bounds with pdu_len yet, do it in the case blocks
     * below to make it possible to decode at least the beginning of
     * a truncated Error Report PDU or a truncated encapsulated PDU.
     */
     hexdump = FALSE;

     ND_PRINT((ndo, "%sRPKI-RTRv%u, %s PDU (%u), length: %u",
 	   indent_string(8),
 	   pdu_header->version,
 	   tok2str(rpki_rtr_pdu_values, "Unknown", pdu_type),
 	   pdu_type, pdu_len));
    if (pdu_len < sizeof(rpki_rtr_pdu) || pdu_len > len)
	goto invalid;

     switch (pdu_type) {

	/*
	 * The following PDUs share the message format.
	 */
     case RPKI_RTR_SERIAL_NOTIFY_PDU:
     case RPKI_RTR_SERIAL_QUERY_PDU:
     case RPKI_RTR_END_OF_DATA_PDU:
	if (pdu_len != sizeof(rpki_rtr_pdu) + 4)
	    goto invalid;
	ND_TCHECK2(*tptr, pdu_len);
         msg = (const u_char *)(pdu_header + 1);
 	ND_PRINT((ndo, "%sSession ID: 0x%04x, Serial: %u",
 	       indent_string(indent+2),
	       EXTRACT_16BITS(pdu_header->u.session_id),
	       EXTRACT_32BITS(msg)));
	break;

	/*
	 * The following PDUs share the message format.
 	 */
     case RPKI_RTR_RESET_QUERY_PDU:
     case RPKI_RTR_CACHE_RESET_PDU:
	if (pdu_len != sizeof(rpki_rtr_pdu))
	    goto invalid;
	/* no additional boundary to check */

 	/*
 	 * Zero payload PDUs.
 	 */
 	break;

     case RPKI_RTR_CACHE_RESPONSE_PDU:
	if (pdu_len != sizeof(rpki_rtr_pdu))
	    goto invalid;
	/* no additional boundary to check */
 	ND_PRINT((ndo, "%sSession ID: 0x%04x",
 	       indent_string(indent+2),
 	       EXTRACT_16BITS(pdu_header->u.session_id)));
	break;

    case RPKI_RTR_IPV4_PREFIX_PDU:
 	{
 	    const rpki_rtr_pdu_ipv4_prefix *pdu;

	    if (pdu_len != sizeof(rpki_rtr_pdu) + 12)
		goto invalid;
	    ND_TCHECK2(*tptr, pdu_len);
 	    pdu = (const rpki_rtr_pdu_ipv4_prefix *)tptr;
 	    ND_PRINT((ndo, "%sIPv4 Prefix %s/%u-%u, origin-as %u, flags 0x%02x",
 		   indent_string(indent+2),
		   ipaddr_string(ndo, pdu->prefix),
		   pdu->prefix_length, pdu->max_length,
		   EXTRACT_32BITS(pdu->as), pdu->flags));
	}
	break;

    case RPKI_RTR_IPV6_PREFIX_PDU:
 	{
 	    const rpki_rtr_pdu_ipv6_prefix *pdu;

	    if (pdu_len != sizeof(rpki_rtr_pdu) + 24)
		goto invalid;
	    ND_TCHECK2(*tptr, pdu_len);
 	    pdu = (const rpki_rtr_pdu_ipv6_prefix *)tptr;
 	    ND_PRINT((ndo, "%sIPv6 Prefix %s/%u-%u, origin-as %u, flags 0x%02x",
 		   indent_string(indent+2),
		   ip6addr_string(ndo, pdu->prefix),
		   pdu->prefix_length, pdu->max_length,
		   EXTRACT_32BITS(pdu->as), pdu->flags));
	}
	break;

    case RPKI_RTR_ERROR_REPORT_PDU:
	{
 	    const rpki_rtr_pdu_error_report *pdu;
 	    u_int encapsulated_pdu_length, text_length, tlen, error_code;

	    tlen = sizeof(rpki_rtr_pdu);
	    /* Do not test for the "Length of Error Text" data element yet. */
	    if (pdu_len < tlen + 4)
		goto invalid;
	    ND_TCHECK2(*tptr, tlen + 4);
	    /* Safe up to and including the "Length of Encapsulated PDU"
	     * data element, more data elements may be present.
	     */
 	    pdu = (const rpki_rtr_pdu_error_report *)tptr;
 	    encapsulated_pdu_length = EXTRACT_32BITS(pdu->encapsulated_pdu_length);
	    tlen += 4;

 	    error_code = EXTRACT_16BITS(pdu->pdu_header.u.error_code);
 	    ND_PRINT((ndo, "%sError code: %s (%u), Encapsulated PDU length: %u",
 		   indent_string(indent+2),
 		   tok2str(rpki_rtr_error_codes, "Unknown", error_code),
 		   error_code, encapsulated_pdu_length));

	    if (encapsulated_pdu_length) {
		/* Section 5.10 of RFC 6810 says:
		 * "An Error Report PDU MUST NOT be sent for an Error Report PDU."
		 *
		 * However, as far as the protocol encoding goes Error Report PDUs can
		 * happen to be nested in each other, however many times, in which case
		 * the decoder should still print such semantically incorrect PDUs.
		 *
		 * That said, "the Erroneous PDU field MAY be truncated" (ibid), thus
		 * to keep things simple this implementation decodes only the two
		 * outermost layers of PDUs and makes bounds checks in the outer and
		 * the inner PDU independently.
		 */
		if (pdu_len < tlen + encapsulated_pdu_length)
		    goto invalid;
		if (! recurse) {
		    ND_TCHECK2(*tptr, tlen + encapsulated_pdu_length);
		}
		else {
		    ND_PRINT((ndo, "%s-----encapsulated PDU-----", indent_string(indent+4)));
		    rpki_rtr_pdu_print(ndo, tptr + tlen,
			encapsulated_pdu_length, 0, indent + 2);
		}
		tlen += encapsulated_pdu_length;
 	    }

	    if (pdu_len < tlen + 4)
		goto invalid;
	    ND_TCHECK2(*tptr, tlen + 4);
	    /* Safe up to and including the "Length of Error Text" data element,
	     * one more data element may be present.
	     */

 	    /*
 	     * Extract, trail-zero and print the Error message.
 	     */
	    text_length = EXTRACT_32BITS(tptr + tlen);
	    tlen += 4;

	    if (text_length) {
		if (pdu_len < tlen + text_length)
		    goto invalid;
		/* fn_printn() makes the bounds check */
 		ND_PRINT((ndo, "%sError text: ", indent_string(indent+2)));
		if (fn_printn(ndo, tptr + tlen, text_length, ndo->ndo_snapend))
 			goto trunc;
 	    }
 	}
 	break;

     default:
	ND_TCHECK2(*tptr, pdu_len);

 	/*
 	 * Unknown data, please hexdump.
	 */
	hexdump = TRUE;
    }

    /* do we also want to see a hex dump ? */
     if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) {
 	print_unknown_data(ndo,tptr,"\n\t  ", pdu_len);
     }
    return pdu_len;

invalid:
    ND_PRINT((ndo, "%s", istr));
    ND_TCHECK2(*tptr, len);
    return len;
 trunc:
    ND_PRINT((ndo, "\n\t%s", tstr));
    return len;
 }


CVE-2017-13049/Rx: add a missing bounds check for Ubik

One of the case blocks in ubik_print() didn't check bounds before
fetching 32 bits of packet data and could overread past the captured
packet data by that amount.

This fixes a buffer over-read discovered by Henri Salo from Nixu
Corporation.

Add a test using the capture file supplied by the reporter(s).

ubik_print(netdissect_options *ndo,
           register const u_char *bp)
{
	int ubik_op;
	int32_t temp;

	/*
	 * Print out the afs call we're invoking.  The table used here was
	 * gleaned from ubik/ubik_int.xg
	 */

	ubik_op = EXTRACT_32BITS(bp + sizeof(struct rx_header));

	ND_PRINT((ndo, " ubik call %s", tok2str(ubik_req, "op#%d", ubik_op)));

	/*
	 * Decode some of the arguments to the Ubik calls
	 */

	bp += sizeof(struct rx_header) + 4;

	switch (ubik_op) {
		case 10000:		/* Beacon */
			ND_TCHECK2(bp[0], 4);
			temp = EXTRACT_32BITS(bp);
			bp += sizeof(int32_t);
			ND_PRINT((ndo, " syncsite %s", temp ? "yes" : "no"));
			ND_PRINT((ndo, " votestart"));
			DATEOUT();
			ND_PRINT((ndo, " dbversion"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			break;
		case 10003:		/* Get sync site */
			ND_PRINT((ndo, " site"));
			UINTOUT();
			break;
		case 20000:		/* Begin */
		case 20001:		/* Commit */
		case 20007:		/* Abort */
		case 20008:		/* Release locks */
		case 20010:		/* Writev */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			break;
		case 20002:		/* Lock */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " file"));
			INTOUT();
			ND_PRINT((ndo, " pos"));
 			INTOUT();
 			ND_PRINT((ndo, " length"));
 			INTOUT();
 			temp = EXTRACT_32BITS(bp);
 			bp += sizeof(int32_t);
 			tok2str(ubik_lock_types, "type %d", temp);
			break;
		case 20003:		/* Write */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " file"));
			INTOUT();
			ND_PRINT((ndo, " pos"));
			INTOUT();
			break;
		case 20005:		/* Get file */
			ND_PRINT((ndo, " file"));
			INTOUT();
			break;
		case 20006:		/* Send file */
			ND_PRINT((ndo, " file"));
			INTOUT();
			ND_PRINT((ndo, " length"));
			INTOUT();
			ND_PRINT((ndo, " dbversion"));
			UBIK_VERSIONOUT();
			break;
		case 20009:		/* Truncate */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " file"));
			INTOUT();
			ND_PRINT((ndo, " length"));
			INTOUT();
			break;
		case 20012:		/* Set version */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " oldversion"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " newversion"));
			UBIK_VERSIONOUT();
			break;
		default:
			;
	}

	return;

trunc:
	ND_PRINT((ndo, " [|ubik]"));
}

The Rx protocol parser in tcpdump before 4.9.2 has a buffer over-read in print-rx.c:ubik_print().


ubik_print(netdissect_options *ndo,
           register const u_char *bp)
{
	int ubik_op;
	int32_t temp;

	/*
	 * Print out the afs call we're invoking.  The table used here was
	 * gleaned from ubik/ubik_int.xg
	 */

	ubik_op = EXTRACT_32BITS(bp + sizeof(struct rx_header));

	ND_PRINT((ndo, " ubik call %s", tok2str(ubik_req, "op#%d", ubik_op)));

	/*
	 * Decode some of the arguments to the Ubik calls
	 */

	bp += sizeof(struct rx_header) + 4;

	switch (ubik_op) {
		case 10000:		/* Beacon */
			ND_TCHECK2(bp[0], 4);
			temp = EXTRACT_32BITS(bp);
			bp += sizeof(int32_t);
			ND_PRINT((ndo, " syncsite %s", temp ? "yes" : "no"));
			ND_PRINT((ndo, " votestart"));
			DATEOUT();
			ND_PRINT((ndo, " dbversion"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			break;
		case 10003:		/* Get sync site */
			ND_PRINT((ndo, " site"));
			UINTOUT();
			break;
		case 20000:		/* Begin */
		case 20001:		/* Commit */
		case 20007:		/* Abort */
		case 20008:		/* Release locks */
		case 20010:		/* Writev */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			break;
		case 20002:		/* Lock */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " file"));
			INTOUT();
			ND_PRINT((ndo, " pos"));
 			INTOUT();
 			ND_PRINT((ndo, " length"));
 			INTOUT();
			ND_TCHECK_32BITS(bp);
 			temp = EXTRACT_32BITS(bp);
 			bp += sizeof(int32_t);
 			tok2str(ubik_lock_types, "type %d", temp);
			break;
		case 20003:		/* Write */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " file"));
			INTOUT();
			ND_PRINT((ndo, " pos"));
			INTOUT();
			break;
		case 20005:		/* Get file */
			ND_PRINT((ndo, " file"));
			INTOUT();
			break;
		case 20006:		/* Send file */
			ND_PRINT((ndo, " file"));
			INTOUT();
			ND_PRINT((ndo, " length"));
			INTOUT();
			ND_PRINT((ndo, " dbversion"));
			UBIK_VERSIONOUT();
			break;
		case 20009:		/* Truncate */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " file"));
			INTOUT();
			ND_PRINT((ndo, " length"));
			INTOUT();
			break;
		case 20012:		/* Set version */
			ND_PRINT((ndo, " tid"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " oldversion"));
			UBIK_VERSIONOUT();
			ND_PRINT((ndo, " newversion"));
			UBIK_VERSIONOUT();
			break;
		default:
			;
	}

	return;

trunc:
	ND_PRINT((ndo, " [|ubik]"));
}


CVE-2017-13045/VQP: add some bounds checks

This fixes a buffer over-read discovered by Bhargava Shastry,
SecT/TU Berlin.

Add a test using the capture file supplied by the reporter(s).

vqp_print(netdissect_options *ndo, register const u_char *pptr, register u_int len)
{
    const struct vqp_common_header_t *vqp_common_header;
    const struct vqp_obj_tlv_t *vqp_obj_tlv;

     const u_char *tptr;
     uint16_t vqp_obj_len;
     uint32_t vqp_obj_type;
    int tlen;
     uint8_t nitems;

     tptr=pptr;
     tlen = len;
     vqp_common_header = (const struct vqp_common_header_t *)pptr;
     ND_TCHECK(*vqp_common_header);

     /*
      * Sanity checking of the header.
     */
    if (VQP_EXTRACT_VERSION(vqp_common_header->version) != VQP_VERSION) {
	ND_PRINT((ndo, "VQP version %u packet not supported",
               VQP_EXTRACT_VERSION(vqp_common_header->version)));
	return;
    }

    /* in non-verbose mode just lets print the basic Message Type */
    if (ndo->ndo_vflag < 1) {
        ND_PRINT((ndo, "VQPv%u %s Message, error-code %s (%u), length %u",
               VQP_EXTRACT_VERSION(vqp_common_header->version),
               tok2str(vqp_msg_type_values, "unknown (%u)",vqp_common_header->msg_type),
               tok2str(vqp_error_code_values, "unknown (%u)",vqp_common_header->error_code),
	       vqp_common_header->error_code,
               len));
        return;
    }

    /* ok they seem to want to know everything - lets fully decode it */
    nitems = vqp_common_header->nitems;
    ND_PRINT((ndo, "\n\tVQPv%u, %s Message, error-code %s (%u), seq 0x%08x, items %u, length %u",
           VQP_EXTRACT_VERSION(vqp_common_header->version),
	   tok2str(vqp_msg_type_values, "unknown (%u)",vqp_common_header->msg_type),
	   tok2str(vqp_error_code_values, "unknown (%u)",vqp_common_header->error_code),
	   vqp_common_header->error_code,
           EXTRACT_32BITS(&vqp_common_header->sequence),
           nitems,
           len));

    /* skip VQP Common header */
    tptr+=sizeof(const struct vqp_common_header_t);
    tlen-=sizeof(const struct vqp_common_header_t);

     while (nitems > 0 && tlen > 0) {

         vqp_obj_tlv = (const struct vqp_obj_tlv_t *)tptr;
         vqp_obj_type = EXTRACT_32BITS(vqp_obj_tlv->obj_type);
         vqp_obj_len = EXTRACT_16BITS(vqp_obj_tlv->obj_length);
         tptr+=sizeof(struct vqp_obj_tlv_t);
        tlen-=sizeof(struct vqp_obj_tlv_t);

        ND_PRINT((ndo, "\n\t  %s Object (0x%08x), length %u, value: ",
               tok2str(vqp_obj_values, "Unknown", vqp_obj_type),
               vqp_obj_type, vqp_obj_len));

        /* basic sanity check */
        if (vqp_obj_type == 0 || vqp_obj_len ==0) {
            return;
        }

         /* did we capture enough for fully decoding the object ? */
         ND_TCHECK2(*tptr, vqp_obj_len);

         switch(vqp_obj_type) {
 	case VQP_OBJ_IP_ADDRESS:
             ND_PRINT((ndo, "%s (0x%08x)", ipaddr_string(ndo, tptr), EXTRACT_32BITS(tptr)));
             break;
             /* those objects have similar semantics - fall through */
        case VQP_OBJ_PORT_NAME:
	case VQP_OBJ_VLAN_NAME:
	case VQP_OBJ_VTP_DOMAIN:
	case VQP_OBJ_ETHERNET_PKT:
            safeputs(ndo, tptr, vqp_obj_len);
            break;
             /* those objects have similar semantics - fall through */
 	case VQP_OBJ_MAC_ADDRESS:
 	case VQP_OBJ_MAC_NULL:
 	      ND_PRINT((ndo, "%s", etheraddr_string(ndo, tptr)));
               break;
         default:
            if (ndo->ndo_vflag <= 1)
                print_unknown_data(ndo,tptr, "\n\t    ", vqp_obj_len);
            break;
        }
	tptr += vqp_obj_len;
	tlen -= vqp_obj_len;
	nitems--;
    }
    return;
trunc:
    ND_PRINT((ndo, "\n\t[|VQP]"));
}

The VQP parser in tcpdump before 4.9.2 has a buffer over-read in print-vqp.c:vqp_print().


vqp_print(netdissect_options *ndo, register const u_char *pptr, register u_int len)
{
    const struct vqp_common_header_t *vqp_common_header;
    const struct vqp_obj_tlv_t *vqp_obj_tlv;

     const u_char *tptr;
     uint16_t vqp_obj_len;
     uint32_t vqp_obj_type;
    u_int tlen;
     uint8_t nitems;

     tptr=pptr;
     tlen = len;
     vqp_common_header = (const struct vqp_common_header_t *)pptr;
     ND_TCHECK(*vqp_common_header);
    if (sizeof(struct vqp_common_header_t) > tlen)
        goto trunc;

     /*
      * Sanity checking of the header.
     */
    if (VQP_EXTRACT_VERSION(vqp_common_header->version) != VQP_VERSION) {
	ND_PRINT((ndo, "VQP version %u packet not supported",
               VQP_EXTRACT_VERSION(vqp_common_header->version)));
	return;
    }

    /* in non-verbose mode just lets print the basic Message Type */
    if (ndo->ndo_vflag < 1) {
        ND_PRINT((ndo, "VQPv%u %s Message, error-code %s (%u), length %u",
               VQP_EXTRACT_VERSION(vqp_common_header->version),
               tok2str(vqp_msg_type_values, "unknown (%u)",vqp_common_header->msg_type),
               tok2str(vqp_error_code_values, "unknown (%u)",vqp_common_header->error_code),
	       vqp_common_header->error_code,
               len));
        return;
    }

    /* ok they seem to want to know everything - lets fully decode it */
    nitems = vqp_common_header->nitems;
    ND_PRINT((ndo, "\n\tVQPv%u, %s Message, error-code %s (%u), seq 0x%08x, items %u, length %u",
           VQP_EXTRACT_VERSION(vqp_common_header->version),
	   tok2str(vqp_msg_type_values, "unknown (%u)",vqp_common_header->msg_type),
	   tok2str(vqp_error_code_values, "unknown (%u)",vqp_common_header->error_code),
	   vqp_common_header->error_code,
           EXTRACT_32BITS(&vqp_common_header->sequence),
           nitems,
           len));

    /* skip VQP Common header */
    tptr+=sizeof(const struct vqp_common_header_t);
    tlen-=sizeof(const struct vqp_common_header_t);

     while (nitems > 0 && tlen > 0) {

         vqp_obj_tlv = (const struct vqp_obj_tlv_t *)tptr;
        ND_TCHECK(*vqp_obj_tlv);
        if (sizeof(struct vqp_obj_tlv_t) > tlen)
            goto trunc;
         vqp_obj_type = EXTRACT_32BITS(vqp_obj_tlv->obj_type);
         vqp_obj_len = EXTRACT_16BITS(vqp_obj_tlv->obj_length);
         tptr+=sizeof(struct vqp_obj_tlv_t);
        tlen-=sizeof(struct vqp_obj_tlv_t);

        ND_PRINT((ndo, "\n\t  %s Object (0x%08x), length %u, value: ",
               tok2str(vqp_obj_values, "Unknown", vqp_obj_type),
               vqp_obj_type, vqp_obj_len));

        /* basic sanity check */
        if (vqp_obj_type == 0 || vqp_obj_len ==0) {
            return;
        }

         /* did we capture enough for fully decoding the object ? */
         ND_TCHECK2(*tptr, vqp_obj_len);
        if (vqp_obj_len > tlen)
            goto trunc;

         switch(vqp_obj_type) {
 	case VQP_OBJ_IP_ADDRESS:
            if (vqp_obj_len != 4)
                goto trunc;
             ND_PRINT((ndo, "%s (0x%08x)", ipaddr_string(ndo, tptr), EXTRACT_32BITS(tptr)));
             break;
             /* those objects have similar semantics - fall through */
        case VQP_OBJ_PORT_NAME:
	case VQP_OBJ_VLAN_NAME:
	case VQP_OBJ_VTP_DOMAIN:
	case VQP_OBJ_ETHERNET_PKT:
            safeputs(ndo, tptr, vqp_obj_len);
            break;
             /* those objects have similar semantics - fall through */
 	case VQP_OBJ_MAC_ADDRESS:
 	case VQP_OBJ_MAC_NULL:
            if (vqp_obj_len != ETHER_ADDR_LEN)
                goto trunc;
 	      ND_PRINT((ndo, "%s", etheraddr_string(ndo, tptr)));
               break;
         default:
            if (ndo->ndo_vflag <= 1)
                print_unknown_data(ndo,tptr, "\n\t    ", vqp_obj_len);
            break;
        }
	tptr += vqp_obj_len;
	tlen -= vqp_obj_len;
	nitems--;
    }
    return;
trunc:
    ND_PRINT((ndo, "\n\t[|VQP]"));
}


CVE-2017-13042/HNCP: add DHCPv6-Data bounds checks

hncp_print_rec() validates each HNCP TLV to be within the declared as
well as the on-the-wire packet space. However, dhcpv6_print() in the same
file didn't do the same for the DHCPv6 options within the HNCP
DHCPv6-Data TLV value, which could cause an out-of-bounds read when
decoding an invalid packet. Add missing checks to dhcpv6_print().

This fixes a buffer over-read discovered by Bhargava Shastry,
SecT/TU Berlin.

Add a test using the capture file supplied by the reporter(s).

dhcpv6_print(netdissect_options *ndo,
             const u_char *cp, u_int length, int indent)
{
    u_int i, t;
    const u_char *tlv, *value;
    uint16_t type, optlen;

     i = 0;
     while (i < length) {
         tlv = cp + i;
         type = EXTRACT_16BITS(tlv);
         optlen = EXTRACT_16BITS(tlv + 2);
        value = tlv + 4;

        ND_PRINT((ndo, "\n"));
        for (t = indent; t > 0; t--)
            ND_PRINT((ndo, "\t"));

         ND_PRINT((ndo, "%s", tok2str(dh6opt_str, "Unknown", type)));
         ND_PRINT((ndo," (%u)", optlen + 4 ));

         switch (type) {
             case DH6OPT_DNS_SERVERS:
            case DH6OPT_SNTP_SERVERS: {
                if (optlen % 16 != 0) {
                    ND_PRINT((ndo, " %s", istr));
                    return -1;
                }
                for (t = 0; t < optlen; t += 16)
                    ND_PRINT((ndo, " %s", ip6addr_string(ndo, value + t)));
            }
                break;
            case DH6OPT_DOMAIN_LIST: {
                const u_char *tp = value;
                while (tp < value + optlen) {
                    ND_PRINT((ndo, " "));
                    if ((tp = ns_nprint(ndo, tp, value + optlen)) == NULL)
                        return -1;
                }
            }
                break;
        }

        i += 4 + optlen;
    }
    return 0;
}

The HNCP parser in tcpdump before 4.9.2 has a buffer over-read in print-hncp.c:dhcpv6_print().


dhcpv6_print(netdissect_options *ndo,
             const u_char *cp, u_int length, int indent)
{
    u_int i, t;
    const u_char *tlv, *value;
    uint16_t type, optlen;

     i = 0;
     while (i < length) {
        if (i + 4 > length)
            return -1;
         tlv = cp + i;
         type = EXTRACT_16BITS(tlv);
         optlen = EXTRACT_16BITS(tlv + 2);
        value = tlv + 4;

        ND_PRINT((ndo, "\n"));
        for (t = indent; t > 0; t--)
            ND_PRINT((ndo, "\t"));

         ND_PRINT((ndo, "%s", tok2str(dh6opt_str, "Unknown", type)));
         ND_PRINT((ndo," (%u)", optlen + 4 ));
        if (i + 4 + optlen > length)
            return -1;

         switch (type) {
             case DH6OPT_DNS_SERVERS:
            case DH6OPT_SNTP_SERVERS: {
                if (optlen % 16 != 0) {
                    ND_PRINT((ndo, " %s", istr));
                    return -1;
                }
                for (t = 0; t < optlen; t += 16)
                    ND_PRINT((ndo, " %s", ip6addr_string(ndo, value + t)));
            }
                break;
            case DH6OPT_DOMAIN_LIST: {
                const u_char *tp = value;
                while (tp < value + optlen) {
                    ND_PRINT((ndo, " "));
                    if ((tp = ns_nprint(ndo, tp, value + optlen)) == NULL)
                        return -1;
                }
            }
                break;
        }

        i += 4 + optlen;
    }
    return 0;
}


CVE-2017-13038/PPP: Do bounds checking.

This fixes a buffer over-read discovered by Brian 'geeknik' Carpenter.

Add a test using the capture file supplied by Katie Holly.

handle_mlppp(netdissect_options *ndo,
             const u_char *p, int length)
{
     if (!ndo->ndo_eflag)
         ND_PRINT((ndo, "MLPPP, "));

     ND_PRINT((ndo, "seq 0x%03x, Flags [%s], length %u",
            (EXTRACT_16BITS(p))&0x0fff, /* only support 12-Bit sequence space for now */
            bittok2str(ppp_ml_flag_values, "none", *p & 0xc0),
           length));
}

The PPP parser in tcpdump before 4.9.2 has a buffer over-read in print-ppp.c:handle_mlppp().


handle_mlppp(netdissect_options *ndo,
             const u_char *p, int length)
{
     if (!ndo->ndo_eflag)
         ND_PRINT((ndo, "MLPPP, "));

    if (length < 2) {
        ND_PRINT((ndo, "[|mlppp]"));
        return;
    }
    if (!ND_TTEST_16BITS(p)) {
        ND_PRINT((ndo, "[|mlppp]"));
        return;
    }

     ND_PRINT((ndo, "seq 0x%03x, Flags [%s], length %u",
            (EXTRACT_16BITS(p))&0x0fff, /* only support 12-Bit sequence space for now */
            bittok2str(ppp_ml_flag_values, "none", *p & 0xc0),
           length));
}


CVE-2017-13037/IP: Add bounds checks when printing time stamp options.

This fixes a buffer over-read discovered by Bhargava Shastry,
SecT/TU Berlin.

Add a test using the capture file supplied by the reporter(s), modified
so the capture file won't be rejected as an invalid capture.

 ip_printts(netdissect_options *ndo,
            register const u_char *cp, u_int length)
 {
	register u_int ptr;
	register u_int len;
	int hoplen;
	const char *type;

 	if (length < 4) {
 		ND_PRINT((ndo, "[bad length %u]", length));
		return;
 	}
 	ND_PRINT((ndo, " TS{"));
 	hoplen = ((cp[3]&0xF) != IPOPT_TS_TSONLY) ? 8 : 4;
 	if ((length - 4) & (hoplen-1))
 		ND_PRINT((ndo, "[bad length %u]", length));
 	ptr = cp[2] - 1;
 	len = 0;
 	if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1)
 		ND_PRINT((ndo, "[bad ptr %u]", cp[2]));
 	switch (cp[3]&0xF) {
 	case IPOPT_TS_TSONLY:
 		ND_PRINT((ndo, "TSONLY"));
		break;
	case IPOPT_TS_TSANDADDR:
		ND_PRINT((ndo, "TS+ADDR"));
		break;
	/*
	 * prespecified should really be 3, but some ones might send 2
	 * instead, and the IPOPT_TS_PRESPEC constant can apparently
	 * have both values, so we have to hard-code it here.
	 */

	case 2:
		ND_PRINT((ndo, "PRESPEC2.0"));
		break;
	case 3:			/* IPOPT_TS_PRESPEC */
		ND_PRINT((ndo, "PRESPEC"));
		break;
	default:
		ND_PRINT((ndo, "[bad ts type %d]", cp[3]&0xF));
		goto done;
	}

	type = " ";
 	for (len = 4; len < length; len += hoplen) {
 		if (ptr == len)
 			type = " ^ ";
 		ND_PRINT((ndo, "%s%d@%s", type, EXTRACT_32BITS(&cp[len+hoplen-4]),
 		       hoplen!=8 ? "" : ipaddr_string(ndo, &cp[len])));
 		type = " ";
	}

done:
	ND_PRINT((ndo, "%s", ptr == len ? " ^ " : ""));

	if (cp[3]>>4)
 		ND_PRINT((ndo, " [%d hops not recorded]} ", cp[3]>>4));
 	else
 		ND_PRINT((ndo, "}"));

The IP parser in tcpdump before 4.9.2 has a buffer over-read in print-ip.c:ip_printts().


 ip_printts(netdissect_options *ndo,
            register const u_char *cp, u_int length)
 {
	register u_int ptr;
	register u_int len;
	int hoplen;
	const char *type;

 	if (length < 4) {
 		ND_PRINT((ndo, "[bad length %u]", length));
		return (0);
 	}
 	ND_PRINT((ndo, " TS{"));
 	hoplen = ((cp[3]&0xF) != IPOPT_TS_TSONLY) ? 8 : 4;
 	if ((length - 4) & (hoplen-1))
 		ND_PRINT((ndo, "[bad length %u]", length));
	ND_TCHECK(cp[2]);
 	ptr = cp[2] - 1;
 	len = 0;
 	if (ptr < 4 || ((ptr - 4) & (hoplen-1)) || ptr > length + 1)
 		ND_PRINT((ndo, "[bad ptr %u]", cp[2]));
	ND_TCHECK(cp[3]);
 	switch (cp[3]&0xF) {
 	case IPOPT_TS_TSONLY:
 		ND_PRINT((ndo, "TSONLY"));
		break;
	case IPOPT_TS_TSANDADDR:
		ND_PRINT((ndo, "TS+ADDR"));
		break;
	/*
	 * prespecified should really be 3, but some ones might send 2
	 * instead, and the IPOPT_TS_PRESPEC constant can apparently
	 * have both values, so we have to hard-code it here.
	 */

	case 2:
		ND_PRINT((ndo, "PRESPEC2.0"));
		break;
	case 3:			/* IPOPT_TS_PRESPEC */
		ND_PRINT((ndo, "PRESPEC"));
		break;
	default:
		ND_PRINT((ndo, "[bad ts type %d]", cp[3]&0xF));
		goto done;
	}

	type = " ";
 	for (len = 4; len < length; len += hoplen) {
 		if (ptr == len)
 			type = " ^ ";
		ND_TCHECK2(cp[len], hoplen);
 		ND_PRINT((ndo, "%s%d@%s", type, EXTRACT_32BITS(&cp[len+hoplen-4]),
 		       hoplen!=8 ? "" : ipaddr_string(ndo, &cp[len])));
 		type = " ";
	}

done:
	ND_PRINT((ndo, "%s", ptr == len ? " ^ " : ""));

	if (cp[3]>>4)
 		ND_PRINT((ndo, " [%d hops not recorded]} ", cp[3]>>4));
 	else
 		ND_PRINT((ndo, "}"));


CVE-2017-13036/OSPFv3: Add a bounds check before fetching data

This fixes a buffer over-read discovered by Bhargava Shastry,
SecT/TU Berlin.

Add a test using the capture file supplied by the reporter(s), modified
so the capture file won't be rejected as an invalid capture.

ospf6_decode_v3(netdissect_options *ndo,
                register const struct ospf6hdr *op,
                register const u_char *dataend)
{
	register const rtrid_t *ap;
	register const struct lsr6 *lsrp;
	register const struct lsa6_hdr *lshp;
	register const struct lsa6 *lsap;
	register int i;

	switch (op->ospf6_type) {

 	case OSPF_TYPE_HELLO: {
 		register const struct hello6 *hellop = (const struct hello6 *)((const uint8_t *)op + OSPF6HDR_LEN);

 		ND_PRINT((ndo, "\n\tOptions [%s]",
 		          bittok2str(ospf6_option_values, "none",
 		          EXTRACT_32BITS(&hellop->hello_options))));

		ND_TCHECK(hellop->hello_deadint);
		ND_PRINT((ndo, "\n\t  Hello Timer %us, Dead Timer %us, Interface-ID %s, Priority %u",
		          EXTRACT_16BITS(&hellop->hello_helloint),
		          EXTRACT_16BITS(&hellop->hello_deadint),
		          ipaddr_string(ndo, &hellop->hello_ifid),
		          hellop->hello_priority));

		ND_TCHECK(hellop->hello_dr);
		if (EXTRACT_32BITS(&hellop->hello_dr) != 0)
			ND_PRINT((ndo, "\n\t  Designated Router %s",
			    ipaddr_string(ndo, &hellop->hello_dr)));
		ND_TCHECK(hellop->hello_bdr);
		if (EXTRACT_32BITS(&hellop->hello_bdr) != 0)
			ND_PRINT((ndo, ", Backup Designated Router %s",
			    ipaddr_string(ndo, &hellop->hello_bdr)));
		if (ndo->ndo_vflag > 1) {
			ND_PRINT((ndo, "\n\t  Neighbor List:"));
			ap = hellop->hello_neighbor;
			while ((const u_char *)ap < dataend) {
				ND_TCHECK(*ap);
				ND_PRINT((ndo, "\n\t    %s", ipaddr_string(ndo, ap)));
				++ap;
			}
		}
		break;	/* HELLO */
	}

	case OSPF_TYPE_DD: {
		register const struct dd6 *ddp = (const struct dd6 *)((const uint8_t *)op + OSPF6HDR_LEN);

		ND_TCHECK(ddp->db_options);
		ND_PRINT((ndo, "\n\tOptions [%s]",
		          bittok2str(ospf6_option_values, "none",
		          EXTRACT_32BITS(&ddp->db_options))));
		ND_TCHECK(ddp->db_flags);
		ND_PRINT((ndo, ", DD Flags [%s]",
		          bittok2str(ospf6_dd_flag_values,"none",ddp->db_flags)));

		ND_TCHECK(ddp->db_seq);
		ND_PRINT((ndo, ", MTU %u, DD-Sequence 0x%08x",
                       EXTRACT_16BITS(&ddp->db_mtu),
                       EXTRACT_32BITS(&ddp->db_seq)));
		if (ndo->ndo_vflag > 1) {
			/* Print all the LS adv's */
			lshp = ddp->db_lshdr;
			while ((const u_char *)lshp < dataend) {
				if (ospf6_print_lshdr(ndo, lshp++, dataend))
					goto trunc;
			}
		}
		break;
	}

	case OSPF_TYPE_LS_REQ:
		if (ndo->ndo_vflag > 1) {
			lsrp = (const struct lsr6 *)((const uint8_t *)op + OSPF6HDR_LEN);
			while ((const u_char *)lsrp < dataend) {
				ND_TCHECK(*lsrp);
				ND_PRINT((ndo, "\n\t  Advertising Router %s",
				          ipaddr_string(ndo, &lsrp->ls_router)));
				ospf6_print_ls_type(ndo, EXTRACT_16BITS(&lsrp->ls_type),
                                                    &lsrp->ls_stateid);
				++lsrp;
			}
		}
		break;

	case OSPF_TYPE_LS_UPDATE:
		if (ndo->ndo_vflag > 1) {
			register const struct lsu6 *lsup = (const struct lsu6 *)((const uint8_t *)op + OSPF6HDR_LEN);

			ND_TCHECK(lsup->lsu_count);
			i = EXTRACT_32BITS(&lsup->lsu_count);
			lsap = lsup->lsu_lsa;
			while ((const u_char *)lsap < dataend && i--) {
				if (ospf6_print_lsa(ndo, lsap, dataend))
					goto trunc;
				lsap = (const struct lsa6 *)((const u_char *)lsap +
				    EXTRACT_16BITS(&lsap->ls_hdr.ls_length));
			}
		}
		break;

	case OSPF_TYPE_LS_ACK:
		if (ndo->ndo_vflag > 1) {
			lshp = (const struct lsa6_hdr *)((const uint8_t *)op + OSPF6HDR_LEN);
			while ((const u_char *)lshp < dataend) {
				if (ospf6_print_lshdr(ndo, lshp++, dataend))
					goto trunc;
			}
		}
		break;

	default:
		break;
	}
	return (0);
trunc:
	return (1);
}

The OSPFv3 parser in tcpdump before 4.9.2 has a buffer over-read in print-ospf6.c:ospf6_decode_v3().


ospf6_decode_v3(netdissect_options *ndo,
                register const struct ospf6hdr *op,
                register const u_char *dataend)
{
	register const rtrid_t *ap;
	register const struct lsr6 *lsrp;
	register const struct lsa6_hdr *lshp;
	register const struct lsa6 *lsap;
	register int i;

	switch (op->ospf6_type) {

 	case OSPF_TYPE_HELLO: {
 		register const struct hello6 *hellop = (const struct hello6 *)((const uint8_t *)op + OSPF6HDR_LEN);

		ND_TCHECK_32BITS(&hellop->hello_options);
 		ND_PRINT((ndo, "\n\tOptions [%s]",
 		          bittok2str(ospf6_option_values, "none",
 		          EXTRACT_32BITS(&hellop->hello_options))));

		ND_TCHECK(hellop->hello_deadint);
		ND_PRINT((ndo, "\n\t  Hello Timer %us, Dead Timer %us, Interface-ID %s, Priority %u",
		          EXTRACT_16BITS(&hellop->hello_helloint),
		          EXTRACT_16BITS(&hellop->hello_deadint),
		          ipaddr_string(ndo, &hellop->hello_ifid),
		          hellop->hello_priority));

		ND_TCHECK(hellop->hello_dr);
		if (EXTRACT_32BITS(&hellop->hello_dr) != 0)
			ND_PRINT((ndo, "\n\t  Designated Router %s",
			    ipaddr_string(ndo, &hellop->hello_dr)));
		ND_TCHECK(hellop->hello_bdr);
		if (EXTRACT_32BITS(&hellop->hello_bdr) != 0)
			ND_PRINT((ndo, ", Backup Designated Router %s",
			    ipaddr_string(ndo, &hellop->hello_bdr)));
		if (ndo->ndo_vflag > 1) {
			ND_PRINT((ndo, "\n\t  Neighbor List:"));
			ap = hellop->hello_neighbor;
			while ((const u_char *)ap < dataend) {
				ND_TCHECK(*ap);
				ND_PRINT((ndo, "\n\t    %s", ipaddr_string(ndo, ap)));
				++ap;
			}
		}
		break;	/* HELLO */
	}

	case OSPF_TYPE_DD: {
		register const struct dd6 *ddp = (const struct dd6 *)((const uint8_t *)op + OSPF6HDR_LEN);

		ND_TCHECK(ddp->db_options);
		ND_PRINT((ndo, "\n\tOptions [%s]",
		          bittok2str(ospf6_option_values, "none",
		          EXTRACT_32BITS(&ddp->db_options))));
		ND_TCHECK(ddp->db_flags);
		ND_PRINT((ndo, ", DD Flags [%s]",
		          bittok2str(ospf6_dd_flag_values,"none",ddp->db_flags)));

		ND_TCHECK(ddp->db_seq);
		ND_PRINT((ndo, ", MTU %u, DD-Sequence 0x%08x",
                       EXTRACT_16BITS(&ddp->db_mtu),
                       EXTRACT_32BITS(&ddp->db_seq)));
		if (ndo->ndo_vflag > 1) {
			/* Print all the LS adv's */
			lshp = ddp->db_lshdr;
			while ((const u_char *)lshp < dataend) {
				if (ospf6_print_lshdr(ndo, lshp++, dataend))
					goto trunc;
			}
		}
		break;
	}

	case OSPF_TYPE_LS_REQ:
		if (ndo->ndo_vflag > 1) {
			lsrp = (const struct lsr6 *)((const uint8_t *)op + OSPF6HDR_LEN);
			while ((const u_char *)lsrp < dataend) {
				ND_TCHECK(*lsrp);
				ND_PRINT((ndo, "\n\t  Advertising Router %s",
				          ipaddr_string(ndo, &lsrp->ls_router)));
				ospf6_print_ls_type(ndo, EXTRACT_16BITS(&lsrp->ls_type),
                                                    &lsrp->ls_stateid);
				++lsrp;
			}
		}
		break;

	case OSPF_TYPE_LS_UPDATE:
		if (ndo->ndo_vflag > 1) {
			register const struct lsu6 *lsup = (const struct lsu6 *)((const uint8_t *)op + OSPF6HDR_LEN);

			ND_TCHECK(lsup->lsu_count);
			i = EXTRACT_32BITS(&lsup->lsu_count);
			lsap = lsup->lsu_lsa;
			while ((const u_char *)lsap < dataend && i--) {
				if (ospf6_print_lsa(ndo, lsap, dataend))
					goto trunc;
				lsap = (const struct lsa6 *)((const u_char *)lsap +
				    EXTRACT_16BITS(&lsap->ls_hdr.ls_length));
			}
		}
		break;

	case OSPF_TYPE_LS_ACK:
		if (ndo->ndo_vflag > 1) {
			lshp = (const struct lsa6_hdr *)((const uint8_t *)op + OSPF6HDR_LEN);
			while ((const u_char *)lshp < dataend) {
				if (ospf6_print_lshdr(ndo, lshp++, dataend))
					goto trunc;
			}
		}
		break;

	default:
		break;
	}
	return (0);
trunc:
	return (1);
}


CVE-2017-13033/VTP: Add more bound and length checks.

This fixes a buffer over-read discovered by Bhargava Shastry.

Add a test using the capture file supplied by the reporter(s), modified
so the capture file won't be rejected as an invalid capture.

Update another VTP test's .out file for this change.

Don't treate a TLV type or length of 0 as invalid; a type of 0 should
just be reported as illegal if that type isn't used, and the length is
the length of the *value*, not the length of the entire TLV, so if it's
zero there won't be an infinite loop.  (It's still not *legal*, as the
values of all the TLVs we handle are 1 16-bit word long; we added a
check for that.)

Update some comments while we're at it, to give a new URL for one Cisco
page and a non-Cisco URL for another former Cisco page (Cisco's UniverCD
pages don't seem to be available any more, and Cisco's robots.txt file
didn't allow the Wayback Machine to archive it).

vtp_print (netdissect_options *ndo,
           const u_char *pptr, u_int length)
{
    int type, len, tlv_len, tlv_value, mgmtd_len;
    const u_char *tptr;
    const struct vtp_vlan_ *vtp_vlan;

    if (length < VTP_HEADER_LEN)
        goto trunc;

    tptr = pptr;

    ND_TCHECK2(*tptr, VTP_HEADER_LEN);

    type = *(tptr+1);
    ND_PRINT((ndo, "VTPv%u, Message %s (0x%02x), length %u",
	   *tptr,
	   tok2str(vtp_message_type_values,"Unknown message type", type),
	   type,
	   length));

    /* In non-verbose mode, just print version and message type */
    if (ndo->ndo_vflag < 1) {
        return;
    }

    /* verbose mode print all fields */
    ND_PRINT((ndo, "\n\tDomain name: "));
    mgmtd_len = *(tptr + 3);
    if (mgmtd_len < 1 ||  mgmtd_len > 32) {
	ND_PRINT((ndo, " [invalid MgmtD Len %d]", mgmtd_len));
	return;
    }
    fn_printzp(ndo, tptr + 4, mgmtd_len, NULL);
    ND_PRINT((ndo, ", %s: %u",
	   tok2str(vtp_header_values, "Unknown", type),
	   *(tptr+2)));

    tptr += VTP_HEADER_LEN;

    switch (type) {

    case VTP_SUMMARY_ADV:

	/*
	 *  SUMMARY ADVERTISEMENT
	 *
	 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |     Version   |     Code      |    Followers  |    MgmtD Len  |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |       Management Domain Name  (zero-padded to 32 bytes)       |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                    Configuration revision number              |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                  Updater Identity IP address                  |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                    Update Timestamp (12 bytes)                |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                        MD5 digest (16 bytes)                  |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *
	 */

	ND_TCHECK2(*tptr, 8);
	ND_PRINT((ndo, "\n\t  Config Rev %x, Updater %s",
	       EXTRACT_32BITS(tptr),
	       ipaddr_string(ndo, tptr+4)));
	tptr += 8;
	ND_TCHECK2(*tptr, VTP_UPDATE_TIMESTAMP_LEN);
	ND_PRINT((ndo, ", Timestamp 0x%08x 0x%08x 0x%08x",
	       EXTRACT_32BITS(tptr),
	       EXTRACT_32BITS(tptr + 4),
	       EXTRACT_32BITS(tptr + 8)));
	tptr += VTP_UPDATE_TIMESTAMP_LEN;
	ND_TCHECK2(*tptr, VTP_MD5_DIGEST_LEN);
	ND_PRINT((ndo, ", MD5 digest: %08x%08x%08x%08x",
	       EXTRACT_32BITS(tptr),
	       EXTRACT_32BITS(tptr + 4),
	       EXTRACT_32BITS(tptr + 8),
	       EXTRACT_32BITS(tptr + 12)));
	tptr += VTP_MD5_DIGEST_LEN;
	break;

    case VTP_SUBSET_ADV:

	/*
	 *  SUBSET ADVERTISEMENT
	 *
	 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |     Version   |     Code      |   Seq number  |    MgmtD Len  |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |       Management Domain Name  (zero-padded to 32 bytes)       |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                    Configuration revision number              |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                         VLAN info field 1                     |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                         ................                      |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                         VLAN info field N                     |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *
	 */

	ND_TCHECK_32BITS(tptr);
	ND_PRINT((ndo, ", Config Rev %x", EXTRACT_32BITS(tptr)));

	/*
	 *  VLAN INFORMATION
	 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  | V info len    |    Status     |  VLAN type    | VLAN name len |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |       ISL vlan id             |            MTU size           |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                     802.10 index (SAID)                       |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                         VLAN name                             |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *
	 */

	tptr += 4;
	while (tptr < (pptr+length)) {

	    ND_TCHECK_8BITS(tptr);
	    len = *tptr;
	    if (len == 0)
		break;

 	    ND_TCHECK2(*tptr, len);

 	    vtp_vlan = (const struct vtp_vlan_*)tptr;
 	    ND_TCHECK(*vtp_vlan);
 	    ND_PRINT((ndo, "\n\tVLAN info status %s, type %s, VLAN-id %u, MTU %u, SAID 0x%08x, Name ",
 		   tok2str(vtp_vlan_status,"Unknown",vtp_vlan->status),
 		   tok2str(vtp_vlan_type_values,"Unknown",vtp_vlan->type),
 		   EXTRACT_16BITS(&vtp_vlan->vlanid),
 		   EXTRACT_16BITS(&vtp_vlan->mtu),
 		   EXTRACT_32BITS(&vtp_vlan->index)));
	    fn_printzp(ndo, tptr + VTP_VLAN_INFO_OFFSET, vtp_vlan->name_len, NULL);
            /*
             * Vlan names are aligned to 32-bit boundaries.
             */
            len  -= VTP_VLAN_INFO_OFFSET + 4*((vtp_vlan->name_len + 3)/4);
            tptr += VTP_VLAN_INFO_OFFSET + 4*((vtp_vlan->name_len + 3)/4);

             /* TLV information follows */

             while (len > 0) {

                 /*
                 * Cisco specs says 2 bytes for type + 2 bytes for length, take only 1
                 * See: http://www.cisco.com/univercd/cc/td/doc/product/lan/trsrb/frames.htm
                  */
                 type = *tptr;
                 tlv_len = *(tptr+1);

                 ND_PRINT((ndo, "\n\t\t%s (0x%04x) TLV",
                        tok2str(vtp_vlan_tlv_values, "Unknown", type),
                        type));

                /*
                 * infinite loop check
                 */
                if (type == 0 || tlv_len == 0) {
                     return;
                 }
                 ND_TCHECK2(*tptr, tlv_len * 2 +2);

                tlv_value = EXTRACT_16BITS(tptr+2);
                switch (type) {
                case VTP_VLAN_STE_HOP_COUNT:
                    ND_PRINT((ndo, ", %u", tlv_value));
                    break;
                case VTP_VLAN_PRUNING:
                    ND_PRINT((ndo, ", %s (%u)",
                           tlv_value == 1 ? "Enabled" : "Disabled",
                           tlv_value));
                    break;
                case VTP_VLAN_STP_TYPE:
                    ND_PRINT((ndo, ", %s (%u)",
                           tok2str(vtp_stp_type_values, "Unknown", tlv_value),
                           tlv_value));
                    break;
                case VTP_VLAN_BRIDGE_TYPE:
                    ND_PRINT((ndo, ", %s (%u)",
                           tlv_value == 1 ? "SRB" : "SRT",
                           tlv_value));
                    break;
                case VTP_VLAN_BACKUP_CRF_MODE:
                    ND_PRINT((ndo, ", %s (%u)",
                           tlv_value == 1 ? "Backup" : "Not backup",
                           tlv_value));
                    break;
                    /*
                     * FIXME those are the defined TLVs that lack a decoder
                     * you are welcome to contribute code ;-)
                     */
                case VTP_VLAN_SOURCE_ROUTING_RING_NUMBER:
                case VTP_VLAN_SOURCE_ROUTING_BRIDGE_NUMBER:
                case VTP_VLAN_PARENT_VLAN:
                case VTP_VLAN_TRANS_BRIDGED_VLAN:
                case VTP_VLAN_ARP_HOP_COUNT:
                default:
		    print_unknown_data(ndo, tptr, "\n\t\t  ", 2 + tlv_len*2);
                    break;
                 }
                 len -= 2 + tlv_len*2;
                 tptr += 2 + tlv_len*2;
            }
	}
	break;

    case VTP_ADV_REQUEST:

	/*
	 *  ADVERTISEMENT REQUEST
	 *
	 *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |     Version   |     Code      |   Reserved    |    MgmtD Len  |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |       Management Domain Name  (zero-padded to 32 bytes)       |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *  |                          Start value                          |
	 *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
	 *
	 */

	ND_TCHECK2(*tptr, 4);
	ND_PRINT((ndo, "\n\tStart value: %u", EXTRACT_32BITS(tptr)));
	break;

    case VTP_JOIN_MESSAGE:

	/* FIXME - Could not find message format */
	break;

    default:
	break;
    }

    return;

 trunc:
    ND_PRINT((ndo, "[|vtp]"));
}

The VTP parser in tcpdump before 4.9.2 has a buffer over-read in print-vtp.c:vtp_print().