Commit message: CVE-2017-13053/BGP: fix VPN route target bounds checks

decode_rt_routing_info() didn't check bounds before fetching 4 octets of
the origin AS field and could over-read the input buffer, put it right.

It also fetched the varying number of octets of the route target field
from 4 octets lower than the correct offset, put it right.

It also used the same temporary buffer explicitly through as_printf()
and implicitly through bgp_vpn_rd_print() so the end result of snprintf()
was not what was originally intended.

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).
CVE: CVE-2017-13053
CWE: CWE-125
Vulnerability description: The BGP parser in tcpdump before 4.9.2 has a buffer over-read in print-bgp.c:decode_rt_routing_info().

Project: tcpdump
File: print-bgp.c

as_printf(netdissect_options *ndo,
          char *str, int size, u_int asnum)
{
	if (!ndo->ndo_bflag || asnum <= 0xFFFF) {
		snprintf(str, size, "%u", asnum);
	} else {
		snprintf(str, size, "%u.%u", asnum >> 16, asnum & 0xFFFF);
	}
	return str;
}

Project: tcpdump
File: print-bgp.c

bgp_attr_get_as_size(netdissect_options *ndo,
                     uint8_t bgpa_type, const u_char *pptr, int len)
{
    const u_char *tptr = pptr;

    /*
     * If the path attribute is the optional AS4 path type, then we already
     * know, that ASs must be encoded in 4 byte format.
     */
    if (bgpa_type == BGPTYPE_AS4_PATH) {
        return 4;
    }

    /*
     * Let us assume that ASs are of 2 bytes in size, and check if the AS-Path
     * TLV is good. If not, ask the caller to try with AS encoded as 4 bytes
     * each.
     */
    while (tptr < pptr + len) {
        ND_TCHECK(tptr[0]);

        /*
         * If we do not find a valid segment type, our guess might be wrong.
         */
        if (tptr[0] < BGP_AS_SEG_TYPE_MIN || tptr[0] > BGP_AS_SEG_TYPE_MAX) {
            goto trunc;
        }
        ND_TCHECK(tptr[1]);
        tptr += 2 + tptr[1] * 2;
    }

    /*
     * If we correctly reached end of the AS path attribute data content,
     * then most likely ASs were indeed encoded as 2 bytes.
     */
    if (tptr == pptr + len) {
        return 2;
    }

trunc:

    /*
     * We can come here, either we did not have enough data, or if we
     * try to decode 4 byte ASs in 2 byte format. Either way, return 4,
     * so that calller can try to decode each AS as of 4 bytes. If indeed
     * there was not enough data, it will crib and end the parse anyways.
     */
   return 4;
}

Project: tcpdump
File: print-bgp.c

bgp_header_print(netdissect_options *ndo,
                 const u_char *dat, int length)
{
	struct bgp bgp;

	ND_TCHECK2(dat[0], BGP_SIZE);
	memcpy(&bgp, dat, BGP_SIZE);
	ND_PRINT((ndo, "\n\t%s Message (%u), length: %u",
               tok2str(bgp_msg_values, "Unknown", bgp.bgp_type),
               bgp.bgp_type,
               length));

	switch (bgp.bgp_type) {
	case BGP_OPEN:
		bgp_open_print(ndo, dat, length);
		break;
	case BGP_UPDATE:
		bgp_update_print(ndo, dat, length);
		break;
	case BGP_NOTIFICATION:
		bgp_notification_print(ndo, dat, length);
		break;
        case BGP_KEEPALIVE:
                break;
        case BGP_ROUTE_REFRESH:
                bgp_route_refresh_print(ndo, dat, length);
                break;
        default:
                /* we have no decoder for the BGP message */
                ND_TCHECK2(*dat, length);
                ND_PRINT((ndo, "\n\t  no Message %u decoder", bgp.bgp_type));
                print_unknown_data(ndo, dat, "\n\t  ", length);
                break;
	}
	return 1;
trunc:
	ND_PRINT((ndo, "[|BGP]"));
	return 0;
}

Project: tcpdump
File: print-bgp.c

bgp_notification_print(netdissect_options *ndo,
                       const u_char *dat, int length)
{
	struct bgp_notification bgpn;
	const u_char *tptr;
	uint8_t shutdown_comm_length;
	uint8_t remainder_offset;

	ND_TCHECK2(dat[0], BGP_NOTIFICATION_SIZE);
	memcpy(&bgpn, dat, BGP_NOTIFICATION_SIZE);

        /* some little sanity checking */
        if (length<BGP_NOTIFICATION_SIZE)
            return;

	ND_PRINT((ndo, ", %s (%u)",
	       tok2str(bgp_notify_major_values, "Unknown Error",
			  bgpn.bgpn_major),
	       bgpn.bgpn_major));

        switch (bgpn.bgpn_major) {

        case BGP_NOTIFY_MAJOR_MSG:
            ND_PRINT((ndo, ", subcode %s (%u)",
		   tok2str(bgp_notify_minor_msg_values, "Unknown",
			      bgpn.bgpn_minor),
		   bgpn.bgpn_minor));
            break;
        case BGP_NOTIFY_MAJOR_OPEN:
            ND_PRINT((ndo, ", subcode %s (%u)",
		   tok2str(bgp_notify_minor_open_values, "Unknown",
			      bgpn.bgpn_minor),
		   bgpn.bgpn_minor));
            break;
        case BGP_NOTIFY_MAJOR_UPDATE:
            ND_PRINT((ndo, ", subcode %s (%u)",
		   tok2str(bgp_notify_minor_update_values, "Unknown",
			      bgpn.bgpn_minor),
		   bgpn.bgpn_minor));
            break;
        case BGP_NOTIFY_MAJOR_FSM:
            ND_PRINT((ndo, " subcode %s (%u)",
		   tok2str(bgp_notify_minor_fsm_values, "Unknown",
			      bgpn.bgpn_minor),
		   bgpn.bgpn_minor));
            break;
        case BGP_NOTIFY_MAJOR_CAP:
            ND_PRINT((ndo, " subcode %s (%u)",
		   tok2str(bgp_notify_minor_cap_values, "Unknown",
			      bgpn.bgpn_minor),
		   bgpn.bgpn_minor));
            break;
        case BGP_NOTIFY_MAJOR_CEASE:
            ND_PRINT((ndo, ", subcode %s (%u)",
		   tok2str(bgp_notify_minor_cease_values, "Unknown",
			      bgpn.bgpn_minor),
		   bgpn.bgpn_minor));

	    /* draft-ietf-idr-cease-subcode-02 mentions optionally 7 bytes
             * for the maxprefix subtype, which may contain AFI, SAFI and MAXPREFIXES
             */
	    if(bgpn.bgpn_minor == BGP_NOTIFY_MINOR_CEASE_MAXPRFX && length >= BGP_NOTIFICATION_SIZE + 7) {
		tptr = dat + BGP_NOTIFICATION_SIZE;
		ND_TCHECK2(*tptr, 7);
		ND_PRINT((ndo, ", AFI %s (%u), SAFI %s (%u), Max Prefixes: %u",
		       tok2str(af_values, "Unknown",
				  EXTRACT_16BITS(tptr)),
		       EXTRACT_16BITS(tptr),
		       tok2str(bgp_safi_values, "Unknown", *(tptr+2)),
		       *(tptr+2),
		       EXTRACT_32BITS(tptr+3)));
	    }
	    /*
	     * draft-ietf-idr-shutdown describes a method to send a communication
	     * intended for human consumption regarding the Administrative Shutdown
	     */
	    if ((bgpn.bgpn_minor == BGP_NOTIFY_MINOR_CEASE_SHUT ||
		bgpn.bgpn_minor == BGP_NOTIFY_MINOR_CEASE_RESET) &&
		length >= BGP_NOTIFICATION_SIZE + 1) {
		    tptr = dat + BGP_NOTIFICATION_SIZE;
		    ND_TCHECK2(*tptr, 1);
		    shutdown_comm_length = *(tptr);
		    remainder_offset = 0;
		    /* garbage, hexdump it all */
		    if (shutdown_comm_length > BGP_NOTIFY_MINOR_CEASE_ADMIN_SHUTDOWN_LEN ||
			shutdown_comm_length > length - (BGP_NOTIFICATION_SIZE + 1)) {
			    ND_PRINT((ndo, ", invalid Shutdown Communication length"));
		    }
		    else if (shutdown_comm_length == 0) {
			    ND_PRINT((ndo, ", empty Shutdown Communication"));
			    remainder_offset += 1;
		    }
		    /* a proper shutdown communication */
		    else {
			    ND_TCHECK2(*(tptr+1), shutdown_comm_length);
			    ND_PRINT((ndo, ", Shutdown Communication (length: %u): \"", shutdown_comm_length));
			    (void)fn_printn(ndo, tptr+1, shutdown_comm_length, NULL);
			    ND_PRINT((ndo, "\""));
			    remainder_offset += shutdown_comm_length + 1;
		    }
		    /* if there is trailing data, hexdump it */
		    if(length - (remainder_offset + BGP_NOTIFICATION_SIZE) > 0) {
			    ND_PRINT((ndo, ", Data: (length: %u)", length - (remainder_offset + BGP_NOTIFICATION_SIZE)));
			    hex_print(ndo, "\n\t\t", tptr + remainder_offset, length - (remainder_offset + BGP_NOTIFICATION_SIZE));
		    }
	    }
	    break;
	default:
            break;
        }

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

Project: tcpdump
File: print-bgp.c

bgp_open_print(netdissect_options *ndo,
               const u_char *dat, int length)
{
	struct bgp_open bgpo;
	struct bgp_opt bgpopt;
	const u_char *opt;
	int i;

	ND_TCHECK2(dat[0], BGP_OPEN_SIZE);
	memcpy(&bgpo, dat, BGP_OPEN_SIZE);

	ND_PRINT((ndo, "\n\t  Version %d, ", bgpo.bgpo_version));
	ND_PRINT((ndo, "my AS %s, ",
	    as_printf(ndo, astostr, sizeof(astostr), ntohs(bgpo.bgpo_myas))));
	ND_PRINT((ndo, "Holdtime %us, ", ntohs(bgpo.bgpo_holdtime)));
	ND_PRINT((ndo, "ID %s", ipaddr_string(ndo, &bgpo.bgpo_id)));
	ND_PRINT((ndo, "\n\t  Optional parameters, length: %u", bgpo.bgpo_optlen));

        /* some little sanity checking */
        if (length < bgpo.bgpo_optlen+BGP_OPEN_SIZE)
            return;

	/* ugly! */
	opt = &((const struct bgp_open *)dat)->bgpo_optlen;
	opt++;

	i = 0;
	while (i < bgpo.bgpo_optlen) {
		ND_TCHECK2(opt[i], BGP_OPT_SIZE);
		memcpy(&bgpopt, &opt[i], BGP_OPT_SIZE);
		if (i + 2 + bgpopt.bgpopt_len > bgpo.bgpo_optlen) {
			ND_PRINT((ndo, "\n\t     Option %d, length: %u", bgpopt.bgpopt_type, bgpopt.bgpopt_len));
			break;
		}

		ND_PRINT((ndo, "\n\t    Option %s (%u), length: %u",
		       tok2str(bgp_opt_values,"Unknown",
				  bgpopt.bgpopt_type),
		       bgpopt.bgpopt_type,
		       bgpopt.bgpopt_len));

		/* now let's decode the options we know*/
		switch(bgpopt.bgpopt_type) {

		case BGP_OPT_CAP:
			bgp_capabilities_print(ndo, &opt[i+BGP_OPT_SIZE],
			    bgpopt.bgpopt_len);
			break;

		case BGP_OPT_AUTH:
		default:
		       ND_PRINT((ndo, "\n\t      no decoder for option %u",
			   bgpopt.bgpopt_type));
		       break;
		}
		i += BGP_OPT_SIZE + bgpopt.bgpopt_len;
	}
	return;
trunc:
	ND_PRINT((ndo, "[|BGP]"));
}

Project: tcpdump
File: print-bgp.c

bgp_print(netdissect_options *ndo,
          const u_char *dat, int length)
{
	const u_char *p;
	const u_char *ep;
	const u_char *start;
	const u_char marker[] = {
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
		0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	};
	struct bgp bgp;
	uint16_t hlen;

	ep = dat + length;
	if (ndo->ndo_snapend < dat + length)
		ep = ndo->ndo_snapend;

	ND_PRINT((ndo, ": BGP"));

        if (ndo->ndo_vflag < 1) /* lets be less chatty */
                return;

	p = dat;
	start = p;
	while (p < ep) {
		if (!ND_TTEST2(p[0], 1))
			break;
		if (p[0] != 0xff) {
			p++;
			continue;
		}

		if (!ND_TTEST2(p[0], sizeof(marker)))
			break;
		if (memcmp(p, marker, sizeof(marker)) != 0) {
			p++;
			continue;
		}

		/* found BGP header */
		ND_TCHECK2(p[0], BGP_SIZE);	/*XXX*/
		memcpy(&bgp, p, BGP_SIZE);

		if (start != p)
			ND_PRINT((ndo, " [|BGP]"));

		hlen = ntohs(bgp.bgp_len);
		if (hlen < BGP_SIZE) {
			ND_PRINT((ndo, "\n[|BGP Bogus header length %u < %u]", hlen,
			    BGP_SIZE));
			break;
		}

		if (ND_TTEST2(p[0], hlen)) {
			if (!bgp_header_print(ndo, p, hlen))
				return;
			p += hlen;
			start = p;
		} else {
			ND_PRINT((ndo, "\n[|BGP %s]",
			       tok2str(bgp_msg_values,
					  "Unknown Message Type",
					  bgp.bgp_type)));
			break;
		}
	}

	return;

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

Project: tcpdump
File: print-bgp.c

bgp_route_refresh_print(netdissect_options *ndo,
                        const u_char *pptr, int len)
{
        const struct bgp_route_refresh *bgp_route_refresh_header;

	ND_TCHECK2(pptr[0], BGP_ROUTE_REFRESH_SIZE);

        /* some little sanity checking */
        if (len<BGP_ROUTE_REFRESH_SIZE)
            return;

        bgp_route_refresh_header = (const struct bgp_route_refresh *)pptr;

        ND_PRINT((ndo, "\n\t  AFI %s (%u), SAFI %s (%u)",
               tok2str(af_values,"Unknown",
			  /* this stinks but the compiler pads the structure
			   * weird */
			  EXTRACT_16BITS(&bgp_route_refresh_header->afi)),
               EXTRACT_16BITS(&bgp_route_refresh_header->afi),
               tok2str(bgp_safi_values,"Unknown",
			  bgp_route_refresh_header->safi),
               bgp_route_refresh_header->safi));

        if (ndo->ndo_vflag > 1) {
            ND_TCHECK2(*pptr, len);
            print_unknown_data(ndo, pptr, "\n\t  ", len);
        }

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

Project: tcpdump
File: print-bgp.c

bgp_update_print(netdissect_options *ndo,
                 const u_char *dat, int length)
{
	struct bgp bgp;
	const u_char *p;
	int withdrawn_routes_len;
	int len;
	int i;

	ND_TCHECK2(dat[0], BGP_SIZE);
	if (length < BGP_SIZE)
		goto trunc;
	memcpy(&bgp, dat, BGP_SIZE);
	p = dat + BGP_SIZE;	/*XXX*/
	length -= BGP_SIZE;

	/* Unfeasible routes */
	ND_TCHECK2(p[0], 2);
	if (length < 2)
		goto trunc;
	withdrawn_routes_len = EXTRACT_16BITS(p);
	p += 2;
	length -= 2;
	if (withdrawn_routes_len) {
		/*
		 * Without keeping state from the original NLRI message,
		 * it's not possible to tell if this a v4 or v6 route,
		 * so only try to decode it if we're not v6 enabled.
	         */
		ND_TCHECK2(p[0], withdrawn_routes_len);
		if (length < withdrawn_routes_len)
			goto trunc;
		ND_PRINT((ndo, "\n\t  Withdrawn routes: %d bytes", withdrawn_routes_len));
		p += withdrawn_routes_len;
		length -= withdrawn_routes_len;
	}

	ND_TCHECK2(p[0], 2);
	if (length < 2)
		goto trunc;
	len = EXTRACT_16BITS(p);
	p += 2;
	length -= 2;

        if (withdrawn_routes_len == 0 && len == 0 && length == 0) {
            /* No withdrawn routes, no path attributes, no NLRI */
            ND_PRINT((ndo, "\n\t  End-of-Rib Marker (empty NLRI)"));
            return;
        }

	if (len) {
		/* do something more useful!*/
		while (len) {
			int aflags, atype, alenlen, alen;

			ND_TCHECK2(p[0], 2);
			if (len < 2)
			    goto trunc;
			if (length < 2)
			    goto trunc;
			aflags = *p;
			atype = *(p + 1);
			p += 2;
			len -= 2;
			length -= 2;
			alenlen = bgp_attr_lenlen(aflags, p);
			ND_TCHECK2(p[0], alenlen);
			if (len < alenlen)
			    goto trunc;
			if (length < alenlen)
			    goto trunc;
			alen = bgp_attr_len(aflags, p);
			p += alenlen;
			len -= alenlen;
			length -= alenlen;

			ND_PRINT((ndo, "\n\t  %s (%u), length: %u",
                              tok2str(bgp_attr_values, "Unknown Attribute",
					 atype),
                              atype,
                              alen));

			if (aflags) {
				ND_PRINT((ndo, ", Flags [%s%s%s%s",
					aflags & 0x80 ? "O" : "",
					aflags & 0x40 ? "T" : "",
					aflags & 0x20 ? "P" : "",
					aflags & 0x10 ? "E" : ""));
				if (aflags & 0xf)
					ND_PRINT((ndo, "+%x", aflags & 0xf));
				ND_PRINT((ndo, "]: "));
			}
			if (len < alen)
				goto trunc;
			if (length < alen)
				goto trunc;
			if (!bgp_attr_print(ndo, atype, p, alen))
				goto trunc;
			p += alen;
			len -= alen;
			length -= alen;
		}
	}

	if (length) {
		/*
		 * XXX - what if they're using the "Advertisement of
		 * Multiple Paths in BGP" feature:
		 *
		 * https://datatracker.ietf.org/doc/draft-ietf-idr-add-paths/
		 *
		 * http://tools.ietf.org/html/draft-ietf-idr-add-paths-06
		 */
		ND_PRINT((ndo, "\n\t  Updated routes:"));
		while (length) {
			char buf[MAXHOSTNAMELEN + 100];
			i = decode_prefix4(ndo, p, length, buf, sizeof(buf));
			if (i == -1) {
				ND_PRINT((ndo, "\n\t    (illegal prefix length)"));
				break;
			} else if (i == -2)
				goto trunc;
			else if (i == -3)
				goto trunc; /* bytes left, but not enough */
			else {
				ND_PRINT((ndo, "\n\t    %s", buf));
				p += i;
				length -= i;
			}
		}
	}
	return;
trunc:
	ND_PRINT((ndo, "[|BGP]"));
}

Project: tcpdump
File: print-bgp.c

bgp_vpn_ip_print(netdissect_options *ndo,
                 const u_char *pptr, u_int addr_length)
{

    /* worst case string is s fully formatted v6 address */
    static char addr[sizeof("1234:5678:89ab:cdef:1234:5678:89ab:cdef")];
    char *pos = addr;

    switch(addr_length) {
    case (sizeof(struct in_addr) << 3): /* 32 */
        ND_TCHECK2(pptr[0], sizeof(struct in_addr));
        snprintf(pos, sizeof(addr), "%s", ipaddr_string(ndo, pptr));
        break;
    case (sizeof(struct in6_addr) << 3): /* 128 */
        ND_TCHECK2(pptr[0], sizeof(struct in6_addr));
        snprintf(pos, sizeof(addr), "%s", ip6addr_string(ndo, pptr));
        break;
    default:
        snprintf(pos, sizeof(addr), "bogus address length %u", addr_length);
        break;
    }
    pos += strlen(pos);

trunc:
    *(pos) = '\0';
    return (addr);
}

Project: tcpdump
File: print-bgp.c

bgp_vpn_rd_print(netdissect_options *ndo,
                 const u_char *pptr)
{
   /* allocate space for the largest possible string */
    static char rd[sizeof("xxxxxxxxxx:xxxxx (xxx.xxx.xxx.xxx:xxxxx)")];
    char *pos = rd;

    /* ok lets load the RD format */
    switch (EXTRACT_16BITS(pptr)) {

        /* 2-byte-AS:number fmt*/
    case 0:
        snprintf(pos, sizeof(rd) - (pos - rd), "%u:%u (= %u.%u.%u.%u)",
                 EXTRACT_16BITS(pptr+2),
                 EXTRACT_32BITS(pptr+4),
                 *(pptr+4), *(pptr+5), *(pptr+6), *(pptr+7));
        break;
        /* IP-address:AS fmt*/

    case 1:
        snprintf(pos, sizeof(rd) - (pos - rd), "%u.%u.%u.%u:%u",
            *(pptr+2), *(pptr+3), *(pptr+4), *(pptr+5), EXTRACT_16BITS(pptr+6));
        break;

        /* 4-byte-AS:number fmt*/
    case 2:
	snprintf(pos, sizeof(rd) - (pos - rd), "%s:%u (%u.%u.%u.%u:%u)",
	    as_printf(ndo, astostr, sizeof(astostr), EXTRACT_32BITS(pptr+2)),
	    EXTRACT_16BITS(pptr+6), *(pptr+2), *(pptr+3), *(pptr+4),
	    *(pptr+5), EXTRACT_16BITS(pptr+6));
        break;
    default:
        snprintf(pos, sizeof(rd) - (pos - rd), "unknown RD format");
        break;
    }
    pos += strlen(pos);
    *(pos) = '\0';
    return (rd);
}

Project: tcpdump
File: print-bgp.c

bgp_vpn_sg_print(netdissect_options *ndo,
                 const u_char *pptr, char *buf, u_int buflen)
{
    uint8_t addr_length;
    u_int total_length, offset;

    total_length = 0;

    /* Source address length, encoded in bits */
    ND_TCHECK2(pptr[0], 1);
    addr_length =  *pptr++;

    /* Source address */
    ND_TCHECK2(pptr[0], (addr_length >> 3));
    total_length += (addr_length >> 3) + 1;
    offset = strlen(buf);
    if (addr_length) {
        snprintf(buf + offset, buflen - offset, ", Source %s",
                 bgp_vpn_ip_print(ndo, pptr, addr_length));
        pptr += (addr_length >> 3);
    }

    /* Group address length, encoded in bits */
    ND_TCHECK2(pptr[0], 1);
    addr_length =  *pptr++;

    /* Group address */
    ND_TCHECK2(pptr[0], (addr_length >> 3));
    total_length += (addr_length >> 3) + 1;
    offset = strlen(buf);
    if (addr_length) {
        snprintf(buf + offset, buflen - offset, ", Group %s",
                 bgp_vpn_ip_print(ndo, pptr, addr_length));
        pptr += (addr_length >> 3);
    }

trunc:
    return (total_length);
}

Project: tcpdump
File: print-bgp.c

decode_clnp_prefix(netdissect_options *ndo,
                   const u_char *pptr, char *buf, u_int buflen)
{
        uint8_t addr[19];
	u_int plen;

	ND_TCHECK(pptr[0]);
	plen = pptr[0]; /* get prefix length */

	if (152 < plen)
		return -1;

	memset(&addr, 0, sizeof(addr));
	ND_TCHECK2(pptr[4], (plen + 7) / 8);
	memcpy(&addr, &pptr[4], (plen + 7) / 8);
	if (plen % 8) {
		addr[(plen + 7) / 8 - 1] &=
			((0xff00 >> (plen % 8)) & 0xff);
	}
	snprintf(buf, buflen, "%s/%d",
                 isonsap_string(ndo, addr,(plen + 7) / 8),
                 plen);

	return 1 + (plen + 7) / 8;

trunc:
	return -2;
}

Project: tcpdump
File: print-bgp.c

decode_labeled_prefix4(netdissect_options *ndo,
                       const u_char *pptr, u_int itemlen, char *buf, u_int buflen)
{
	struct in_addr addr;
	u_int plen, plenbytes;

	/* prefix length and label = 4 bytes */
	ND_TCHECK2(pptr[0], 4);
	ITEMCHECK(4);
	plen = pptr[0];   /* get prefix length */

        /* this is one of the weirdnesses of rfc3107
           the label length (actually the label + COS bits)
           is added to the prefix length;
           we also do only read out just one label -
           there is no real application for advertisement of
           stacked labels in a single BGP message
        */

	if (24 > plen)
		return -1;

        plen-=24; /* adjust prefixlen - labellength */

	if (32 < plen)
		return -1;
	itemlen -= 4;

	memset(&addr, 0, sizeof(addr));
	plenbytes = (plen + 7) / 8;
	ND_TCHECK2(pptr[4], plenbytes);
	ITEMCHECK(plenbytes);
	memcpy(&addr, &pptr[4], plenbytes);
	if (plen % 8) {
		((u_char *)&addr)[plenbytes - 1] &=
			((0xff00 >> (plen % 8)) & 0xff);
	}
        /* the label may get offsetted by 4 bits so lets shift it right */
	snprintf(buf, buflen, "%s/%d, label:%u %s",
                 ipaddr_string(ndo, &addr),
                 plen,
                 EXTRACT_24BITS(pptr+1)>>4,
                 ((pptr[3]&1)==0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );

	return 4 + plenbytes;

trunc:
	return -2;

badtlv:
	return -3;
}

Project: tcpdump
File: print-bgp.c

decode_labeled_prefix6(netdissect_options *ndo,
                       const u_char *pptr, u_int itemlen, char *buf, u_int buflen)
{
	struct in6_addr addr;
	u_int plen, plenbytes;

	/* prefix length and label = 4 bytes */
	ND_TCHECK2(pptr[0], 4);
	ITEMCHECK(4);
	plen = pptr[0]; /* get prefix length */

	if (24 > plen)
		return -1;

        plen-=24; /* adjust prefixlen - labellength */

	if (128 < plen)
		return -1;
	itemlen -= 4;

	memset(&addr, 0, sizeof(addr));
	plenbytes = (plen + 7) / 8;
	ND_TCHECK2(pptr[4], plenbytes);
	memcpy(&addr, &pptr[4], plenbytes);
	if (plen % 8) {
		addr.s6_addr[plenbytes - 1] &=
			((0xff00 >> (plen % 8)) & 0xff);
	}
        /* the label may get offsetted by 4 bits so lets shift it right */
	snprintf(buf, buflen, "%s/%d, label:%u %s",
                 ip6addr_string(ndo, &addr),
                 plen,
                 EXTRACT_24BITS(pptr+1)>>4,
                 ((pptr[3]&1)==0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );

	return 4 + plenbytes;

trunc:
	return -2;

badtlv:
	return -3;
}

Project: tcpdump
File: print-bgp.c

decode_labeled_vpn_clnp_prefix(netdissect_options *ndo,
                               const u_char *pptr, char *buf, u_int buflen)
{
        uint8_t addr[19];
	u_int plen;

	ND_TCHECK(pptr[0]);
	plen = pptr[0];   /* get prefix length */

	if ((24+64) > plen)
		return -1;

        plen-=(24+64); /* adjust prefixlen - labellength - RD len*/

	if (152 < plen)
		return -1;

	memset(&addr, 0, sizeof(addr));
	ND_TCHECK2(pptr[12], (plen + 7) / 8);
	memcpy(&addr, &pptr[12], (plen + 7) / 8);
	if (plen % 8) {
		addr[(plen + 7) / 8 - 1] &=
			((0xff00 >> (plen % 8)) & 0xff);
	}
        /* the label may get offsetted by 4 bits so lets shift it right */
	snprintf(buf, buflen, "RD: %s, %s/%d, label:%u %s",
                 bgp_vpn_rd_print(ndo, pptr+4),
                 isonsap_string(ndo, addr,(plen + 7) / 8),
                 plen,
                 EXTRACT_24BITS(pptr+1)>>4,
                 ((pptr[3]&1)==0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );

	return 12 + (plen + 7) / 8;

trunc:
	return -2;
}

Project: tcpdump
File: print-bgp.c

decode_labeled_vpn_prefix6(netdissect_options *ndo,
                           const u_char *pptr, char *buf, u_int buflen)
{
	struct in6_addr addr;
	u_int plen;

	ND_TCHECK(pptr[0]);
	plen = pptr[0];   /* get prefix length */

	if ((24+64) > plen)
		return -1;

        plen-=(24+64); /* adjust prefixlen - labellength - RD len*/

	if (128 < plen)
		return -1;

	memset(&addr, 0, sizeof(addr));
	ND_TCHECK2(pptr[12], (plen + 7) / 8);
	memcpy(&addr, &pptr[12], (plen + 7) / 8);
	if (plen % 8) {
		addr.s6_addr[(plen + 7) / 8 - 1] &=
			((0xff00 >> (plen % 8)) & 0xff);
	}
        /* the label may get offsetted by 4 bits so lets shift it right */
	snprintf(buf, buflen, "RD: %s, %s/%d, label:%u %s",
                 bgp_vpn_rd_print(ndo, pptr+4),
                 ip6addr_string(ndo, &addr),
                 plen,
                 EXTRACT_24BITS(pptr+1)>>4,
                 ((pptr[3]&1)==0) ? "(BOGUS: Bottom of Stack NOT set!)" : "(bottom)" );

	return 12 + (plen + 7) / 8;

trunc:
	return -2;
}

Project: tcpdump
File: print-bgp.c

decode_mdt_vpn_nlri(netdissect_options *ndo,
                    const u_char *pptr, char *buf, u_int buflen)
{

    const u_char *rd;
    const u_char *vpn_ip;

    ND_TCHECK(pptr[0]);

    /* if the NLRI is not predefined length, quit.*/
    if (*pptr != MDT_VPN_NLRI_LEN * 8)
	return -1;
    pptr++;

    /* RD */
    ND_TCHECK2(pptr[0], 8);
    rd = pptr;
    pptr+=8;

    /* IPv4 address */
    ND_TCHECK2(pptr[0], sizeof(struct in_addr));
    vpn_ip = pptr;
    pptr+=sizeof(struct in_addr);

    /* MDT Group Address */
    ND_TCHECK2(pptr[0], sizeof(struct in_addr));

    snprintf(buf, buflen, "RD: %s, VPN IP Address: %s, MC Group Address: %s",
	     bgp_vpn_rd_print(ndo, rd), ipaddr_string(ndo, vpn_ip), ipaddr_string(ndo, pptr));

    return MDT_VPN_NLRI_LEN + 1;

 trunc:

return -2;
}

Project: tcpdump
File: print-bgp.c

decode_multicast_vpn(netdissect_options *ndo,
                     const u_char *pptr, char *buf, u_int buflen)
{
        uint8_t route_type, route_length, addr_length, sg_length;
        u_int offset;

	ND_TCHECK2(pptr[0], 2);
        route_type = *pptr++;
        route_length = *pptr++;

        snprintf(buf, buflen, "Route-Type: %s (%u), length: %u",
                 tok2str(bgp_multicast_vpn_route_type_values,
                         "Unknown", route_type),
                 route_type, route_length);

        switch(route_type) {
        case BGP_MULTICAST_VPN_ROUTE_TYPE_INTRA_AS_I_PMSI:
            ND_TCHECK2(pptr[0], BGP_VPN_RD_LEN);
            offset = strlen(buf);
            snprintf(buf + offset, buflen - offset, ", RD: %s, Originator %s",
                     bgp_vpn_rd_print(ndo, pptr),
                     bgp_vpn_ip_print(ndo, pptr + BGP_VPN_RD_LEN,
                                      (route_length - BGP_VPN_RD_LEN) << 3));
            break;
        case BGP_MULTICAST_VPN_ROUTE_TYPE_INTER_AS_I_PMSI:
            ND_TCHECK2(pptr[0], BGP_VPN_RD_LEN + 4);
            offset = strlen(buf);
	    snprintf(buf + offset, buflen - offset, ", RD: %s, Source-AS %s",
		bgp_vpn_rd_print(ndo, pptr),
		as_printf(ndo, astostr, sizeof(astostr),
		EXTRACT_32BITS(pptr + BGP_VPN_RD_LEN)));
            break;

        case BGP_MULTICAST_VPN_ROUTE_TYPE_S_PMSI:
            ND_TCHECK2(pptr[0], BGP_VPN_RD_LEN);
            offset = strlen(buf);
            snprintf(buf + offset, buflen - offset, ", RD: %s",
                     bgp_vpn_rd_print(ndo, pptr));
            pptr += BGP_VPN_RD_LEN;

            sg_length = bgp_vpn_sg_print(ndo, pptr, buf, buflen);
            addr_length =  route_length - sg_length;

            ND_TCHECK2(pptr[0], addr_length);
            offset = strlen(buf);
            snprintf(buf + offset, buflen - offset, ", Originator %s",
                     bgp_vpn_ip_print(ndo, pptr, addr_length << 3));
            break;

        case BGP_MULTICAST_VPN_ROUTE_TYPE_SOURCE_ACTIVE:
            ND_TCHECK2(pptr[0], BGP_VPN_RD_LEN);
            offset = strlen(buf);
            snprintf(buf + offset, buflen - offset, ", RD: %s",
                     bgp_vpn_rd_print(ndo, pptr));
            pptr += BGP_VPN_RD_LEN;

            bgp_vpn_sg_print(ndo, pptr, buf, buflen);
            break;

        case BGP_MULTICAST_VPN_ROUTE_TYPE_SHARED_TREE_JOIN: /* fall through */
        case BGP_MULTICAST_VPN_ROUTE_TYPE_SOURCE_TREE_JOIN:
            ND_TCHECK2(pptr[0], BGP_VPN_RD_LEN + 4);
            offset = strlen(buf);
	    snprintf(buf + offset, buflen - offset, ", RD: %s, Source-AS %s",
		bgp_vpn_rd_print(ndo, pptr),
		as_printf(ndo, astostr, sizeof(astostr),
		EXTRACT_32BITS(pptr + BGP_VPN_RD_LEN)));
            pptr += BGP_VPN_RD_LEN + 4;

            bgp_vpn_sg_print(ndo, pptr, buf, buflen);
            break;

            /*
             * no per route-type printing yet.
             */
        case BGP_MULTICAST_VPN_ROUTE_TYPE_INTRA_AS_SEG_LEAF:
        default:
            break;
        }

        return route_length + 2;

trunc:
	return -2;
}