示例#1
0
static void
igrp_entry_print(netdissect_options *ndo, const struct igrprte *igr,
    int is_interior, int is_exterior)
{
	u_int delay, bandwidth;
	u_int metric, mtu;

	if (is_interior)
		ND_PRINT(" *.%u.%u.%u", igr->igr_net[0],
		    igr->igr_net[1], igr->igr_net[2]);
	else if (is_exterior)
		ND_PRINT(" X%u.%u.%u.0", igr->igr_net[0],
		    igr->igr_net[1], igr->igr_net[2]);
	else
		ND_PRINT(" %u.%u.%u.0", igr->igr_net[0],
		    igr->igr_net[1], igr->igr_net[2]);

	delay = EXTRACT_BE_U_3(igr->igr_dly);
	bandwidth = EXTRACT_BE_U_3(igr->igr_bw);
	metric = bandwidth + delay;
	if (metric > 0xffffff)
		metric = 0xffffff;
	mtu = EXTRACT_BE_U_2(igr->igr_mtu);

	ND_PRINT(" d=%u b=%u r=%u l=%u M=%u mtu=%u in %u hops",
	    10 * delay, bandwidth == 0 ? 0 : 10000000 / bandwidth,
	    EXTRACT_U_1(igr->igr_rel), EXTRACT_U_1(igr->igr_ld), metric,
	    mtu, EXTRACT_U_1(igr->igr_hct));
}
示例#2
0
void
vxlan_print(netdissect_options *ndo, const u_char *bp, u_int len)
{
    uint8_t flags;
    uint32_t vni;

    ndo->ndo_protocol = "vxlan";
    if (len < VXLAN_HDR_LEN)
        goto trunc;

    ND_TCHECK_LEN(bp, VXLAN_HDR_LEN);

    flags = EXTRACT_U_1(bp);
    bp += 4;

    vni = EXTRACT_BE_U_3(bp);
    bp += 4;

    ND_PRINT("VXLAN, ");
    ND_PRINT("flags [%s] (0x%02x), ", flags & 0x08 ? "I" : ".", flags);
    ND_PRINT("vni %u\n", vni);

    ether_print(ndo, bp, len - VXLAN_HDR_LEN, ndo->ndo_snapend - bp, NULL, NULL);

    return;

trunc:
    nd_print_trunc(ndo);
}
示例#3
0
static uint64_t dccp_seqno(const u_char *bp)
{
	const struct dccp_hdr *dh = (const struct dccp_hdr *)bp;
	uint64_t seqno;

	if (DCCPH_X(dh) != 0) {
		const struct dccp_hdr_ext *dhx = (const struct dccp_hdr_ext *)bp;
		seqno = EXTRACT_BE_U_6(dhx->dccph_seq);
	} else {
		seqno = EXTRACT_BE_U_3(dh->dccph_seq);
	}

	return seqno;
}
示例#4
0
static void dccp_print_ack_no(netdissect_options *ndo, const u_char *bp)
{
	const struct dccp_hdr *dh = (const struct dccp_hdr *)bp;
	const u_char *ackp = bp + dccp_basic_hdr_len(dh);
	uint64_t ackno;

	if (DCCPH_X(dh) != 0) {
		ND_TCHECK_8(ackp);
		ackno = EXTRACT_BE_U_6(ackp + 2);
	} else {
		ND_TCHECK_4(ackp);
		ackno = EXTRACT_BE_U_3(ackp + 1);
	}

	ND_PRINT("(ack=%" PRIu64 ") ", ackno);
trunc:
	return;
}
示例#5
0
int
snap_print(netdissect_options *ndo, const u_char *p, u_int length, u_int caplen,
	const struct lladdr_info *src, const struct lladdr_info *dst,
	u_int bridge_pad)
{
	uint32_t orgcode;
	u_short et;
	int ret;

	ND_TCHECK_5(p);
	if (caplen < 5 || length < 5)
		goto trunc;
	orgcode = EXTRACT_BE_U_3(p);
	et = EXTRACT_BE_U_2(p + 3);

	if (ndo->ndo_eflag) {
		/*
		 * Somebody's already printed the MAC addresses, if there
		 * are any, so just print the SNAP header, not the MAC
		 * addresses.
		 */
		ND_PRINT("oui %s (0x%06x), %s %s (0x%04x), length %u: ",
		     tok2str(oui_values, "Unknown", orgcode),
		     orgcode,
		     (orgcode == 0x000000 ? "ethertype" : "pid"),
		     tok2str(oui_to_struct_tok(orgcode), "Unknown", et),
		     et, length - 5);
	}
	p += 5;
	length -= 5;
	caplen -= 5;

	switch (orgcode) {
	case OUI_ENCAP_ETHER:
	case OUI_CISCO_90:
		/*
		 * This is an encapsulated Ethernet packet,
		 * or a packet bridged by some piece of
		 * Cisco hardware; the protocol ID is
		 * an Ethernet protocol type.
		 */
		ret = ethertype_print(ndo, et, p, length, caplen, src, dst);
		if (ret)
			return (ret);
		break;

	case OUI_APPLETALK:
		if (et == ETHERTYPE_ATALK) {
			/*
			 * No, I have no idea why Apple used one
			 * of their own OUIs, rather than
			 * 0x000000, and an Ethernet packet
			 * type, for Appletalk data packets,
			 * but used 0x000000 and an Ethernet
			 * packet type for AARP packets.
			 */
			ret = ethertype_print(ndo, et, p, length, caplen, src, dst);
			if (ret)
				return (ret);
		}
		break;

	case OUI_CISCO:
                switch (et) {
                case PID_CISCO_CDP:
                        cdp_print(ndo, p, length, caplen);
                        return (1);
                case PID_CISCO_DTP:
                        dtp_print(ndo, p, length);
                        return (1);
                case PID_CISCO_UDLD:
                        udld_print(ndo, p, length);
                        return (1);
                case PID_CISCO_VTP:
                        vtp_print(ndo, p, length);
                        return (1);
                case PID_CISCO_PVST:
                case PID_CISCO_VLANBRIDGE:
                        stp_print(ndo, p, length);
                        return (1);
                default:
                        break;
                }
		break;

	case OUI_RFC2684:
		switch (et) {

		case PID_RFC2684_ETH_FCS:
		case PID_RFC2684_ETH_NOFCS:
			/*
			 * XXX - remove the last two bytes for
			 * PID_RFC2684_ETH_FCS?
			 */
			/*
			 * Skip the padding.
			 */
			ND_TCHECK_LEN(p, bridge_pad);
			caplen -= bridge_pad;
			length -= bridge_pad;
			p += bridge_pad;

			/*
			 * What remains is an Ethernet packet.
			 */
			ether_print(ndo, p, length, caplen, NULL, NULL);
			return (1);

		case PID_RFC2684_802_5_FCS:
		case PID_RFC2684_802_5_NOFCS:
			/*
			 * XXX - remove the last two bytes for
			 * PID_RFC2684_ETH_FCS?
			 */
			/*
			 * Skip the padding, but not the Access
			 * Control field.
			 */
			ND_TCHECK_LEN(p, bridge_pad);
			caplen -= bridge_pad;
			length -= bridge_pad;
			p += bridge_pad;

			/*
			 * What remains is an 802.5 Token Ring
			 * packet.
			 */
			token_print(ndo, p, length, caplen);
			return (1);

		case PID_RFC2684_FDDI_FCS:
		case PID_RFC2684_FDDI_NOFCS:
			/*
			 * XXX - remove the last two bytes for
			 * PID_RFC2684_ETH_FCS?
			 */
			/*
			 * Skip the padding.
			 */
			ND_TCHECK_LEN(p, bridge_pad + 1);
			caplen -= bridge_pad + 1;
			length -= bridge_pad + 1;
			p += bridge_pad + 1;

			/*
			 * What remains is an FDDI packet.
			 */
			fddi_print(ndo, p, length, caplen);
			return (1);

		case PID_RFC2684_BPDU:
			stp_print(ndo, p, length);
			return (1);
		}
	}
	if (!ndo->ndo_eflag) {
		/*
		 * Nobody printed the link-layer addresses, so print them, if
		 * we have any.
		 */
		if (src != NULL && dst != NULL) {
			ND_PRINT("%s > %s ",
				(src->addr_string)(ndo, src->addr),
				(dst->addr_string)(ndo, dst->addr));
		}
		/*
		 * Print the SNAP header, but if the OUI is 000000, don't
		 * bother printing it, and report the PID as being an
		 * ethertype.
		 */
		if (orgcode == 0x000000) {
			ND_PRINT("SNAP, ethertype %s (0x%04x), length %u: ",
			     tok2str(ethertype_values, "Unknown", et),
			     et, length);
		} else {
			ND_PRINT("SNAP, oui %s (0x%06x), pid %s (0x%04x), length %u: ",
			     tok2str(oui_values, "Unknown", orgcode),
			     orgcode,
			     tok2str(oui_to_struct_tok(orgcode), "Unknown", et),
			     et, length);
		}
	}
	return (0);

trunc:
	ND_PRINT("[|snap]");
	return (1);
}
示例#6
0
static void
slow_oam_print(netdissect_options *ndo,
               const u_char *tptr, u_int tlen)
{
    uint8_t code;
    uint8_t type, length;
    uint8_t state;
    uint8_t command;
    u_int hexdump;

    struct slow_oam_common_header_t {
        nd_uint16_t flags;
        nd_uint8_t code;
    };

    struct slow_oam_tlv_header_t {
        nd_uint8_t type;
        nd_uint8_t length;
    };

    union {
        const struct slow_oam_common_header_t *slow_oam_common_header;
        const struct slow_oam_tlv_header_t *slow_oam_tlv_header;
    } ptr;

    union {
	const struct slow_oam_info_t *slow_oam_info;
        const struct slow_oam_link_event_t *slow_oam_link_event;
        const struct slow_oam_variablerequest_t *slow_oam_variablerequest;
        const struct slow_oam_variableresponse_t *slow_oam_variableresponse;
        const struct slow_oam_loopbackctrl_t *slow_oam_loopbackctrl;
    } tlv;

    ptr.slow_oam_common_header = (const struct slow_oam_common_header_t *)tptr;
    if (tlen < sizeof(*ptr.slow_oam_common_header))
        goto tooshort;
    ND_TCHECK_SIZE(ptr.slow_oam_common_header);
    tptr += sizeof(struct slow_oam_common_header_t);
    tlen -= sizeof(struct slow_oam_common_header_t);

    code = EXTRACT_U_1(ptr.slow_oam_common_header->code);
    ND_PRINT("\n\tCode %s OAM PDU, Flags [%s]",
           tok2str(slow_oam_code_values, "Unknown (%u)", code),
           bittok2str(slow_oam_flag_values,
                      "none",
                      EXTRACT_BE_U_2(ptr.slow_oam_common_header->flags)));

    switch (code) {
    case SLOW_OAM_CODE_INFO:
        while (tlen > 0) {
            ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
            if (tlen < sizeof(*ptr.slow_oam_tlv_header))
                goto tooshort;
            ND_TCHECK_SIZE(ptr.slow_oam_tlv_header);
            type = EXTRACT_U_1(ptr.slow_oam_tlv_header->type);
            length = EXTRACT_U_1(ptr.slow_oam_tlv_header->length);
            ND_PRINT("\n\t  %s Information Type (%u), length %u",
                   tok2str(slow_oam_info_type_values, "Reserved", type),
                   type,
                   length);

            if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
                /*
                 * As IEEE Std 802.3-2015 says for the End of TLV Marker,
                 * "(the length and value of the Type 0x00 TLV can be ignored)".
                 */
                return;
            }

            /* length includes the type and length fields */
            if (length < sizeof(struct slow_oam_tlv_header_t)) {
                ND_PRINT("\n\t    ERROR: illegal length - should be >= %u",
                       (u_int)sizeof(struct slow_oam_tlv_header_t));
                return;
            }

            if (tlen < length)
                goto tooshort;
            ND_TCHECK_LEN(tptr, length);

            hexdump = FALSE;
            switch (type) {
            case SLOW_OAM_INFO_TYPE_LOCAL: /* identical format - fall through */
            case SLOW_OAM_INFO_TYPE_REMOTE:
                tlv.slow_oam_info = (const struct slow_oam_info_t *)tptr;

                if (EXTRACT_U_1(tlv.slow_oam_info->info_length) !=
                    sizeof(struct slow_oam_info_t)) {
                    ND_PRINT("\n\t    ERROR: illegal length - should be %lu",
                           (unsigned long) sizeof(struct slow_oam_info_t));
                    hexdump = TRUE;
                    goto badlength_code_info;
                }

                ND_PRINT("\n\t    OAM-Version %u, Revision %u",
                       EXTRACT_U_1(tlv.slow_oam_info->oam_version),
                       EXTRACT_BE_U_2(tlv.slow_oam_info->revision));

                state = EXTRACT_U_1(tlv.slow_oam_info->state);
                ND_PRINT("\n\t    State-Parser-Action %s, State-MUX-Action %s",
                       tok2str(slow_oam_info_type_state_parser_values, "Reserved",
                               state & OAM_INFO_TYPE_PARSER_MASK),
                       tok2str(slow_oam_info_type_state_mux_values, "Reserved",
                               state & OAM_INFO_TYPE_MUX_MASK));
                ND_PRINT("\n\t    OAM-Config Flags [%s], OAM-PDU-Config max-PDU size %u",
                       bittok2str(slow_oam_info_type_oam_config_values, "none",
                                  EXTRACT_U_1(tlv.slow_oam_info->oam_config)),
                       EXTRACT_BE_U_2(tlv.slow_oam_info->oam_pdu_config) &
                       OAM_INFO_TYPE_PDU_SIZE_MASK);
                ND_PRINT("\n\t    OUI %s (0x%06x), Vendor-Private 0x%08x",
                       tok2str(oui_values, "Unknown",
                               EXTRACT_BE_U_3(tlv.slow_oam_info->oui)),
                       EXTRACT_BE_U_3(tlv.slow_oam_info->oui),
                       EXTRACT_BE_U_4(tlv.slow_oam_info->vendor_private));
                break;

            case SLOW_OAM_INFO_TYPE_ORG_SPECIFIC:
                hexdump = TRUE;
                break;

            default:
                hexdump = TRUE;
                break;
            }

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

            tlen -= length;
            tptr += length;
        }
        break;

    case SLOW_OAM_CODE_EVENT_NOTIF:
        /* Sequence number */
        if (tlen < 2)
            goto tooshort;
        ND_TCHECK_2(tptr);
        ND_PRINT("\n\t  Sequence Number %u", EXTRACT_BE_U_2(tptr));
        tlen -= 2;
        tptr += 2;

        /* TLVs */
        while (tlen > 0) {
            ptr.slow_oam_tlv_header = (const struct slow_oam_tlv_header_t *)tptr;
            if (tlen < sizeof(*ptr.slow_oam_tlv_header))
                goto tooshort;
            ND_TCHECK_SIZE(ptr.slow_oam_tlv_header);
            type = EXTRACT_U_1(ptr.slow_oam_tlv_header->type);
            length = EXTRACT_U_1(ptr.slow_oam_tlv_header->length);
            ND_PRINT("\n\t  %s Link Event Type (%u), length %u",
                   tok2str(slow_oam_link_event_values, "Reserved",
                           type),
                   type,
                   length);

            if (type == SLOW_OAM_INFO_TYPE_END_OF_TLV) {
                /*
                 * As IEEE Std 802.3-2015 says for the End of TLV Marker,
                 * "(the length and value of the Type 0x00 TLV can be ignored)".
                 */
                return;
            }

            /* length includes the type and length fields */
            if (length < sizeof(struct slow_oam_tlv_header_t)) {
                ND_PRINT("\n\t    ERROR: illegal length - should be >= %u",
                       (u_int)sizeof(struct slow_oam_tlv_header_t));
                return;
            }

            if (tlen < length)
                goto tooshort;
            ND_TCHECK_LEN(tptr, length);

            hexdump = FALSE;
            switch (type) {
            case SLOW_OAM_LINK_EVENT_ERR_SYM_PER: /* identical format - fall through */
            case SLOW_OAM_LINK_EVENT_ERR_FRM:
            case SLOW_OAM_LINK_EVENT_ERR_FRM_PER:
            case SLOW_OAM_LINK_EVENT_ERR_FRM_SUMM:
                tlv.slow_oam_link_event = (const struct slow_oam_link_event_t *)tptr;

                if (EXTRACT_U_1(tlv.slow_oam_link_event->event_length) !=
                    sizeof(struct slow_oam_link_event_t)) {
                    ND_PRINT("\n\t    ERROR: illegal length - should be %lu",
                           (unsigned long) sizeof(struct slow_oam_link_event_t));
                    hexdump = TRUE;
                    goto badlength_event_notif;
                }

                ND_PRINT("\n\t    Timestamp %u ms, Errored Window %" PRIu64
                       "\n\t    Errored Threshold %" PRIu64
                       "\n\t    Errors %" PRIu64
                       "\n\t    Error Running Total %" PRIu64
                       "\n\t    Event Running Total %u",
                       EXTRACT_BE_U_2(tlv.slow_oam_link_event->time_stamp)*100,
                       EXTRACT_BE_U_8(tlv.slow_oam_link_event->window),
                       EXTRACT_BE_U_8(tlv.slow_oam_link_event->threshold),
                       EXTRACT_BE_U_8(tlv.slow_oam_link_event->errors),
                       EXTRACT_BE_U_8(tlv.slow_oam_link_event->errors_running_total),
                       EXTRACT_BE_U_4(tlv.slow_oam_link_event->event_running_total));
                break;

            case SLOW_OAM_LINK_EVENT_ORG_SPECIFIC:
                hexdump = TRUE;
                break;

            default:
                hexdump = TRUE;
                break;
            }

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

            tlen -= length;
            tptr += length;
        }
        break;

    case SLOW_OAM_CODE_LOOPBACK_CTRL:
        tlv.slow_oam_loopbackctrl = (const struct slow_oam_loopbackctrl_t *)tptr;
        if (tlen < sizeof(*tlv.slow_oam_loopbackctrl))
            goto tooshort;
        ND_TCHECK_SIZE(tlv.slow_oam_loopbackctrl);
        command = EXTRACT_U_1(tlv.slow_oam_loopbackctrl->command);
        ND_PRINT("\n\t  Command %s (%u)",
               tok2str(slow_oam_loopbackctrl_cmd_values,
                       "Unknown",
                       command),
               command);
        tptr ++;
        tlen --;
        break;

        /*
         * FIXME those are the defined codes that lack a decoder
         * you are welcome to contribute code ;-)
         */
    case SLOW_OAM_CODE_VAR_REQUEST:
    case SLOW_OAM_CODE_VAR_RESPONSE:
    case SLOW_OAM_CODE_PRIVATE:
    default:
        if (ndo->ndo_vflag <= 1) {
            print_unknown_data(ndo, tptr, "\n\t  ", tlen);
        }
        break;
    }
    return;

tooshort:
    ND_PRINT("\n\t\t packet is too short");
    return;

trunc:
    ND_PRINT("%s", tstr);
}
示例#7
0
/* LCP config options */
static u_int
print_lcp_config_options(netdissect_options *ndo,
                         const u_char *p, u_int length)
{
	u_int opt, len;

	if (length < 2)
		return 0;
	ND_TCHECK_2(p);
	opt = EXTRACT_U_1(p);
	len = EXTRACT_U_1(p + 1);
	if (length < len)
		return 0;
	if (len < 2) {
		if (opt < NUM_LCPOPTS)
			ND_PRINT("\n\t  %s Option (0x%02x), length %u (length bogus, should be >= 2)",
			          lcpconfopts[opt], opt, len);
		else
			ND_PRINT("\n\tunknown LCP option 0x%02x", opt);
		return 0;
	}
	if (opt < NUM_LCPOPTS)
		ND_PRINT("\n\t  %s Option (0x%02x), length %u", lcpconfopts[opt], opt, len);
	else {
		ND_PRINT("\n\tunknown LCP option 0x%02x", opt);
		return len;
	}

	switch (opt) {
	case LCPOPT_VEXT:
		if (len < 6) {
			ND_PRINT(" (length bogus, should be >= 6)");
			return len;
		}
		ND_TCHECK_3(p + 2);
		ND_PRINT(": Vendor: %s (%u)",
			tok2str(oui_values,"Unknown",EXTRACT_BE_U_3(p + 2)),
			EXTRACT_BE_U_3(p + 2));
#if 0
		ND_TCHECK_1(p + 5);
		ND_PRINT(", kind: 0x%02x", EXTRACT_U_1(p + 5));
		ND_PRINT(", Value: 0x");
		for (i = 0; i < len - 6; i++) {
			ND_TCHECK_1(p + 6 + i);
			ND_PRINT("%02x", EXTRACT_U_1(p + 6 + i));
		}
#endif
		break;
	case LCPOPT_MRU:
		if (len != 4) {
			ND_PRINT(" (length bogus, should be = 4)");
			return len;
		}
		ND_TCHECK_2(p + 2);
		ND_PRINT(": %u", EXTRACT_BE_U_2(p + 2));
		break;
	case LCPOPT_ACCM:
		if (len != 6) {
			ND_PRINT(" (length bogus, should be = 6)");
			return len;
		}
		ND_TCHECK_4(p + 2);
		ND_PRINT(": 0x%08x", EXTRACT_BE_U_4(p + 2));
		break;
	case LCPOPT_AP:
		if (len < 4) {
			ND_PRINT(" (length bogus, should be >= 4)");
			return len;
		}
		ND_TCHECK_2(p + 2);
		ND_PRINT(": %s", tok2str(ppptype2str, "Unknown Auth Proto (0x04x)", EXTRACT_BE_U_2(p + 2)));

		switch (EXTRACT_BE_U_2(p + 2)) {
		case PPP_CHAP:
			ND_TCHECK_1(p + 4);
			ND_PRINT(", %s", tok2str(authalg_values, "Unknown Auth Alg %u", EXTRACT_U_1(p + 4)));
			break;
		case PPP_PAP: /* fall through */
		case PPP_EAP:
		case PPP_SPAP:
		case PPP_SPAP_OLD:
                        break;
		default:
			print_unknown_data(ndo, p, "\n\t", len);
		}
		break;
	case LCPOPT_QP:
		if (len < 4) {
			ND_PRINT(" (length bogus, should be >= 4)");
			return 0;
		}
		ND_TCHECK_2(p + 2);
		if (EXTRACT_BE_U_2(p + 2) == PPP_LQM)
			ND_PRINT(": LQR");
		else
			ND_PRINT(": unknown");
		break;
	case LCPOPT_MN:
		if (len != 6) {
			ND_PRINT(" (length bogus, should be = 6)");
			return 0;
		}
		ND_TCHECK_4(p + 2);
		ND_PRINT(": 0x%08x", EXTRACT_BE_U_4(p + 2));
		break;
	case LCPOPT_PFC:
		break;
	case LCPOPT_ACFC:
		break;
	case LCPOPT_LD:
		if (len != 4) {
			ND_PRINT(" (length bogus, should be = 4)");
			return 0;
		}
		ND_TCHECK_2(p + 2);
		ND_PRINT(": 0x%04x", EXTRACT_BE_U_2(p + 2));
		break;
	case LCPOPT_CBACK:
		if (len < 3) {
			ND_PRINT(" (length bogus, should be >= 3)");
			return 0;
		}
		ND_PRINT(": ");
		ND_TCHECK_1(p + 2);
		ND_PRINT(": Callback Operation %s (%u)",
                       tok2str(ppp_callback_values, "Unknown", EXTRACT_U_1(p + 2)),
                       EXTRACT_U_1(p + 2));
		break;
	case LCPOPT_MLMRRU:
		if (len != 4) {
			ND_PRINT(" (length bogus, should be = 4)");
			return 0;
		}
		ND_TCHECK_2(p + 2);
		ND_PRINT(": %u", EXTRACT_BE_U_2(p + 2));
		break;
	case LCPOPT_MLED:
		if (len < 3) {
			ND_PRINT(" (length bogus, should be >= 3)");
			return 0;
		}
		ND_TCHECK_1(p + 2);
		switch (EXTRACT_U_1(p + 2)) {		/* class */
		case MEDCLASS_NULL:
			ND_PRINT(": Null");
			break;
		case MEDCLASS_LOCAL:
			ND_PRINT(": Local"); /* XXX */
			break;
		case MEDCLASS_IPV4:
			if (len != 7) {
				ND_PRINT(" (length bogus, should be = 7)");
				return 0;
			}
			ND_TCHECK_4(p + 3);
			ND_PRINT(": IPv4 %s", ipaddr_string(ndo, p + 3));
			break;
		case MEDCLASS_MAC:
			if (len != 9) {
				ND_PRINT(" (length bogus, should be = 9)");
				return 0;
			}
			ND_TCHECK_6(p + 3);
			ND_PRINT(": MAC %s", etheraddr_string(ndo, p + 3));
			break;
		case MEDCLASS_MNB:
			ND_PRINT(": Magic-Num-Block"); /* XXX */
			break;
		case MEDCLASS_PSNDN:
			ND_PRINT(": PSNDN"); /* XXX */
			break;
		default:
			ND_PRINT(": Unknown class %u", EXTRACT_U_1(p + 2));
			break;
		}
		break;

/* XXX: to be supported */
#if 0
	case LCPOPT_DEP6:
	case LCPOPT_FCSALT:
	case LCPOPT_SDP:
	case LCPOPT_NUMMODE:
	case LCPOPT_DEP12:
	case LCPOPT_DEP14:
	case LCPOPT_DEP15:
	case LCPOPT_DEP16:
        case LCPOPT_MLSSNHF:
	case LCPOPT_PROP:
	case LCPOPT_DCEID:
	case LCPOPT_MPP:
	case LCPOPT_LCPAOPT:
	case LCPOPT_COBS:
	case LCPOPT_PE:
	case LCPOPT_MLHF:
	case LCPOPT_I18N:
	case LCPOPT_SDLOS:
	case LCPOPT_PPPMUX:
		break;
#endif
	default:
		/*
		 * Unknown option; dump it as raw bytes now if we're
		 * not going to do so below.
		 */
		if (ndo->ndo_vflag < 2)
			print_unknown_data(ndo, p + 2, "\n\t    ", len - 2);
		break;
	}

	if (ndo->ndo_vflag > 1)
		print_unknown_data(ndo, p + 2, "\n\t    ", len - 2); /* exclude TLV header */

	return len;

trunc:
	ND_PRINT("[|lcp]");
	return 0;
}
示例#8
0
/* generic Control Protocol (e.g. LCP, IPCP, CCP, etc.) handler */
static void
handle_ctrl_proto(netdissect_options *ndo,
                  u_int proto, const u_char *pptr, u_int length)
{
	const char *typestr;
	u_int code, len;
	u_int (*pfunc)(netdissect_options *, const u_char *, u_int);
	u_int x, j;
        const u_char *tptr;

        tptr=pptr;

        typestr = tok2str(ppptype2str, "unknown ctrl-proto (0x%04x)", proto);
	ND_PRINT("%s, ", typestr);

	if (length < 4) /* FIXME weak boundary checking */
		goto trunc;
	ND_TCHECK_2(tptr);

	code = EXTRACT_U_1(tptr);
	tptr++;

	ND_PRINT("%s (0x%02x), id %u, length %u",
	          tok2str(cpcodes, "Unknown Opcode",code),
	          code,
	          EXTRACT_U_1(tptr), /* ID */
	          length + 2);
	tptr++;

	if (!ndo->ndo_vflag)
		return;

	ND_TCHECK_2(tptr);
	len = EXTRACT_BE_U_2(tptr);
	tptr += 2;

	if (len < 4) {
		ND_PRINT("\n\tencoded length %u (< 4))", len);
		return;
	}

	if (len > length) {
		ND_PRINT("\n\tencoded length %u (> packet length %u))", len, length);
		return;
	}
	length = len;

	ND_PRINT("\n\tencoded length %u (=Option(s) length %u)", len, len - 4);

	if (length == 4)
		return;    /* there may be a NULL confreq etc. */

	if (ndo->ndo_vflag > 1)
		print_unknown_data(ndo, pptr - 2, "\n\t", 6);


	switch (code) {
	case CPCODES_VEXT:
		if (length < 11)
			break;
		ND_TCHECK_4(tptr);
		ND_PRINT("\n\t  Magic-Num 0x%08x", EXTRACT_BE_U_4(tptr));
		tptr += 4;
		ND_TCHECK_3(tptr);
		ND_PRINT(" Vendor: %s (%u)",
                       tok2str(oui_values,"Unknown",EXTRACT_BE_U_3(tptr)),
                       EXTRACT_BE_U_3(tptr));
		/* XXX: need to decode Kind and Value(s)? */
		break;
	case CPCODES_CONF_REQ:
	case CPCODES_CONF_ACK:
	case CPCODES_CONF_NAK:
	case CPCODES_CONF_REJ:
		x = len - 4;	/* Code(1), Identifier(1) and Length(2) */
		do {
			switch (proto) {
			case PPP_LCP:
				pfunc = print_lcp_config_options;
				break;
			case PPP_IPCP:
				pfunc = print_ipcp_config_options;
				break;
			case PPP_IPV6CP:
				pfunc = print_ip6cp_config_options;
				break;
			case PPP_CCP:
				pfunc = print_ccp_config_options;
				break;
			case PPP_BACP:
				pfunc = print_bacp_config_options;
				break;
			default:
				/*
				 * No print routine for the options for
				 * this protocol.
				 */
				pfunc = NULL;
				break;
			}

			if (pfunc == NULL) /* catch the above null pointer if unknown CP */
				break;

			if ((j = (*pfunc)(ndo, tptr, len)) == 0)
				break;
			x -= j;
			tptr += j;
		} while (x != 0);
		break;

	case CPCODES_TERM_REQ:
	case CPCODES_TERM_ACK:
		/* XXX: need to decode Data? */
		break;
	case CPCODES_CODE_REJ:
		/* XXX: need to decode Rejected-Packet? */
		break;
	case CPCODES_PROT_REJ:
		if (length < 6)
			break;
		ND_TCHECK_2(tptr);
		ND_PRINT("\n\t  Rejected %s Protocol (0x%04x)",
		       tok2str(ppptype2str,"unknown", EXTRACT_BE_U_2(tptr)),
		       EXTRACT_BE_U_2(tptr));
		/* XXX: need to decode Rejected-Information? - hexdump for now */
		if (len > 6) {
			ND_PRINT("\n\t  Rejected Packet");
			print_unknown_data(ndo, tptr + 2, "\n\t    ", len - 2);
		}
		break;
	case CPCODES_ECHO_REQ:
	case CPCODES_ECHO_RPL:
	case CPCODES_DISC_REQ:
		if (length < 8)
			break;
		ND_TCHECK_4(tptr);
		ND_PRINT("\n\t  Magic-Num 0x%08x", EXTRACT_BE_U_4(tptr));
		/* XXX: need to decode Data? - hexdump for now */
		if (len > 8) {
			ND_PRINT("\n\t  -----trailing data-----");
			ND_TCHECK_LEN(tptr + 4, len - 8);
			print_unknown_data(ndo, tptr + 4, "\n\t  ", len - 8);
		}
		break;
	case CPCODES_ID:
		if (length < 8)
			break;
		ND_TCHECK_4(tptr);
		ND_PRINT("\n\t  Magic-Num 0x%08x", EXTRACT_BE_U_4(tptr));
		/* RFC 1661 says this is intended to be human readable */
		if (len > 8) {
			ND_PRINT("\n\t  Message\n\t    ");
			if (fn_printn(ndo, tptr + 4, len - 4, ndo->ndo_snapend))
				goto trunc;
		}
		break;
	case CPCODES_TIME_REM:
		if (length < 12)
			break;
		ND_TCHECK_4(tptr);
		ND_PRINT("\n\t  Magic-Num 0x%08x", EXTRACT_BE_U_4(tptr));
		ND_TCHECK_4(tptr + 4);
		ND_PRINT(", Seconds-Remaining %us", EXTRACT_BE_U_4(tptr + 4));
		/* XXX: need to decode Message? */
		break;
	default:
		/* XXX this is dirty but we do not get the
		 * original pointer passed to the begin
		 * the PPP packet */
		if (ndo->ndo_vflag <= 1)
			print_unknown_data(ndo, pptr - 2, "\n\t  ", length + 2);
		break;
	}
	return;

trunc:
	ND_PRINT("[|%s]", typestr);
}
示例#9
0
static void
print_attr_num(netdissect_options *ndo,
               const u_char *data, u_int length, u_short attr_code)
{
   uint32_t timeout;

   if (length != 4)
   {
       ND_PRINT("ERROR: length %u != 4", length);
       return;
   }

   ND_TCHECK_4(data);
                          /* This attribute has standard values */
   if (attr_type[attr_code].siz_subtypes)
   {
      static const char **table;
      uint32_t data_value;
      table = attr_type[attr_code].subtypes;

      if ( (attr_code == TUNNEL_TYPE) || (attr_code == TUNNEL_MEDIUM) )
      {
         if (!EXTRACT_U_1(data))
            ND_PRINT("Tag[Unused] ");
         else
            ND_PRINT("Tag[%u] ", EXTRACT_U_1(data));
         data++;
         data_value = EXTRACT_BE_U_3(data);
      }
      else
      {
         data_value = EXTRACT_BE_U_4(data);
      }
      if ( data_value <= (uint32_t)(attr_type[attr_code].siz_subtypes - 1 +
            attr_type[attr_code].first_subtype) &&
	   data_value >= attr_type[attr_code].first_subtype )
         ND_PRINT("%s", table[data_value]);
      else
         ND_PRINT("#%u", data_value);
   }
   else
   {
      switch(attr_code) /* Be aware of special cases... */
      {
        case FRM_IPX:
             if (EXTRACT_BE_U_4(data) == 0xFFFFFFFE )
                ND_PRINT("NAS Select");
             else
                ND_PRINT("%u", EXTRACT_BE_U_4(data));
          break;

        case SESSION_TIMEOUT:
        case IDLE_TIMEOUT:
        case ACCT_DELAY:
        case ACCT_SESSION_TIME:
        case ACCT_INT_INTERVAL:
             timeout = EXTRACT_BE_U_4(data);
             if ( timeout < 60 )
                ND_PRINT("%02d secs", timeout);
             else
             {
                if ( timeout < 3600 )
                   ND_PRINT("%02d:%02d min",
                          timeout / 60, timeout % 60);
                else
                   ND_PRINT("%02d:%02d:%02d hours",
                          timeout / 3600, (timeout % 3600) / 60,
                          timeout % 60);
             }
          break;

        case FRM_ATALK_LINK:
             if (EXTRACT_BE_U_4(data))
                ND_PRINT("%u", EXTRACT_BE_U_4(data));
             else
                ND_PRINT("Unnumbered");
          break;

        case FRM_ATALK_NETWORK:
             if (EXTRACT_BE_U_4(data))
                ND_PRINT("%u", EXTRACT_BE_U_4(data));
             else
                ND_PRINT("NAS assigned");
          break;

        case TUNNEL_PREFERENCE:
            if (EXTRACT_U_1(data))
               ND_PRINT("Tag[%u] ", EXTRACT_U_1(data));
            else
               ND_PRINT("Tag[Unused] ");
            data++;
            ND_PRINT("%u", EXTRACT_BE_U_3(data));
          break;

        case EGRESS_VLAN_ID:
            ND_PRINT("%s (0x%02x) ",
                   tok2str(rfc4675_tagged,"Unknown tag",EXTRACT_U_1(data)),
                   EXTRACT_U_1(data));
            data++;
            ND_PRINT("%u", EXTRACT_BE_U_3(data));
          break;

        default:
             ND_PRINT("%u", EXTRACT_BE_U_4(data));
          break;

      } /* switch */

   } /* if-else */

   return;

   trunc:
     nd_print_trunc(ndo);
}
示例#10
0
void
geneve_print(netdissect_options *ndo, const u_char *bp, u_int len)
{
    uint8_t ver_opt;
    u_int version;
    uint8_t flags;
    uint16_t prot;
    uint32_t vni;
    uint8_t reserved;
    u_int opts_len;

    ndo->ndo_protocol = "geneve";
    ND_PRINT("Geneve");

    ND_TCHECK_8(bp);

    ver_opt = EXTRACT_U_1(bp);
    bp += 1;
    len -= 1;

    version = ver_opt >> VER_SHIFT;
    if (version != 0) {
        ND_PRINT(" ERROR: unknown-version %u", version);
        return;
    }

    flags = EXTRACT_U_1(bp);
    bp += 1;
    len -= 1;

    prot = EXTRACT_BE_U_2(bp);
    bp += 2;
    len -= 2;

    vni = EXTRACT_BE_U_3(bp);
    bp += 3;
    len -= 3;

    reserved = EXTRACT_U_1(bp);
    bp += 1;
    len -= 1;

    ND_PRINT(", Flags [%s]",
              bittok2str_nosep(geneve_flag_values, "none", flags));
    ND_PRINT(", vni 0x%x", vni);

    if (reserved)
        ND_PRINT(", rsvd 0x%x", reserved);

    if (ndo->ndo_eflag)
        ND_PRINT(", proto %s (0x%04x)",
                  tok2str(ethertype_values, "unknown", prot), prot);

    opts_len = (ver_opt & HDR_OPTS_LEN_MASK) * 4;

    if (len < opts_len) {
        ND_PRINT(" truncated-geneve - %u bytes missing",
                  opts_len - len);
        return;
    }

    ND_TCHECK_LEN(bp, opts_len);

    if (opts_len > 0) {
        ND_PRINT(", options [");

        if (ndo->ndo_vflag)
            geneve_opts_print(ndo, bp, opts_len);
        else
            ND_PRINT("%u bytes", opts_len);

        ND_PRINT("]");
    }

    bp += opts_len;
    len -= opts_len;

    if (ndo->ndo_vflag < 1)
        ND_PRINT(": ");
    else
        ND_PRINT("\n\t");

    if (ethertype_print(ndo, prot, bp, len, ndo->ndo_snapend - bp, NULL, NULL) == 0) {
        if (prot == ETHERTYPE_TEB)
            ether_print(ndo, bp, len, ndo->ndo_snapend - bp, NULL, NULL);
        else
            ND_PRINT("geneve-proto-0x%x", prot);
    }

    return;

trunc:
    ND_PRINT(" [|geneve]");
}
示例#11
0
void
cfm_print(netdissect_options *ndo,
          const u_char *pptr, u_int length)
{
    const struct cfm_common_header_t *cfm_common_header;
    uint8_t mdlevel_version, opcode, flags, first_tlv_offset;
    const struct cfm_tlv_header_t *cfm_tlv_header;
    const uint8_t *tptr, *tlv_ptr;
    const uint8_t *namesp;
    u_int names_data_remaining;
    uint8_t md_nameformat, md_namelength;
    const uint8_t *md_name;
    uint8_t ma_nameformat, ma_namelength;
    const uint8_t *ma_name;
    u_int hexdump, tlen, cfm_tlv_len, cfm_tlv_type, ccm_interval;


    union {
        const struct cfm_ccm_t *cfm_ccm;
        const struct cfm_lbm_t *cfm_lbm;
        const struct cfm_ltm_t *cfm_ltm;
        const struct cfm_ltr_t *cfm_ltr;
    } msg_ptr;

    tptr=pptr;
    cfm_common_header = (const struct cfm_common_header_t *)pptr;
    if (length < sizeof(*cfm_common_header))
        goto tooshort;
    ND_TCHECK_SIZE(cfm_common_header);

    /*
     * Sanity checking of the header.
     */
    mdlevel_version = EXTRACT_U_1(cfm_common_header->mdlevel_version);
    if (CFM_EXTRACT_VERSION(mdlevel_version) != CFM_VERSION) {
	ND_PRINT("CFMv%u not supported, length %u",
               CFM_EXTRACT_VERSION(mdlevel_version), length);
	return;
    }

    opcode = EXTRACT_U_1(cfm_common_header->opcode);
    ND_PRINT("CFMv%u %s, MD Level %u, length %u",
           CFM_EXTRACT_VERSION(mdlevel_version),
           tok2str(cfm_opcode_values, "unknown (%u)", opcode),
           CFM_EXTRACT_MD_LEVEL(mdlevel_version),
           length);

    /*
     * In non-verbose mode just print the opcode and md-level.
     */
    if (ndo->ndo_vflag < 1) {
        return;
    }

    flags = EXTRACT_U_1(cfm_common_header->flags);
    first_tlv_offset = EXTRACT_U_1(cfm_common_header->first_tlv_offset);
    ND_PRINT("\n\tFirst TLV offset %u", first_tlv_offset);

    tptr += sizeof(struct cfm_common_header_t);
    tlen = length - sizeof(struct cfm_common_header_t);

    /*
     * Sanity check the first TLV offset.
     */
    if (first_tlv_offset > tlen) {
        ND_PRINT(" (too large, must be <= %u)", tlen);
        return;
    }

    switch (opcode) {
    case CFM_OPCODE_CCM:
        msg_ptr.cfm_ccm = (const struct cfm_ccm_t *)tptr;
        if (first_tlv_offset < sizeof(*msg_ptr.cfm_ccm)) {
            ND_PRINT(" (too small 1, must be >= %lu)",
                     (unsigned long) sizeof(*msg_ptr.cfm_ccm));
            return;
        }
        if (tlen < sizeof(*msg_ptr.cfm_ccm))
            goto tooshort;
        ND_TCHECK_SIZE(msg_ptr.cfm_ccm);

        ccm_interval = CFM_EXTRACT_CCM_INTERVAL(flags);
        ND_PRINT(", Flags [CCM Interval %u%s]",
               ccm_interval,
               flags & CFM_CCM_RDI_FLAG ?
               ", RDI" : "");

        /*
         * Resolve the CCM interval field.
         */
        if (ccm_interval) {
            ND_PRINT("\n\t  CCM Interval %.3fs"
                   ", min CCM Lifetime %.3fs, max CCM Lifetime %.3fs",
                   ccm_interval_base[ccm_interval],
                   ccm_interval_base[ccm_interval] * CCM_INTERVAL_MIN_MULTIPLIER,
                   ccm_interval_base[ccm_interval] * CCM_INTERVAL_MAX_MULTIPLIER);
        }

        ND_PRINT("\n\t  Sequence Number 0x%08x, MA-End-Point-ID 0x%04x",
               EXTRACT_BE_U_4(msg_ptr.cfm_ccm->sequence),
               EXTRACT_BE_U_2(msg_ptr.cfm_ccm->ma_epi));

        namesp = msg_ptr.cfm_ccm->names;
        names_data_remaining = sizeof(msg_ptr.cfm_ccm->names);

        /*
         * Resolve the MD fields.
         */
        md_nameformat = EXTRACT_U_1(namesp);
        namesp++;
        names_data_remaining--;  /* We know this is != 0 */
        if (md_nameformat != CFM_CCM_MD_FORMAT_NONE) {
            md_namelength = EXTRACT_U_1(namesp);
            namesp++;
            names_data_remaining--; /* We know this is !=0 */
            ND_PRINT("\n\t  MD Name Format %s (%u), MD Name length %u",
                   tok2str(cfm_md_nameformat_values, "Unknown",
                           md_nameformat),
                   md_nameformat,
                   md_namelength);

            /*
             * -3 for the MA short name format and length and one byte
             * of MA short name.
             */
            if (md_namelength > names_data_remaining - 3) {
                ND_PRINT(" (too large, must be <= %u)", names_data_remaining - 2);
                return;
            }

            md_name = namesp;
            ND_PRINT("\n\t  MD Name: ");
            switch (md_nameformat) {
            case CFM_CCM_MD_FORMAT_DNS:
            case CFM_CCM_MD_FORMAT_CHAR:
                safeputs(ndo, md_name, md_namelength);
                break;

            case CFM_CCM_MD_FORMAT_MAC:
                if (md_namelength == 6) {
                    ND_PRINT("\n\t  MAC %s", etheraddr_string(ndo,
                               md_name));
                } else {
                    ND_PRINT("\n\t  MAC (length invalid)");
                }
                break;

                /* FIXME add printers for those MD formats - hexdump for now */
            case CFM_CCM_MA_FORMAT_8021:
            default:
                print_unknown_data(ndo, md_name, "\n\t    ",
                                   md_namelength);
            }
            namesp += md_namelength;
            names_data_remaining -= md_namelength;
        } else {
            ND_PRINT("\n\t  MD Name Format %s (%u)",
                   tok2str(cfm_md_nameformat_values, "Unknown",
                           md_nameformat),
                   md_nameformat);
        }


        /*
         * Resolve the MA fields.
         */
        ma_nameformat = EXTRACT_U_1(namesp);
        namesp++;
        names_data_remaining--; /* We know this is != 0 */
        ma_namelength = EXTRACT_U_1(namesp);
        namesp++;
        names_data_remaining--; /* We know this is != 0 */
        ND_PRINT("\n\t  MA Name-Format %s (%u), MA name length %u",
               tok2str(cfm_ma_nameformat_values, "Unknown",
                       ma_nameformat),
               ma_nameformat,
               ma_namelength);

        if (ma_namelength > names_data_remaining) {
            ND_PRINT(" (too large, must be <= %u)", names_data_remaining);
            return;
        }

        ma_name = namesp;
        ND_PRINT("\n\t  MA Name: ");
        switch (ma_nameformat) {
        case CFM_CCM_MA_FORMAT_CHAR:
            safeputs(ndo, ma_name, ma_namelength);
            break;

            /* FIXME add printers for those MA formats - hexdump for now */
        case CFM_CCM_MA_FORMAT_8021:
        case CFM_CCM_MA_FORMAT_VID:
        case CFM_CCM_MA_FORMAT_INT:
        case CFM_CCM_MA_FORMAT_VPN:
        default:
            print_unknown_data(ndo, ma_name, "\n\t    ", ma_namelength);
        }
        break;

    case CFM_OPCODE_LTM:
        msg_ptr.cfm_ltm = (const struct cfm_ltm_t *)tptr;
        if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltm)) {
            ND_PRINT(" (too small 4, must be >= %lu)",
                     (unsigned long) sizeof(*msg_ptr.cfm_ltm));
            return;
        }
        if (tlen < sizeof(*msg_ptr.cfm_ltm))
            goto tooshort;
        ND_TCHECK_SIZE(msg_ptr.cfm_ltm);

        ND_PRINT(", Flags [%s]",
               bittok2str(cfm_ltm_flag_values, "none", flags));

        ND_PRINT("\n\t  Transaction-ID 0x%08x, ttl %u",
               EXTRACT_BE_U_4(msg_ptr.cfm_ltm->transaction_id),
               EXTRACT_U_1(msg_ptr.cfm_ltm->ttl));

        ND_PRINT("\n\t  Original-MAC %s, Target-MAC %s",
               etheraddr_string(ndo, msg_ptr.cfm_ltm->original_mac),
               etheraddr_string(ndo, msg_ptr.cfm_ltm->target_mac));
        break;

    case CFM_OPCODE_LTR:
        msg_ptr.cfm_ltr = (const struct cfm_ltr_t *)tptr;
        if (first_tlv_offset < sizeof(*msg_ptr.cfm_ltr)) {
            ND_PRINT(" (too small 5, must be >= %lu)",
                     (unsigned long) sizeof(*msg_ptr.cfm_ltr));
            return;
        }
        if (tlen < sizeof(*msg_ptr.cfm_ltr))
            goto tooshort;
        ND_TCHECK_SIZE(msg_ptr.cfm_ltr);

        ND_PRINT(", Flags [%s]",
               bittok2str(cfm_ltr_flag_values, "none", flags));

        ND_PRINT("\n\t  Transaction-ID 0x%08x, ttl %u",
               EXTRACT_BE_U_4(msg_ptr.cfm_ltr->transaction_id),
               EXTRACT_U_1(msg_ptr.cfm_ltr->ttl));

        ND_PRINT("\n\t  Replay-Action %s (%u)",
               tok2str(cfm_ltr_replay_action_values,
                       "Unknown",
                       EXTRACT_U_1(msg_ptr.cfm_ltr->replay_action)),
               EXTRACT_U_1(msg_ptr.cfm_ltr->replay_action));
        break;

        /*
         * No message decoder yet.
         * Hexdump everything up until the start of the TLVs
         */
    case CFM_OPCODE_LBR:
    case CFM_OPCODE_LBM:
    default:
        print_unknown_data(ndo, tptr, "\n\t  ",
                           tlen -  first_tlv_offset);
        break;
    }

    tptr += first_tlv_offset;
    tlen -= first_tlv_offset;

    while (tlen > 0) {
        cfm_tlv_header = (const struct cfm_tlv_header_t *)tptr;

        /* Enough to read the tlv type ? */
        ND_TCHECK_1(cfm_tlv_header->type);
        cfm_tlv_type = EXTRACT_U_1(cfm_tlv_header->type);

        ND_PRINT("\n\t%s TLV (0x%02x)",
               tok2str(cfm_tlv_values, "Unknown", cfm_tlv_type),
               cfm_tlv_type);

        if (cfm_tlv_type == CFM_TLV_END) {
            /* Length is "Not present if the Type field is 0." */
            return;
        }

        /* do we have the full tlv header ? */
        if (tlen < sizeof(struct cfm_tlv_header_t))
            goto tooshort;
        ND_TCHECK_LEN(tptr, sizeof(struct cfm_tlv_header_t));
        cfm_tlv_len=EXTRACT_BE_U_2(cfm_tlv_header->length);

        ND_PRINT(", length %u", cfm_tlv_len);

        tptr += sizeof(struct cfm_tlv_header_t);
        tlen -= sizeof(struct cfm_tlv_header_t);
        tlv_ptr = tptr;

        /* do we have the full tlv ? */
        if (tlen < cfm_tlv_len)
            goto tooshort;
        ND_TCHECK_LEN(tptr, cfm_tlv_len);
        hexdump = FALSE;

        switch(cfm_tlv_type) {
        case CFM_TLV_PORT_STATUS:
            if (cfm_tlv_len < 1) {
                ND_PRINT(" (too short, must be >= 1)");
                return;
            }
            ND_PRINT(", Status: %s (%u)",
                   tok2str(cfm_tlv_port_status_values, "Unknown", EXTRACT_U_1(tptr)),
                   EXTRACT_U_1(tptr));
            break;

        case CFM_TLV_INTERFACE_STATUS:
            if (cfm_tlv_len < 1) {
                ND_PRINT(" (too short, must be >= 1)");
                return;
            }
            ND_PRINT(", Status: %s (%u)",
                   tok2str(cfm_tlv_interface_status_values, "Unknown", EXTRACT_U_1(tptr)),
                   EXTRACT_U_1(tptr));
            break;

        case CFM_TLV_PRIVATE:
            if (cfm_tlv_len < 4) {
                ND_PRINT(" (too short, must be >= 4)");
                return;
            }
            ND_PRINT(", Vendor: %s (%u), Sub-Type %u",
                   tok2str(oui_values,"Unknown", EXTRACT_BE_U_3(tptr)),
                   EXTRACT_BE_U_3(tptr),
                   EXTRACT_U_1(tptr + 3));
            hexdump = TRUE;
            break;

        case CFM_TLV_SENDER_ID:
        {
            u_int chassis_id_type, chassis_id_length;
            u_int mgmt_addr_length;

            if (cfm_tlv_len < 1) {
                ND_PRINT(" (too short, must be >= 1)");
                goto next_tlv;
            }

            /*
             * Get the Chassis ID length and check it.
             * IEEE 802.1Q-2014 Section 21.5.3.1
             */
            chassis_id_length = EXTRACT_U_1(tptr);
            tptr++;
            tlen--;
            cfm_tlv_len--;

            if (chassis_id_length) {
                /*
                 * IEEE 802.1Q-2014 Section 21.5.3.2: Chassis ID Subtype, references
                 * IEEE 802.1AB-2005 Section 9.5.2.2, subsequently
                 * IEEE 802.1AB-2016 Section 8.5.2.2: chassis ID subtype
                 */
                if (cfm_tlv_len < 1) {
                    ND_PRINT("\n\t  (TLV too short)");
                    goto next_tlv;
                }
                chassis_id_type = EXTRACT_U_1(tptr);
                cfm_tlv_len--;
                ND_PRINT("\n\t  Chassis-ID Type %s (%u), Chassis-ID length %u",
                       tok2str(cfm_tlv_senderid_chassisid_values,
                               "Unknown",
                               chassis_id_type),
                       chassis_id_type,
                       chassis_id_length);

                if (cfm_tlv_len < chassis_id_length) {
                    ND_PRINT("\n\t  (TLV too short)");
                    goto next_tlv;
                }

                /* IEEE 802.1Q-2014 Section 21.5.3.3: Chassis ID */
                switch (chassis_id_type) {
                case CFM_CHASSIS_ID_MAC_ADDRESS:
                    if (chassis_id_length != MAC_ADDR_LEN) {
                        ND_PRINT(" (invalid MAC address length)");
                        hexdump = TRUE;
                        break;
                    }
                    ND_PRINT("\n\t  MAC %s", etheraddr_string(ndo, tptr + 1));
                    break;

                case CFM_CHASSIS_ID_NETWORK_ADDRESS:
                    hexdump |= cfm_network_addr_print(ndo, tptr + 1, chassis_id_length);
                    break;

                case CFM_CHASSIS_ID_INTERFACE_NAME: /* fall through */
                case CFM_CHASSIS_ID_INTERFACE_ALIAS:
                case CFM_CHASSIS_ID_LOCAL:
                case CFM_CHASSIS_ID_CHASSIS_COMPONENT:
                case CFM_CHASSIS_ID_PORT_COMPONENT:
                    safeputs(ndo, tptr + 1, chassis_id_length);
                    break;

                default:
                    hexdump = TRUE;
                    break;
                }
                cfm_tlv_len -= chassis_id_length;

                tptr += 1 + chassis_id_length;
                tlen -= 1 + chassis_id_length;
            }

            /*
             * Check if there is a Management Address.
             * IEEE 802.1Q-2014 Section 21.5.3.4: Management Address Domain Length
             * This and all subsequent fields are not present if the TLV length
             * allows only the above fields.
             */
            if (cfm_tlv_len == 0) {
                /* No, there isn't; we're done. */
                break;
            }

            /* Here mgmt_addr_length stands for the management domain length. */
            mgmt_addr_length = EXTRACT_U_1(tptr);
            tptr++;
            tlen--;
            cfm_tlv_len--;
            ND_PRINT("\n\t  Management Address Domain Length %u", mgmt_addr_length);
            if (mgmt_addr_length) {
                /* IEEE 802.1Q-2014 Section 21.5.3.5: Management Address Domain */
                if (cfm_tlv_len < mgmt_addr_length) {
                    ND_PRINT("\n\t  (TLV too short)");
                    goto next_tlv;
                }
                cfm_tlv_len -= mgmt_addr_length;
                /*
                 * XXX - this is an OID; print it as such.
                 */
                hex_print(ndo, "\n\t  Management Address Domain: ", tptr, mgmt_addr_length);
                tptr += mgmt_addr_length;
                tlen -= mgmt_addr_length;

                /*
                 * IEEE 802.1Q-2014 Section 21.5.3.6: Management Address Length
                 * This field is present if Management Address Domain Length is not 0.
                 */
                if (cfm_tlv_len < 1) {
                    ND_PRINT(" (Management Address Length is missing)");
                    hexdump = TRUE;
                    break;
                }

                /* Here mgmt_addr_length stands for the management address length. */
                mgmt_addr_length = EXTRACT_U_1(tptr);
                tptr++;
                tlen--;
                cfm_tlv_len--;
                ND_PRINT("\n\t  Management Address Length %u", mgmt_addr_length);
                if (mgmt_addr_length) {
                    /* IEEE 802.1Q-2014 Section 21.5.3.7: Management Address */
                    if (cfm_tlv_len < mgmt_addr_length) {
                        ND_PRINT("\n\t  (TLV too short)");
                        return;
                    }
                    cfm_tlv_len -= mgmt_addr_length;
                    /*
                     * XXX - this is a TransportDomain; print it as such.
                     */
                    hex_print(ndo, "\n\t  Management Address: ", tptr, mgmt_addr_length);
                    tptr += mgmt_addr_length;
                    tlen -= mgmt_addr_length;
                }
            }
            break;
        }

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

        case CFM_TLV_DATA:
        case CFM_TLV_REPLY_INGRESS:
        case CFM_TLV_REPLY_EGRESS:
        default:
            hexdump = TRUE;
            break;
        }
        /* do we want to see an additional hexdump ? */
        if (hexdump || ndo->ndo_vflag > 1)
            print_unknown_data(ndo, tlv_ptr, "\n\t  ", cfm_tlv_len);

next_tlv:
        tptr+=cfm_tlv_len;
        tlen-=cfm_tlv_len;
    }
    return;

tooshort:
    ND_PRINT("\n\t\t packet is too short");
    return;

trunc:
    ND_PRINT("%s", tstr);
}