+ 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",
+ getname(ndo, (u_char *)&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;
+}
+
+/*
+ * bgp_vpn_ip_print
+ *
+ * print an ipv4 or ipv6 address into a buffer dependend on address length.
+ */
+static char *
+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);
+}
+
+/*
+ * bgp_vpn_sg_print
+ *
+ * print an multicast s,g entry into a buffer.
+ * the s,g entry is encoded like this.
+ *
+ * +-----------------------------------+
+ * | Multicast Source Length (1 octet) |
+ * +-----------------------------------+
+ * | Multicast Source (Variable) |
+ * +-----------------------------------+
+ * | Multicast Group Length (1 octet) |
+ * +-----------------------------------+
+ * | Multicast Group (Variable) |
+ * +-----------------------------------+
+ *
+ * return the number of bytes read from the wire.
+ */
+static int
+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);
+}
+
+
+/* RDs and RTs share the same semantics
+ * we use bgp_vpn_rd_print for
+ * printing route targets inside a NLRI */
+char *
+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);
+}
+
+static int
+decode_rt_routing_info(netdissect_options *ndo,
+ const u_char *pptr, char *buf, u_int buflen)
+{
+ uint8_t route_target[8];
+ u_int plen;
+
+ ND_TCHECK(pptr[0]);
+ plen = pptr[0]; /* get prefix length */
+
+ if (0 == plen) {
+ snprintf(buf, buflen, "default route target");
+ return 1;
+ }
+
+ if (32 > plen)
+ return -1;
+
+ plen-=32; /* adjust prefix length */
+
+ if (64 < plen)
+ return -1;
+
+ memset(&route_target, 0, sizeof(route_target));
+ ND_TCHECK2(pptr[1], (plen + 7) / 8);
+ memcpy(&route_target, &pptr[1], (plen + 7) / 8);
+ if (plen % 8) {
+ ((u_char *)&route_target)[(plen + 7) / 8 - 1] &=
+ ((0xff00 >> (plen % 8)) & 0xff);
+ }
+ snprintf(buf, buflen, "origin AS: %s, route target %s",
+ as_printf(ndo, astostr, sizeof(astostr), EXTRACT_32BITS(pptr+1)),
+ bgp_vpn_rd_print(ndo, (u_char *)&route_target));
+
+ return 5 + (plen + 7) / 8;
+
+trunc:
+ return -2;
+}
+
+static int
+decode_labeled_vpn_prefix4(netdissect_options *ndo,
+ const u_char *pptr, char *buf, u_int buflen)
+{
+ struct in_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 (32 < plen)
+ return -1;
+
+ memset(&addr, 0, sizeof(addr));
+ ND_TCHECK2(pptr[12], (plen + 7) / 8);
+ memcpy(&addr, &pptr[12], (plen + 7) / 8);