static void slow_marker_lacp_print(netdissect_options *ndo, const u_char *tptr, u_int tlen, u_int proto_subtype) { const struct tlv_header_t *tlv_header; const u_char *tlv_tptr; u_int tlv_type, tlv_len, tlv_tlen; union { const struct lacp_marker_tlv_terminator_t *lacp_marker_tlv_terminator; const struct lacp_tlv_actor_partner_info_t *lacp_tlv_actor_partner_info; const struct lacp_tlv_collector_info_t *lacp_tlv_collector_info; const struct marker_tlv_marker_info_t *marker_tlv_marker_info; } tlv_ptr; while(tlen>0) { /* is the packet big enough to include the tlv header ? */ if (tlen < sizeof(struct tlv_header_t)) goto tooshort; /* did we capture enough for fully decoding the tlv header ? */ ND_TCHECK_LEN(tptr, sizeof(struct tlv_header_t)); tlv_header = (const struct tlv_header_t *)tptr; tlv_type = EXTRACT_U_1(tlv_header->type); tlv_len = EXTRACT_U_1(tlv_header->length); ND_PRINT("\n\t%s TLV (0x%02x), length %u", tok2str(slow_tlv_values, "Unknown", (proto_subtype << 8) + tlv_type), tlv_type, tlv_len); if (tlv_type == LACP_MARKER_TLV_TERMINATOR) { /* * This TLV has a length of zero, and means there are no * more TLVs to process. */ return; } /* length includes the type and length fields */ if (tlv_len < sizeof(struct tlv_header_t)) { ND_PRINT("\n\t ERROR: illegal length - should be >= %lu", (unsigned long) sizeof(struct tlv_header_t)); return; } /* is the packet big enough to include the tlv ? */ if (tlen < tlv_len) goto tooshort; /* did we capture enough for fully decoding the tlv ? */ ND_TCHECK_LEN(tptr, tlv_len); tlv_tptr=tptr+sizeof(struct tlv_header_t); tlv_tlen=tlv_len-sizeof(struct tlv_header_t); switch((proto_subtype << 8) + tlv_type) { /* those two TLVs have the same structure -> fall through */ case ((SLOW_PROTO_LACP << 8) + LACP_TLV_ACTOR_INFO): case ((SLOW_PROTO_LACP << 8) + LACP_TLV_PARTNER_INFO): if (tlv_tlen != sizeof(struct lacp_tlv_actor_partner_info_t)) { ND_PRINT("\n\t ERROR: illegal length - should be %lu", (unsigned long) (sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_actor_partner_info_t))); goto badlength; } tlv_ptr.lacp_tlv_actor_partner_info = (const struct lacp_tlv_actor_partner_info_t *)tlv_tptr; ND_PRINT("\n\t System %s, System Priority %u, Key %u" ", Port %u, Port Priority %u\n\t State Flags [%s]", etheraddr_string(ndo, tlv_ptr.lacp_tlv_actor_partner_info->sys), EXTRACT_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->sys_pri), EXTRACT_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->key), EXTRACT_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port), EXTRACT_BE_U_2(tlv_ptr.lacp_tlv_actor_partner_info->port_pri), bittok2str(lacp_tlv_actor_partner_info_state_values, "none", EXTRACT_U_1(tlv_ptr.lacp_tlv_actor_partner_info->state))); break; case ((SLOW_PROTO_LACP << 8) + LACP_TLV_COLLECTOR_INFO): if (tlv_tlen != sizeof(struct lacp_tlv_collector_info_t)) { ND_PRINT("\n\t ERROR: illegal length - should be %lu", (unsigned long) (sizeof(struct tlv_header_t) + sizeof(struct lacp_tlv_collector_info_t))); goto badlength; } tlv_ptr.lacp_tlv_collector_info = (const struct lacp_tlv_collector_info_t *)tlv_tptr; ND_PRINT("\n\t Max Delay %u", EXTRACT_BE_U_2(tlv_ptr.lacp_tlv_collector_info->max_delay)); break; case ((SLOW_PROTO_MARKER << 8) + MARKER_TLV_MARKER_INFO): if (tlv_tlen != sizeof(struct marker_tlv_marker_info_t)) { ND_PRINT("\n\t ERROR: illegal length - should be %lu", (unsigned long) (sizeof(struct tlv_header_t) + sizeof(struct marker_tlv_marker_info_t))); goto badlength; } tlv_ptr.marker_tlv_marker_info = (const struct marker_tlv_marker_info_t *)tlv_tptr; ND_PRINT("\n\t Request System %s, Request Port %u, Request Transaction ID 0x%08x", etheraddr_string(ndo, tlv_ptr.marker_tlv_marker_info->req_sys), EXTRACT_BE_U_2(tlv_ptr.marker_tlv_marker_info->req_port), EXTRACT_BE_U_4(tlv_ptr.marker_tlv_marker_info->req_trans_id)); break; default: if (ndo->ndo_vflag <= 1) print_unknown_data(ndo, tlv_tptr, "\n\t ", tlv_tlen); break; } badlength: /* do we want to see an additional hexdump ? */ if (ndo->ndo_vflag > 1) { print_unknown_data(ndo, tptr+sizeof(struct tlv_header_t), "\n\t ", tlv_len-sizeof(struct tlv_header_t)); } tptr+=tlv_len; tlen-=tlv_len; } return; tooshort: ND_PRINT("\n\t\t packet is too short"); return; trunc: ND_PRINT("%s", tstr); }
static void chdlc_slarp_print(netdissect_options *ndo, const u_char *cp, u_int length) { const struct cisco_slarp *slarp; u_int sec,min,hrs,days; ndo->ndo_protocol = "chdlc_slarp"; ND_PRINT("SLARP (length: %u), ",length); if (length < SLARP_MIN_LEN) goto trunc; slarp = (const struct cisco_slarp *)cp; ND_TCHECK_LEN(slarp, SLARP_MIN_LEN); switch (GET_BE_U_4(slarp->code)) { case SLARP_REQUEST: ND_PRINT("request"); /* * At least according to William "Chops" Westfield's * message in * * http://www.nethelp.no/net/cisco-hdlc.txt * * the address and mask aren't used in requests - * they're just zero. */ break; case SLARP_REPLY: ND_PRINT("reply %s/%s", ipaddr_string(ndo, slarp->un.addr.addr), ipaddr_string(ndo, slarp->un.addr.mask)); break; case SLARP_KEEPALIVE: ND_PRINT("keepalive: mineseen=0x%08x, yourseen=0x%08x, reliability=0x%04x", GET_BE_U_4(slarp->un.keep.myseq), GET_BE_U_4(slarp->un.keep.yourseq), GET_BE_U_2(slarp->un.keep.rel)); if (length >= SLARP_MAX_LEN) { /* uptime-stamp is optional */ cp += SLARP_MIN_LEN; ND_TCHECK_4(cp); sec = GET_BE_U_4(cp) / 1000; min = sec / 60; sec -= min * 60; hrs = min / 60; min -= hrs * 60; days = hrs / 24; hrs -= days * 24; ND_PRINT(", link uptime=%ud%uh%um%us",days,hrs,min,sec); } break; default: ND_PRINT("0x%02x unknown", GET_BE_U_4(slarp->code)); if (ndo->ndo_vflag <= 1) print_unknown_data(ndo,cp+4,"\n\t",length-4); break; } if (SLARP_MAX_LEN < length && ndo->ndo_vflag) ND_PRINT(", (trailing junk: %u bytes)", length - SLARP_MAX_LEN); if (ndo->ndo_vflag > 1) print_unknown_data(ndo,cp+4,"\n\t",length-4); return; trunc: nd_print_trunc(ndo); }
u_int chdlc_print(netdissect_options *ndo, const u_char *p, u_int length) { u_int proto; const u_char *bp = p; ndo->ndo_protocol = "chdlc"; if (length < CHDLC_HDRLEN) goto trunc; ND_TCHECK_LEN(p, CHDLC_HDRLEN); proto = GET_BE_U_2(p + 2); if (ndo->ndo_eflag) { ND_PRINT("%s, ethertype %s (0x%04x), length %u: ", tok2str(chdlc_cast_values, "0x%02x", GET_U_1(p)), tok2str(ethertype_values, "Unknown", proto), proto, length); } length -= CHDLC_HDRLEN; p += CHDLC_HDRLEN; switch (proto) { case ETHERTYPE_IP: ip_print(ndo, p, length); break; case ETHERTYPE_IPV6: ip6_print(ndo, p, length); break; case CHDLC_TYPE_SLARP: chdlc_slarp_print(ndo, p, length); break; #if 0 case CHDLC_TYPE_CDP: chdlc_cdp_print(p, length); break; #endif case ETHERTYPE_MPLS: case ETHERTYPE_MPLS_MULTI: mpls_print(ndo, p, length); break; case ETHERTYPE_ISO: /* is the fudge byte set ? lets verify by spotting ISO headers */ if (length < 2) goto trunc; ND_TCHECK_2(p); if (GET_U_1(p + 1) == NLPID_CLNP || GET_U_1(p + 1) == NLPID_ESIS || GET_U_1(p + 1) == NLPID_ISIS) isoclns_print(ndo, p + 1, length - 1); else isoclns_print(ndo, p, length); break; default: if (!ndo->ndo_eflag) ND_PRINT("unknown CHDLC protocol (0x%04x)", proto); break; } return (CHDLC_HDRLEN); trunc: nd_print_trunc(ndo); return (ND_BYTES_AVAILABLE_AFTER(bp)); }
static u_int dccp_print_option(netdissect_options *ndo, const u_char *option, u_int hlen) { uint8_t optlen, i; ND_TCHECK_1(option); if (EXTRACT_U_1(option) >= 32) { ND_TCHECK_1(option + 1); optlen = EXTRACT_U_1(option + 1); if (optlen < 2) { if (EXTRACT_U_1(option) >= 128) ND_PRINT("CCID option %u optlen too short", EXTRACT_U_1(option)); else ND_PRINT("%s optlen too short", tok2str(dccp_option_values, "Option %u", EXTRACT_U_1(option))); return 0; } } else optlen = 1; if (hlen < optlen) { if (EXTRACT_U_1(option) >= 128) ND_PRINT("CCID option %u optlen goes past header length", EXTRACT_U_1(option)); else ND_PRINT("%s optlen goes past header length", tok2str(dccp_option_values, "Option %u", EXTRACT_U_1(option))); return 0; } ND_TCHECK_LEN(option, optlen); if (EXTRACT_U_1(option) >= 128) { ND_PRINT("CCID option %u", EXTRACT_U_1(option)); switch (optlen) { case 4: ND_PRINT(" %u", EXTRACT_BE_U_2(option + 2)); break; case 6: ND_PRINT(" %u", EXTRACT_BE_U_4(option + 2)); break; default: break; } } else { ND_PRINT("%s", tok2str(dccp_option_values, "Option %u", EXTRACT_U_1(option))); switch (EXTRACT_U_1(option)) { case 32: case 33: case 34: case 35: if (optlen < 3) { ND_PRINT(" optlen too short"); return optlen; } if (EXTRACT_U_1(option + 2) < 10){ ND_PRINT(" %s", dccp_feature_nums[EXTRACT_U_1(option + 2)]); for (i = 0; i < optlen - 3; i++) ND_PRINT(" %u", EXTRACT_U_1(option + 3 + i)); } break; case 36: if (optlen > 2) { ND_PRINT(" 0x"); for (i = 0; i < optlen - 2; i++) ND_PRINT("%02x", EXTRACT_U_1(option + 2 + i)); } break; case 37: for (i = 0; i < optlen - 2; i++) ND_PRINT(" %u", EXTRACT_U_1(option + 2 + i)); break; case 38: if (optlen > 2) { ND_PRINT(" 0x"); for (i = 0; i < optlen - 2; i++) ND_PRINT("%02x", EXTRACT_U_1(option + 2 + i)); } break; case 39: if (optlen > 2) { ND_PRINT(" 0x"); for (i = 0; i < optlen - 2; i++) ND_PRINT("%02x", EXTRACT_U_1(option + 2 + i)); } break; case 40: if (optlen > 2) { ND_PRINT(" 0x"); for (i = 0; i < optlen - 2; i++) ND_PRINT("%02x", EXTRACT_U_1(option + 2 + i)); } break; case 41: /* * 13.1. Timestamp Option * * +--------+--------+--------+--------+--------+--------+ * |00101001|00000110| Timestamp Value | * +--------+--------+--------+--------+--------+--------+ * Type=41 Length=6 */ if (optlen == 6) ND_PRINT(" %u", EXTRACT_BE_U_4(option + 2)); else ND_PRINT(" [optlen != 6]"); break; case 42: /* * 13.3. Timestamp Echo Option * * +--------+--------+--------+--------+--------+--------+ * |00101010|00000110| Timestamp Echo | * +--------+--------+--------+--------+--------+--------+ * Type=42 Len=6 * * +--------+--------+------- ... -------+--------+--------+ * |00101010|00001000| Timestamp Echo | Elapsed Time | * +--------+--------+------- ... -------+--------+--------+ * Type=42 Len=8 (4 bytes) * * +--------+--------+------- ... -------+------- ... -------+ * |00101010|00001010| Timestamp Echo | Elapsed Time | * +--------+--------+------- ... -------+------- ... -------+ * Type=42 Len=10 (4 bytes) (4 bytes) */ switch (optlen) { case 6: ND_PRINT(" %u", EXTRACT_BE_U_4(option + 2)); break; case 8: ND_PRINT(" %u", EXTRACT_BE_U_4(option + 2)); ND_PRINT(" (elapsed time %u)", EXTRACT_BE_U_2(option + 6)); break; case 10: ND_PRINT(" %u", EXTRACT_BE_U_4(option + 2)); ND_PRINT(" (elapsed time %u)", EXTRACT_BE_U_4(option + 6)); break; default: ND_PRINT(" [optlen != 6 or 8 or 10]"); break; } break; case 43: if (optlen == 6) ND_PRINT(" %u", EXTRACT_BE_U_4(option + 2)); else if (optlen == 4) ND_PRINT(" %u", EXTRACT_BE_U_2(option + 2)); else ND_PRINT(" [optlen != 4 or 6]"); break; case 44: if (optlen > 2) { ND_PRINT(" "); for (i = 0; i < optlen - 2; i++) ND_PRINT("%02x", EXTRACT_U_1(option + 2 + i)); } break; } } return optlen; trunc: nd_print_trunc(ndo); return 0; }
static int auth_print(netdissect_options *ndo, const u_char *pptr) { const struct bfd_auth_header_t *bfd_auth_header; uint8_t auth_type, auth_len; int i; pptr += sizeof (struct bfd_header_t); bfd_auth_header = (const struct bfd_auth_header_t *)pptr; ND_TCHECK_SIZE(bfd_auth_header); auth_type = GET_U_1(bfd_auth_header->auth_type); auth_len = GET_U_1(bfd_auth_header->auth_len); ND_PRINT("\n\tAuthentication: %s (%u), length: %u", tok2str(bfd_v1_authentication_values,"Unknown",auth_type), auth_type, auth_len); pptr += 2; ND_PRINT("\n\t Auth Key ID: %u", GET_U_1(pptr)); switch(auth_type) { case AUTH_PASSWORD: /* * Simple Password Authentication Section Format * * 0 1 2 3 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Auth Type | Auth Len | Auth Key ID | Password... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | ... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ if (auth_len < AUTH_PASSWORD_FIELD_MIN_LEN || auth_len > AUTH_PASSWORD_FIELD_MAX_LEN) { ND_PRINT("[invalid length %u]", auth_len); break; } pptr++; ND_PRINT(", Password: "******"[invalid length %u]", auth_len); break; } pptr += 2; ND_TCHECK_4(pptr); ND_PRINT(", Sequence Number: 0x%08x", GET_BE_U_4(pptr)); pptr += 4; ND_TCHECK_LEN(pptr, AUTH_MD5_HASH_LEN); ND_PRINT("\n\t Digest: "); for(i = 0; i < AUTH_MD5_HASH_LEN; i++) ND_PRINT("%02x", GET_U_1(pptr + i)); break; case AUTH_SHA1: case AUTH_MET_SHA1: /* * Keyed SHA1 and Meticulous Keyed SHA1 Authentication Section Format * * 0 1 2 3 * 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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Auth Type | Auth Len | Auth Key ID | Reserved | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Sequence Number | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | Auth Key/Hash... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ * | ... | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ if (auth_len != AUTH_SHA1_FIELD_LEN) { ND_PRINT("[invalid length %u]", auth_len); break; } pptr += 2; ND_TCHECK_4(pptr); ND_PRINT(", Sequence Number: 0x%08x", GET_BE_U_4(pptr)); pptr += 4; ND_TCHECK_LEN(pptr, AUTH_SHA1_HASH_LEN); ND_PRINT("\n\t Hash: "); for(i = 0; i < AUTH_SHA1_HASH_LEN; i++) ND_PRINT("%02x", GET_U_1(pptr + i)); break; } return 0; trunc: return 1; }
void vtp_print(netdissect_options *ndo, const u_char *pptr, u_int length) { u_int type, len, name_len, tlv_len, tlv_value, mgmtd_len; const u_char *tptr; const struct vtp_vlan_ *vtp_vlan; ndo->ndo_protocol = "vtp"; if (length < VTP_HEADER_LEN) goto trunc; tptr = pptr; ND_TCHECK_LEN(tptr, VTP_HEADER_LEN); type = EXTRACT_U_1(tptr + 1); ND_PRINT("VTPv%u, Message %s (0x%02x), length %u", EXTRACT_U_1(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("\n\tDomain name: "); mgmtd_len = EXTRACT_U_1(tptr + 3); if (mgmtd_len < 1 || mgmtd_len > 32) { ND_PRINT(" [invalid MgmtD Len %u]", mgmtd_len); return; } nd_printzp(ndo, tptr + 4, mgmtd_len, NULL); ND_PRINT(", %s: %u", tok2str(vtp_header_values, "Unknown", type), EXTRACT_U_1(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_TCHECK_8(tptr); ND_PRINT("\n\t Config Rev %x, Updater %s", EXTRACT_BE_U_4(tptr), ipaddr_string(ndo, tptr+4)); tptr += 8; ND_TCHECK_LEN(tptr, VTP_UPDATE_TIMESTAMP_LEN); ND_PRINT(", Timestamp 0x%08x 0x%08x 0x%08x", EXTRACT_BE_U_4(tptr), EXTRACT_BE_U_4(tptr + 4), EXTRACT_BE_U_4(tptr + 8)); tptr += VTP_UPDATE_TIMESTAMP_LEN; ND_TCHECK_LEN(tptr, VTP_MD5_DIGEST_LEN); ND_PRINT(", MD5 digest: %08x%08x%08x%08x", EXTRACT_BE_U_4(tptr), EXTRACT_BE_U_4(tptr + 4), EXTRACT_BE_U_4(tptr + 8), EXTRACT_BE_U_4(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_4(tptr); ND_PRINT(", Config Rev %x", EXTRACT_BE_U_4(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_1(tptr); len = EXTRACT_U_1(tptr); if (len == 0) break; ND_TCHECK_LEN(tptr, len); vtp_vlan = (const struct vtp_vlan_*)tptr; if (len < VTP_VLAN_INFO_FIXED_PART_LEN) goto trunc; ND_TCHECK_SIZE(vtp_vlan); ND_PRINT("\n\tVLAN info status %s, type %s, VLAN-id %u, MTU %u, SAID 0x%08x, Name ", tok2str(vtp_vlan_status,"Unknown",EXTRACT_U_1(vtp_vlan->status)), tok2str(vtp_vlan_type_values,"Unknown",EXTRACT_U_1(vtp_vlan->type)), EXTRACT_BE_U_2(vtp_vlan->vlanid), EXTRACT_BE_U_2(vtp_vlan->mtu), EXTRACT_BE_U_4(vtp_vlan->index)); len -= VTP_VLAN_INFO_FIXED_PART_LEN; tptr += VTP_VLAN_INFO_FIXED_PART_LEN; name_len = EXTRACT_U_1(vtp_vlan->name_len); if (len < 4*((name_len + 3)/4)) goto trunc; ND_TCHECK_LEN(tptr, name_len); nd_printzp(ndo, tptr, name_len, NULL); /* * Vlan names are aligned to 32-bit boundaries. */ len -= 4*((name_len + 3)/4); tptr += 4*((name_len + 3)/4); /* TLV information follows */ while (len > 0) { /* * Cisco specs say 2 bytes for type + 2 bytes for length; * see http://docstore.mik.ua/univercd/cc/td/doc/product/lan/trsrb/frames.htm * However, actual packets on the wire appear to use 1 * byte for the type and 1 byte for the length, so that's * what we do. */ if (len < 2) goto trunc; ND_TCHECK_2(tptr); type = EXTRACT_U_1(tptr); tlv_len = EXTRACT_U_1(tptr + 1); ND_PRINT("\n\t\t%s (0x%04x) TLV", tok2str(vtp_vlan_tlv_values, "Unknown", type), type); if (len < tlv_len * 2 + 2) { ND_PRINT(" (TLV goes past the end of the packet)"); return; } ND_TCHECK_LEN(tptr, tlv_len * 2 + 2); /* * We assume the value is a 2-byte integer; the length is * in units of 16-bit words. */ if (tlv_len != 1) { ND_PRINT(" (invalid TLV length %u != 1)", tlv_len); return; } else { tlv_value = EXTRACT_BE_U_2(tptr + 2); switch (type) { case VTP_VLAN_STE_HOP_COUNT: ND_PRINT(", %u", tlv_value); break; case VTP_VLAN_PRUNING: ND_PRINT(", %s (%u)", tlv_value == 1 ? "Enabled" : "Disabled", tlv_value); break; case VTP_VLAN_STP_TYPE: ND_PRINT(", %s (%u)", tok2str(vtp_stp_type_values, "Unknown", tlv_value), tlv_value); break; case VTP_VLAN_BRIDGE_TYPE: ND_PRINT(", %s (%u)", tlv_value == 1 ? "SRB" : "SRT", tlv_value); break; case VTP_VLAN_BACKUP_CRF_MODE: ND_PRINT(", %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_TCHECK_4(tptr); ND_PRINT("\n\tStart value: %u", EXTRACT_BE_U_4(tptr)); break; case VTP_JOIN_MESSAGE: /* FIXME - Could not find message format */ break; default: break; } return; trunc: nd_print_trunc(ndo); }
/** * dccp_print - show dccp packet * @bp - beginning of dccp packet * @data2 - beginning of enclosing * @len - lenght of ip packet */ void dccp_print(netdissect_options *ndo, const u_char *bp, const u_char *data2, u_int len) { const struct dccp_hdr *dh; const struct ip *ip; const struct ip6_hdr *ip6; const u_char *cp; u_short sport, dport; u_int hlen; u_int fixed_hdrlen; uint8_t dccph_type; ndo->ndo_protocol = "dccp"; dh = (const struct dccp_hdr *)bp; ip = (const struct ip *)data2; if (IP_V(ip) == 6) ip6 = (const struct ip6_hdr *)data2; else ip6 = NULL; /* make sure we have enough data to look at the X bit */ cp = (const u_char *)(dh + 1); if (cp > ndo->ndo_snapend) goto trunc; if (len < sizeof(struct dccp_hdr)) { ND_PRINT("truncated-dccp - %u bytes missing!", (u_int)sizeof(struct dccp_hdr) - len); return; } /* get the length of the generic header */ fixed_hdrlen = dccp_basic_hdr_len(dh); if (len < fixed_hdrlen) { ND_PRINT("truncated-dccp - %u bytes missing!", fixed_hdrlen - len); return; } ND_TCHECK_LEN(dh, fixed_hdrlen); sport = EXTRACT_BE_U_2(dh->dccph_sport); dport = EXTRACT_BE_U_2(dh->dccph_dport); hlen = EXTRACT_U_1(dh->dccph_doff) * 4; if (ip6) { ND_PRINT("%s.%u > %s.%u: ", ip6addr_string(ndo, ip6->ip6_src), sport, ip6addr_string(ndo, ip6->ip6_dst), dport); } else { ND_PRINT("%s.%u > %s.%u: ", ipaddr_string(ndo, ip->ip_src), sport, ipaddr_string(ndo, ip->ip_dst), dport); } ND_PRINT("DCCP"); if (ndo->ndo_qflag) { ND_PRINT(" %u", len - hlen); if (hlen > len) { ND_PRINT(" [bad hdr length %u - too long, > %u]", hlen, len); } return; } /* other variables in generic header */ if (ndo->ndo_vflag) { ND_PRINT(" (CCVal %u, CsCov %u, ", DCCPH_CCVAL(dh), DCCPH_CSCOV(dh)); } /* checksum calculation */ if (ndo->ndo_vflag && ND_TTEST_LEN(bp, len)) { uint16_t sum = 0, dccp_sum; dccp_sum = EXTRACT_BE_U_2(dh->dccph_checksum); ND_PRINT("cksum 0x%04x ", dccp_sum); if (IP_V(ip) == 4) sum = dccp_cksum(ndo, ip, dh, len); else if (IP_V(ip) == 6) sum = dccp6_cksum(ndo, ip6, dh, len); if (sum != 0) ND_PRINT("(incorrect -> 0x%04x)",in_cksum_shouldbe(dccp_sum, sum)); else ND_PRINT("(correct)"); } if (ndo->ndo_vflag) ND_PRINT(")"); ND_PRINT(" "); dccph_type = DCCPH_TYPE(dh); switch (dccph_type) { case DCCP_PKT_REQUEST: { const struct dccp_hdr_request *dhr = (const struct dccp_hdr_request *)(bp + fixed_hdrlen); fixed_hdrlen += 4; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_TCHECK_SIZE(dhr); ND_PRINT("%s (service=%u) ", tok2str(dccp_pkt_type_str, "", dccph_type), EXTRACT_BE_U_4(dhr->dccph_req_service)); break; } case DCCP_PKT_RESPONSE: { const struct dccp_hdr_response *dhr = (const struct dccp_hdr_response *)(bp + fixed_hdrlen); fixed_hdrlen += 12; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_TCHECK_SIZE(dhr); ND_PRINT("%s (service=%u) ", tok2str(dccp_pkt_type_str, "", dccph_type), EXTRACT_BE_U_4(dhr->dccph_resp_service)); break; } case DCCP_PKT_DATA: ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; case DCCP_PKT_ACK: { fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; } case DCCP_PKT_DATAACK: { fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; } case DCCP_PKT_CLOSEREQ: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; case DCCP_PKT_CLOSE: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; case DCCP_PKT_RESET: { const struct dccp_hdr_reset *dhr = (const struct dccp_hdr_reset *)(bp + fixed_hdrlen); fixed_hdrlen += 12; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_TCHECK_SIZE(dhr); ND_PRINT("%s (code=%s) ", tok2str(dccp_pkt_type_str, "", dccph_type), dccp_reset_code(EXTRACT_U_1(dhr->dccph_reset_code))); break; } case DCCP_PKT_SYNC: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; case DCCP_PKT_SYNCACK: fixed_hdrlen += 8; if (len < fixed_hdrlen) { ND_PRINT("truncated-%s - %u bytes missing!", tok2str(dccp_pkt_type_str, "", dccph_type), fixed_hdrlen - len); return; } ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "", dccph_type)); break; default: ND_PRINT("%s ", tok2str(dccp_pkt_type_str, "unknown-type-%u", dccph_type)); break; } if ((DCCPH_TYPE(dh) != DCCP_PKT_DATA) && (DCCPH_TYPE(dh) != DCCP_PKT_REQUEST)) dccp_print_ack_no(ndo, bp); if (ndo->ndo_vflag < 2) return; ND_PRINT("seq %" PRIu64, dccp_seqno(bp)); /* process options */ if (hlen > fixed_hdrlen){ u_int optlen; cp = bp + fixed_hdrlen; ND_PRINT(" <"); hlen -= fixed_hdrlen; while(1){ optlen = dccp_print_option(ndo, cp, hlen); if (!optlen) break; if (hlen <= optlen) break; hlen -= optlen; cp += optlen; ND_PRINT(", "); } ND_PRINT(">"); } return; trunc: nd_print_trunc(ndo); return; }
void cdp_print(netdissect_options *ndo, const u_char *pptr, u_int length, u_int caplen) { u_int type, len, i, j; const u_char *tptr; if (caplen < CDP_HEADER_LEN) { ND_PRINT("%s", tstr); return; } tptr = pptr; /* temporary pointer */ ND_TCHECK_LEN(tptr, CDP_HEADER_LEN); ND_PRINT("CDPv%u, ttl: %us", EXTRACT_U_1((tptr + CDP_HEADER_VERSION_OFFSET)), EXTRACT_U_1(tptr + CDP_HEADER_TTL_OFFSET)); if (ndo->ndo_vflag) ND_PRINT(", checksum: 0x%04x (unverified), length %u", EXTRACT_BE_U_2(tptr + CDP_HEADER_CHECKSUM_OFFSET), length); tptr += CDP_HEADER_LEN; while (tptr < (pptr+length)) { ND_TCHECK_LEN(tptr, CDP_TLV_HEADER_LEN); /* read out Type and Length */ type = EXTRACT_BE_U_2(tptr + CDP_TLV_TYPE_OFFSET); len = EXTRACT_BE_U_2(tptr + CDP_TLV_LEN_OFFSET); /* object length includes the 4 bytes header length */ if (len < CDP_TLV_HEADER_LEN) { if (ndo->ndo_vflag) ND_PRINT("\n\t%s (0x%02x), TLV length: %u byte%s (too short)", tok2str(cdp_tlv_values,"unknown field type", type), type, len, PLURAL_SUFFIX(len)); /* plural */ else ND_PRINT(", %s TLV length %u too short", tok2str(cdp_tlv_values,"unknown field type", type), len); break; } tptr += CDP_TLV_HEADER_LEN; len -= CDP_TLV_HEADER_LEN; ND_TCHECK_LEN(tptr, len); if (ndo->ndo_vflag || type == 1) { /* in non-verbose mode just print Device-ID */ if (ndo->ndo_vflag) ND_PRINT("\n\t%s (0x%02x), value length: %u byte%s: ", tok2str(cdp_tlv_values,"unknown field type", type), type, len, PLURAL_SUFFIX(len)); /* plural */ switch (type) { case 0x01: /* Device-ID */ if (!ndo->ndo_vflag) ND_PRINT(", Device-ID "); ND_PRINT("'"); (void)fn_printn(ndo, tptr, len, NULL); ND_PRINT("'"); break; case 0x02: /* Address */ if (cdp_print_addr(ndo, tptr, len) < 0) goto trunc; break; case 0x03: /* Port-ID */ ND_PRINT("'"); (void)fn_printn(ndo, tptr, len, NULL); ND_PRINT("'"); break; case 0x04: /* Capabilities */ if (len < 4) goto trunc; ND_PRINT("(0x%08x): %s", EXTRACT_BE_U_4(tptr), bittok2str(cdp_capability_values, "none", EXTRACT_BE_U_4(tptr))); break; case 0x05: /* Version */ ND_PRINT("\n\t "); for (i=0;i<len;i++) { j = EXTRACT_U_1(tptr + i); if (j == '\n') /* lets rework the version string to get a nice indentation */ ND_PRINT("\n\t "); else fn_print_char(ndo, j); } break; case 0x06: /* Platform */ ND_PRINT("'"); (void)fn_printn(ndo, tptr, len, NULL); ND_PRINT("'"); break; case 0x07: /* Prefixes */ if (cdp_print_prefixes(ndo, tptr, len) < 0) goto trunc; break; case 0x08: /* Protocol Hello Option - not documented */ break; case 0x09: /* VTP Mgmt Domain - CDPv2 */ ND_PRINT("'"); (void)fn_printn(ndo, tptr, len, NULL); ND_PRINT("'"); break; case 0x0a: /* Native VLAN ID - CDPv2 */ if (len < 2) goto trunc; ND_PRINT("%u", EXTRACT_BE_U_2(tptr)); break; case 0x0b: /* Duplex - CDPv2 */ if (len < 1) goto trunc; ND_PRINT("%s", EXTRACT_U_1(tptr) ? "full": "half"); break; /* http://www.cisco.com/c/en/us/td/docs/voice_ip_comm/cata/186/2_12_m/english/release/notes/186rn21m.html * plus more details from other sources * * There are apparently versions of the request with both * 2 bytes and 3 bytes of value. The 3 bytes of value * appear to be a 1-byte application type followed by a * 2-byte VLAN ID; the 2 bytes of value are unknown * (they're 0x20 0x00 in some captures I've seen; that * is not a valid VLAN ID, as VLAN IDs are 12 bits). * * The replies all appear to be 3 bytes long. */ case 0x0e: /* ATA-186 VoIP VLAN assignment - incomplete doc. */ if (len < 3) goto trunc; ND_PRINT("app %u, vlan %u", EXTRACT_U_1(tptr), EXTRACT_BE_U_2(tptr + 1)); break; case 0x0f: /* ATA-186 VoIP VLAN request - incomplete doc. */ if (len < 2) goto trunc; if (len == 2) ND_PRINT("unknown 0x%04x", EXTRACT_BE_U_2(tptr)); else ND_PRINT("app %u, vlan %u", EXTRACT_U_1(tptr), EXTRACT_BE_U_2(tptr + 1)); break; case 0x10: /* Power - not documented */ ND_PRINT("%1.2fW", cdp_get_number(tptr, len) / 1000.0); break; case 0x11: /* MTU - not documented */ if (len < 4) goto trunc; ND_PRINT("%u bytes", EXTRACT_BE_U_4(tptr)); break; case 0x12: /* AVVID trust bitmap - not documented */ if (len < 1) goto trunc; ND_PRINT("0x%02x", EXTRACT_U_1(tptr)); break; case 0x13: /* AVVID untrusted port CoS - not documented */ if (len < 1) goto trunc; ND_PRINT("0x%02x", EXTRACT_U_1(tptr)); break; case 0x14: /* System Name - not documented */ ND_PRINT("'"); (void)fn_printn(ndo, tptr, len, NULL); ND_PRINT("'"); break; case 0x16: /* System Object ID - not documented */ if (cdp_print_addr(ndo, tptr, len) < 0) goto trunc; break; case 0x17: /* Physical Location - not documented */ if (len < 1) goto trunc; ND_PRINT("0x%02x", EXTRACT_U_1(tptr)); if (len > 1) { ND_PRINT("/"); (void)fn_printn(ndo, tptr + 1, len - 1, NULL); } break; default: print_unknown_data(ndo, tptr, "\n\t ", len); break; } } tptr = tptr+len; } if (ndo->ndo_vflag < 1) ND_PRINT(", length %u", caplen); return; trunc: ND_PRINT("%s", tstr); }
static void rfc1048_print(netdissect_options *ndo, const u_char *bp) { uint16_t tag; u_int len; const char *cp; char c; int first, idx; uint8_t subopt, suboptlen; ND_PRINT("\n\t Vendor-rfc1048 Extensions"); /* Step over magic cookie */ ND_PRINT("\n\t Magic Cookie 0x%08x", EXTRACT_BE_U_4(bp)); bp += sizeof(int32_t); /* Loop while we there is a tag left in the buffer */ while (ND_TTEST_1(bp)) { tag = EXTRACT_U_1(bp); bp++; if (tag == TAG_PAD && ndo->ndo_vflag < 3) continue; if (tag == TAG_END && ndo->ndo_vflag < 3) return; if (tag == TAG_EXTENDED_OPTION) { ND_TCHECK_2(bp + 1); tag = EXTRACT_BE_U_2(bp + 1); /* XXX we don't know yet if the IANA will * preclude overlap of 1-byte and 2-byte spaces. * If not, we need to offset tag after this step. */ cp = tok2str(xtag2str, "?xT%u", tag); } else cp = tok2str(tag2str, "?T%u", tag); c = *cp++; if (tag == TAG_PAD || tag == TAG_END) len = 0; else { /* Get the length; check for truncation */ ND_TCHECK_1(bp); len = EXTRACT_U_1(bp); bp++; } ND_PRINT("\n\t %s Option %u, length %u%s", cp, tag, len, len > 0 ? ": " : ""); if (tag == TAG_PAD && ndo->ndo_vflag > 2) { u_int ntag = 1; while (ND_TTEST_1(bp) && EXTRACT_U_1(bp) == TAG_PAD) { bp++; ntag++; } if (ntag > 1) ND_PRINT(", occurs %u", ntag); } ND_TCHECK_LEN(bp, len); if (tag == TAG_DHCP_MESSAGE && len == 1) { ND_PRINT("%s", tok2str(dhcp_msg_values, "Unknown (%u)", EXTRACT_U_1(bp))); bp++; continue; } if (tag == TAG_PARM_REQUEST) { idx = 0; while (len > 0) { cp = tok2str(tag2str, "?Option %u", EXTRACT_U_1(bp)); bp++; len--; if (idx % 4 == 0) ND_PRINT("\n\t "); else ND_PRINT(", "); ND_PRINT("%s", cp + 1); idx++; } continue; } if (tag == TAG_EXTENDED_REQUEST) { first = 1; while (len > 1) { cp = tok2str(xtag2str, "?xT%u", EXTRACT_BE_U_2(bp)); bp += 2; len -= 2; if (!first) ND_PRINT("+"); ND_PRINT("%s", cp + 1); first = 0; } continue; } /* Print data */ if (c == '?') { /* Base default formats for unknown tags on data size */ if (len & 1) c = 'b'; else if (len & 2) c = 's'; else c = 'l'; } first = 1; switch (c) { case 'a': /* ASCII strings */ ND_PRINT("\""); if (nd_printn(ndo, bp, len, ndo->ndo_snapend)) { ND_PRINT("\""); goto trunc; } ND_PRINT("\""); bp += len; len = 0; break; case 'i': case 'l': case 'L': /* ip addresses/32-bit words */ while (len >= 4) { if (!first) ND_PRINT(","); if (c == 'i') ND_PRINT("%s", ipaddr_string(ndo, bp)); else if (c == 'L') ND_PRINT("%d", EXTRACT_BE_S_4(bp)); else ND_PRINT("%u", EXTRACT_BE_U_4(bp)); bp += 4; len -= 4; first = 0; } break; case 'p': /* IP address pairs */ while (len >= 2*4) { if (!first) ND_PRINT(","); ND_PRINT("(%s:", ipaddr_string(ndo, bp)); bp += 4; len -= 4; ND_PRINT("%s)", ipaddr_string(ndo, bp)); bp += 4; len -= 4; first = 0; } break; case 's': /* shorts */ while (len >= 2) { if (!first) ND_PRINT(","); ND_PRINT("%u", EXTRACT_BE_U_2(bp)); bp += 2; len -= 2; first = 0; } break; case 'B': /* boolean */ while (len > 0) { uint8_t bool_value; if (!first) ND_PRINT(","); bool_value = EXTRACT_U_1(bp); switch (bool_value) { case 0: ND_PRINT("N"); break; case 1: ND_PRINT("Y"); break; default: ND_PRINT("%u?", bool_value); break; } ++bp; --len; first = 0; } break; case 'b': case 'x': default: /* Bytes */ while (len > 0) { uint8_t byte_value; if (!first) ND_PRINT(c == 'x' ? ":" : "."); byte_value = EXTRACT_U_1(bp); if (c == 'x') ND_PRINT("%02x", byte_value); else ND_PRINT("%u", byte_value); ++bp; --len; first = 0; } break; case '$': /* Guys we can't handle with one of the usual cases */ switch (tag) { case TAG_NETBIOS_NODE: /* this option should be at least 1 byte long */ if (len < 1) { ND_PRINT("ERROR: length < 1 bytes"); break; } tag = EXTRACT_U_1(bp); ++bp; --len; ND_PRINT("%s", tok2str(nbo2str, NULL, tag)); break; case TAG_OPT_OVERLOAD: /* this option should be at least 1 byte long */ if (len < 1) { ND_PRINT("ERROR: length < 1 bytes"); break; } tag = EXTRACT_U_1(bp); ++bp; --len; ND_PRINT("%s", tok2str(oo2str, NULL, tag)); break; case TAG_CLIENT_FQDN: /* this option should be at least 3 bytes long */ if (len < 3) { ND_PRINT("ERROR: length < 3 bytes"); bp += len; len = 0; break; } if (EXTRACT_U_1(bp)) ND_PRINT("[%s] ", client_fqdn_flags(EXTRACT_U_1(bp))); bp++; if (EXTRACT_U_1(bp) || EXTRACT_U_1(bp + 1)) ND_PRINT("%u/%u ", EXTRACT_U_1(bp), EXTRACT_U_1(bp + 1)); bp += 2; ND_PRINT("\""); if (nd_printn(ndo, bp, len - 3, ndo->ndo_snapend)) { ND_PRINT("\""); goto trunc; } ND_PRINT("\""); bp += len - 3; len = 0; break; case TAG_CLIENT_ID: { int type; /* this option should be at least 1 byte long */ if (len < 1) { ND_PRINT("ERROR: length < 1 bytes"); break; } type = EXTRACT_U_1(bp); bp++; len--; if (type == 0) { ND_PRINT("\""); if (nd_printn(ndo, bp, len, ndo->ndo_snapend)) { ND_PRINT("\""); goto trunc; } ND_PRINT("\""); bp += len; len = 0; break; } else { ND_PRINT("%s ", tok2str(arp2str, "hardware-type %u,", type)); while (len > 0) { if (!first) ND_PRINT(":"); ND_PRINT("%02x", EXTRACT_U_1(bp)); ++bp; --len; first = 0; } } break; } case TAG_AGENT_CIRCUIT: while (len >= 2) { subopt = EXTRACT_U_1(bp); suboptlen = EXTRACT_U_1(bp + 1); bp += 2; len -= 2; if (suboptlen > len) { ND_PRINT("\n\t %s SubOption %u, length %u: length goes past end of option", tok2str(agent_suboption_values, "Unknown", subopt), subopt, suboptlen); bp += len; len = 0; break; } ND_PRINT("\n\t %s SubOption %u, length %u: ", tok2str(agent_suboption_values, "Unknown", subopt), subopt, suboptlen); switch (subopt) { case AGENT_SUBOPTION_CIRCUIT_ID: /* fall through */ case AGENT_SUBOPTION_REMOTE_ID: case AGENT_SUBOPTION_SUBSCRIBER_ID: if (nd_printn(ndo, bp, suboptlen, ndo->ndo_snapend)) goto trunc; break; default: print_unknown_data(ndo, bp, "\n\t\t", suboptlen); } len -= suboptlen; bp += suboptlen; } break; case TAG_CLASSLESS_STATIC_RT: case TAG_CLASSLESS_STA_RT_MS: { u_int mask_width, significant_octets, i; /* this option should be at least 5 bytes long */ if (len < 5) { ND_PRINT("ERROR: length < 5 bytes"); bp += len; len = 0; break; } while (len > 0) { if (!first) ND_PRINT(","); mask_width = EXTRACT_U_1(bp); bp++; len--; /* mask_width <= 32 */ if (mask_width > 32) { ND_PRINT("[ERROR: Mask width (%u) > 32]", mask_width); bp += len; len = 0; break; } significant_octets = (mask_width + 7) / 8; /* significant octets + router(4) */ if (len < significant_octets + 4) { ND_PRINT("[ERROR: Remaining length (%u) < %u bytes]", len, significant_octets + 4); bp += len; len = 0; break; } ND_PRINT("("); if (mask_width == 0) ND_PRINT("default"); else { for (i = 0; i < significant_octets ; i++) { if (i > 0) ND_PRINT("."); ND_PRINT("%u", EXTRACT_U_1(bp)); bp++; } for (i = significant_octets ; i < 4 ; i++) ND_PRINT(".0"); ND_PRINT("/%u", mask_width); } ND_PRINT(":%s)", ipaddr_string(ndo, bp)); bp += 4; len -= (significant_octets + 4); first = 0; } break; } case TAG_USER_CLASS: { u_int suboptnumber = 1; first = 1; if (len < 2) { ND_PRINT("ERROR: length < 2 bytes"); bp += len; len = 0; break; } while (len > 0) { suboptlen = EXTRACT_U_1(bp); bp++; len--; ND_PRINT("\n\t "); ND_PRINT("instance#%u: ", suboptnumber); if (suboptlen == 0) { ND_PRINT("ERROR: suboption length must be non-zero"); bp += len; len = 0; break; } if (len < suboptlen) { ND_PRINT("ERROR: invalid option"); bp += len; len = 0; break; } ND_PRINT("\""); if (nd_printn(ndo, bp, suboptlen, ndo->ndo_snapend)) { ND_PRINT("\""); goto trunc; } ND_PRINT("\""); ND_PRINT(", length %u", suboptlen); suboptnumber++; len -= suboptlen; bp += suboptlen; } break; } default: ND_PRINT("[unknown special tag %u, size %u]", tag, len); bp += len; len = 0; break; } break; } /* Data left over? */ if (len) { ND_PRINT("\n\t trailing data length %u", len); bp += len; } } return; trunc: nd_print_trunc(ndo); }
/* 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); }
static int cdp_print_addr(netdissect_options *ndo, const u_char * p, u_int l) { u_int pt, pl, al, num; const u_char *endp = p + l; static const u_char prot_ipv6[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00, 0x86, 0xdd }; ND_TCHECK_4(p); if (p + 4 > endp) goto trunc; num = EXTRACT_BE_U_4(p); p += 4; while (p < endp && num != 0) { ND_TCHECK_2(p); if (p + 2 > endp) goto trunc; pt = EXTRACT_U_1(p); /* type of "protocol" field */ pl = EXTRACT_U_1(p + 1); /* length of "protocol" field */ p += 2; ND_TCHECK_2(p + pl); if (p + pl + 2 > endp) goto trunc; al = EXTRACT_BE_U_2(p + pl); /* address length */ if (pt == PT_NLPID && pl == 1 && EXTRACT_U_1(p) == NLPID_IP && al == 4) { /* * IPv4: protocol type = NLPID, protocol length = 1 * (1-byte NLPID), protocol = 0xcc (NLPID for IPv4), * address length = 4 */ p += 3; ND_TCHECK_4(p); if (p + 4 > endp) goto trunc; ND_PRINT("IPv4 (%u) %s", num, ipaddr_string(ndo, p)); p += 4; } else if (pt == PT_IEEE_802_2 && pl == 8 && memcmp(p, prot_ipv6, 8) == 0 && al == 16) { /* * IPv6: protocol type = IEEE 802.2 header, * protocol length = 8 (size of LLC+SNAP header), * protocol = LLC+SNAP header with the IPv6 * Ethertype, address length = 16 */ p += 10; ND_TCHECK_LEN(p, al); if (p + al > endp) goto trunc; ND_PRINT("IPv6 (%u) %s", num, ip6addr_string(ndo, p)); p += al; } else { /* * Generic case: just print raw data */ ND_TCHECK_LEN(p, pl); if (p + pl > endp) goto trunc; ND_PRINT("pt=0x%02x, pl=%u, pb=", EXTRACT_U_1((p - 2)), pl); while (pl-- > 0) { ND_PRINT(" %02x", EXTRACT_U_1(p)); p++; } ND_TCHECK_2(p); if (p + 2 > endp) goto trunc; ND_PRINT(", al=%u, a=", al); p += 2; ND_TCHECK_LEN(p, al); if (p + al > endp) goto trunc; while (al-- > 0) { ND_PRINT(" %02x", EXTRACT_U_1(p)); p++; } } num--; if (num) ND_PRINT(" "); } return 0; trunc: return -1; }
/* IPCP config options */ static u_int print_ipcp_config_options(netdissect_options *ndo, const u_char *p, u_int length) { u_int opt, len; u_int compproto, ipcomp_subopttotallen, ipcomp_subopt, ipcomp_suboptlen; 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) { ND_PRINT("\n\t %s Option (0x%02x), length %u (length bogus, should be >= 2)", tok2str(ipcpopt_values,"unknown",opt), opt, len); return 0; } ND_PRINT("\n\t %s Option (0x%02x), length %u", tok2str(ipcpopt_values,"unknown",opt), opt, len); switch (opt) { case IPCPOPT_2ADDR: /* deprecated */ if (len != 10) { ND_PRINT(" (length bogus, should be = 10)"); return len; } ND_TCHECK_4(p + 6); ND_PRINT(": src %s, dst %s", ipaddr_string(ndo, p + 2), ipaddr_string(ndo, p + 6)); break; case IPCPOPT_IPCOMP: if (len < 4) { ND_PRINT(" (length bogus, should be >= 4)"); return 0; } ND_TCHECK_2(p + 2); compproto = EXTRACT_BE_U_2(p + 2); ND_PRINT(": %s (0x%02x):", tok2str(ipcpopt_compproto_values, "Unknown", compproto), compproto); switch (compproto) { case PPP_VJC: /* XXX: VJ-Comp parameters should be decoded */ break; case IPCPOPT_IPCOMP_HDRCOMP: if (len < IPCPOPT_IPCOMP_MINLEN) { ND_PRINT(" (length bogus, should be >= %u)", IPCPOPT_IPCOMP_MINLEN); return 0; } ND_TCHECK_LEN(p + 2, IPCPOPT_IPCOMP_MINLEN); ND_PRINT("\n\t TCP Space %u, non-TCP Space %u" ", maxPeriod %u, maxTime %u, maxHdr %u", EXTRACT_BE_U_2(p + 4), EXTRACT_BE_U_2(p + 6), EXTRACT_BE_U_2(p + 8), EXTRACT_BE_U_2(p + 10), EXTRACT_BE_U_2(p + 12)); /* suboptions present ? */ if (len > IPCPOPT_IPCOMP_MINLEN) { ipcomp_subopttotallen = len - IPCPOPT_IPCOMP_MINLEN; p += IPCPOPT_IPCOMP_MINLEN; ND_PRINT("\n\t Suboptions, length %u", ipcomp_subopttotallen); while (ipcomp_subopttotallen >= 2) { ND_TCHECK_2(p); ipcomp_subopt = EXTRACT_U_1(p); ipcomp_suboptlen = EXTRACT_U_1(p + 1); /* sanity check */ if (ipcomp_subopt == 0 || ipcomp_suboptlen == 0 ) break; /* XXX: just display the suboptions for now */ ND_PRINT("\n\t\t%s Suboption #%u, length %u", tok2str(ipcpopt_compproto_subopt_values, "Unknown", ipcomp_subopt), ipcomp_subopt, ipcomp_suboptlen); ipcomp_subopttotallen -= ipcomp_suboptlen; p += ipcomp_suboptlen; } } break; default: break; } break; case IPCPOPT_ADDR: /* those options share the same format - fall through */ case IPCPOPT_MOBILE4: case IPCPOPT_PRIDNS: case IPCPOPT_PRINBNS: case IPCPOPT_SECDNS: case IPCPOPT_SECNBNS: if (len != 6) { ND_PRINT(" (length bogus, should be = 6)"); return 0; } ND_TCHECK_4(p + 2); ND_PRINT(": %s", ipaddr_string(ndo, p + 2)); break; 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("[|ipcp]"); return 0; }
void udld_print(netdissect_options *ndo, const u_char *pptr, u_int length) { int code, type, len; const u_char *tptr; ndo->ndo_protocol = "udld"; if (length < UDLD_HEADER_LEN) goto trunc; tptr = pptr; ND_TCHECK_LEN(tptr, UDLD_HEADER_LEN); code = UDLD_EXTRACT_OPCODE(EXTRACT_U_1(tptr)); ND_PRINT("UDLDv%u, Code %s (%x), Flags [%s] (0x%02x), length %u", UDLD_EXTRACT_VERSION(EXTRACT_U_1(tptr)), tok2str(udld_code_values, "Reserved", code), code, bittok2str(udld_flags_values, "none", EXTRACT_U_1((tptr + 1))), EXTRACT_U_1((tptr + 1)), length); /* * In non-verbose mode, just print version and opcode type */ if (ndo->ndo_vflag < 1) { return; } ND_PRINT("\n\tChecksum 0x%04x (unverified)", EXTRACT_BE_U_2(tptr + 2)); tptr += UDLD_HEADER_LEN; while (tptr < (pptr+length)) { ND_TCHECK_4(tptr); type = EXTRACT_BE_U_2(tptr); len = EXTRACT_BE_U_2(tptr + 2); ND_PRINT("\n\t%s (0x%04x) TLV, length %u", tok2str(udld_tlv_values, "Unknown", type), type, len); if (type == 0) goto invalid; /* infinite loop check */ if (len <= 4) goto invalid; len -= 4; tptr += 4; ND_TCHECK_LEN(tptr, len); switch (type) { case UDLD_DEVICE_ID_TLV: case UDLD_PORT_ID_TLV: case UDLD_DEVICE_NAME_TLV: ND_PRINT(", "); nd_printzp(ndo, tptr, len, NULL); break; case UDLD_ECHO_TLV: ND_PRINT(", "); (void)nd_printn(ndo, tptr, len, NULL); break; case UDLD_MESSAGE_INTERVAL_TLV: case UDLD_TIMEOUT_INTERVAL_TLV: if (len != 1) goto invalid; ND_PRINT(", %us", (EXTRACT_U_1(tptr))); break; case UDLD_SEQ_NUMBER_TLV: if (len != 4) goto invalid; ND_PRINT(", %u", EXTRACT_BE_U_4(tptr)); break; default: break; } tptr += len; } return; invalid: ND_PRINT("%s", istr); return; trunc: nd_print_trunc(ndo); }
void sctp_print(netdissect_options *ndo, const u_char *bp, /* beginning of sctp packet */ const u_char *bp2, /* beginning of enclosing */ u_int sctpPacketLength) /* ip packet */ { u_int sctpPacketLengthRemaining; const struct sctpHeader *sctpPktHdr; const struct ip *ip; const struct ip6_hdr *ip6; uint8_t chunkID; u_short sourcePort, destPort; u_int chunkCount; const struct sctpChunkDesc *chunkDescPtr; const char *sep; int isforces = 0; if (sctpPacketLength < sizeof(struct sctpHeader)) { ND_PRINT("truncated-sctp - %ld bytes missing!", (long)(sizeof(struct sctpHeader) - sctpPacketLength)); return; } sctpPktHdr = (const struct sctpHeader*) bp; ND_TCHECK_SIZE(sctpPktHdr); sctpPacketLengthRemaining = sctpPacketLength; sourcePort = EXTRACT_BE_U_2(sctpPktHdr->source); destPort = EXTRACT_BE_U_2(sctpPktHdr->destination); ip = (const struct ip *)bp2; if (IP_V(ip) == 6) ip6 = (const struct ip6_hdr *)bp2; else ip6 = NULL; if (ip6) { ND_PRINT("%s.%u > %s.%u: sctp", ip6addr_string(ndo, &ip6->ip6_src), sourcePort, ip6addr_string(ndo, &ip6->ip6_dst), destPort); } else { ND_PRINT("%s.%u > %s.%u: sctp", ipaddr_string(ndo, &ip->ip_src), sourcePort, ipaddr_string(ndo, &ip->ip_dst), destPort); } if (isForCES_port(sourcePort)) { ND_PRINT("[%s]", tok2str(ForCES_channels, NULL, sourcePort)); isforces = 1; } if (isForCES_port(destPort)) { ND_PRINT("[%s]", tok2str(ForCES_channels, NULL, destPort)); isforces = 1; } bp += sizeof(struct sctpHeader); sctpPacketLengthRemaining -= sizeof(struct sctpHeader); if (ndo->ndo_vflag >= 2) sep = "\n\t"; else sep = " ("; /* cycle through all chunks, printing information on each one */ for (chunkCount = 0, chunkDescPtr = (const struct sctpChunkDesc *)bp; sctpPacketLengthRemaining != 0; chunkCount++) { uint16_t chunkLength, chunkLengthRemaining; uint16_t align; chunkDescPtr = (const struct sctpChunkDesc *)bp; if (sctpPacketLengthRemaining < sizeof(*chunkDescPtr)) { ND_PRINT("%s%u) [chunk descriptor cut off at end of packet]", sep, chunkCount+1); break; } ND_TCHECK_SIZE(chunkDescPtr); chunkLength = EXTRACT_BE_U_2(chunkDescPtr->chunkLength); if (chunkLength < sizeof(*chunkDescPtr)) { ND_PRINT("%s%u) [Bad chunk length %u, < size of chunk descriptor]", sep, chunkCount+1, chunkLength); break; } chunkLengthRemaining = chunkLength; align = chunkLength % 4; if (align != 0) align = 4 - align; if (sctpPacketLengthRemaining < align) { ND_PRINT("%s%u) [Bad chunk length %u, > remaining data in packet]", sep, chunkCount+1, chunkLength); break; } ND_TCHECK_LEN(bp, chunkLength); bp += sizeof(*chunkDescPtr); sctpPacketLengthRemaining -= sizeof(*chunkDescPtr); chunkLengthRemaining -= sizeof(*chunkDescPtr); ND_PRINT("%s%u) ", sep, chunkCount+1); chunkID = EXTRACT_U_1(chunkDescPtr->chunkID); ND_PRINT("[%s] ", tok2str(sctp_chunkid_str, "Unknown chunk type: 0x%x", chunkID)); switch (chunkID) { case SCTP_DATA : { const struct sctpDataPart *dataHdrPtr; uint8_t chunkFlg; uint32_t ppid; u_int payload_size; chunkFlg = EXTRACT_U_1(chunkDescPtr->chunkFlg); if ((chunkFlg & SCTP_DATA_UNORDERED) == SCTP_DATA_UNORDERED) ND_PRINT("(U)"); if ((chunkFlg & SCTP_DATA_FIRST_FRAG) == SCTP_DATA_FIRST_FRAG) ND_PRINT("(B)"); if ((chunkFlg & SCTP_DATA_LAST_FRAG) == SCTP_DATA_LAST_FRAG) ND_PRINT("(E)"); if( ((chunkFlg & SCTP_DATA_UNORDERED) == SCTP_DATA_UNORDERED) || ((chunkFlg & SCTP_DATA_FIRST_FRAG) == SCTP_DATA_FIRST_FRAG) || ((chunkFlg & SCTP_DATA_LAST_FRAG) == SCTP_DATA_LAST_FRAG) ) ND_PRINT(" "); if (chunkLengthRemaining < sizeof(*dataHdrPtr)) { ND_PRINT("bogus chunk length %u]", chunkLength); return; } dataHdrPtr=(const struct sctpDataPart*)bp; ppid = EXTRACT_BE_U_4(dataHdrPtr->payloadtype); ND_PRINT("[TSN: %u] ", EXTRACT_BE_U_4(dataHdrPtr->TSN)); ND_PRINT("[SID: %u] ", EXTRACT_BE_U_2(dataHdrPtr->streamId)); ND_PRINT("[SSEQ %u] ", EXTRACT_BE_U_2(dataHdrPtr->sequence)); ND_PRINT("[PPID %s] ", tok2str(PayloadProto_idents, "0x%x", ppid)); if (!isforces) { isforces = (ppid == SCTP_PPID_FORCES_HP) || (ppid == SCTP_PPID_FORCES_MP) || (ppid == SCTP_PPID_FORCES_LP); } bp += sizeof(*dataHdrPtr); sctpPacketLengthRemaining -= sizeof(*dataHdrPtr); chunkLengthRemaining -= sizeof(*dataHdrPtr); payload_size = chunkLengthRemaining; if (payload_size == 0) { ND_PRINT("bogus chunk length %u]", chunkLength); return; } if (isforces) { forces_print(ndo, bp, payload_size); } else if (ndo->ndo_vflag >= 2) { /* if verbose output is specified */ /* at the command line */ switch (ppid) { case SCTP_PPID_M3UA : m3ua_print(ndo, bp, payload_size); break; default: ND_PRINT("[Payload"); if (!ndo->ndo_suppress_default_print) { ND_PRINT(":"); ND_DEFAULTPRINT(bp, payload_size); } ND_PRINT("]"); break; } } bp += payload_size; sctpPacketLengthRemaining -= payload_size; chunkLengthRemaining -= payload_size; break; } case SCTP_INITIATION : { const struct sctpInitiation *init; if (chunkLengthRemaining < sizeof(*init)) { ND_PRINT("bogus chunk length %u]", chunkLength); return; } init=(const struct sctpInitiation*)bp; ND_PRINT("[init tag: %u] ", EXTRACT_BE_U_4(init->initTag)); ND_PRINT("[rwnd: %u] ", EXTRACT_BE_U_4(init->rcvWindowCredit)); ND_PRINT("[OS: %u] ", EXTRACT_BE_U_2(init->NumPreopenStreams)); ND_PRINT("[MIS: %u] ", EXTRACT_BE_U_2(init->MaxInboundStreams)); ND_PRINT("[init TSN: %u] ", EXTRACT_BE_U_4(init->initialTSN)); bp += sizeof(*init); sctpPacketLengthRemaining -= sizeof(*init); chunkLengthRemaining -= sizeof(*init); #if 0 /* ALC you can add code for optional params here */ if( chunkLengthRemaining != 0 ) ND_PRINT(" @@@@@ UNFINISHED @@@@@@%s\n", "Optional params present, but not printed."); #endif bp += chunkLengthRemaining; sctpPacketLengthRemaining -= chunkLengthRemaining; chunkLengthRemaining = 0; break; } case SCTP_INITIATION_ACK : { const struct sctpInitiation *init; if (chunkLengthRemaining < sizeof(*init)) { ND_PRINT("bogus chunk length %u]", chunkLength); return; } init=(const struct sctpInitiation*)bp; ND_PRINT("[init tag: %u] ", EXTRACT_BE_U_4(init->initTag)); ND_PRINT("[rwnd: %u] ", EXTRACT_BE_U_4(init->rcvWindowCredit)); ND_PRINT("[OS: %u] ", EXTRACT_BE_U_2(init->NumPreopenStreams)); ND_PRINT("[MIS: %u] ", EXTRACT_BE_U_2(init->MaxInboundStreams)); ND_PRINT("[init TSN: %u] ", EXTRACT_BE_U_4(init->initialTSN)); bp += sizeof(*init); sctpPacketLengthRemaining -= sizeof(*init); chunkLengthRemaining -= sizeof(*init); #if 0 /* ALC you can add code for optional params here */ if( chunkLengthRemaining != 0 ) ND_PRINT(" @@@@@ UNFINISHED @@@@@@%s\n", "Optional params present, but not printed."); #endif bp += chunkLengthRemaining; sctpPacketLengthRemaining -= chunkLengthRemaining; chunkLengthRemaining = 0; break; } case SCTP_SELECTIVE_ACK: { const struct sctpSelectiveAck *sack; const struct sctpSelectiveFrag *frag; u_int fragNo, tsnNo; const u_char *dupTSN; if (chunkLengthRemaining < sizeof(*sack)) { ND_PRINT("bogus chunk length %u]", chunkLength); return; } sack=(const struct sctpSelectiveAck*)bp; ND_PRINT("[cum ack %u] ", EXTRACT_BE_U_4(sack->highestConseqTSN)); ND_PRINT("[a_rwnd %u] ", EXTRACT_BE_U_4(sack->updatedRwnd)); ND_PRINT("[#gap acks %u] ", EXTRACT_BE_U_2(sack->numberOfdesc)); ND_PRINT("[#dup tsns %u] ", EXTRACT_BE_U_2(sack->numDupTsns)); bp += sizeof(*sack); sctpPacketLengthRemaining -= sizeof(*sack); chunkLengthRemaining -= sizeof(*sack); /* print gaps */ for (fragNo=0; chunkLengthRemaining != 0 && fragNo < EXTRACT_BE_U_2(sack->numberOfdesc); bp += sizeof(*frag), sctpPacketLengthRemaining -= sizeof(*frag), chunkLengthRemaining -= sizeof(*frag), fragNo++) { if (chunkLengthRemaining < sizeof(*frag)) { ND_PRINT("bogus chunk length %u]", chunkLength); return; } frag = (const struct sctpSelectiveFrag *)bp; ND_PRINT("\n\t\t[gap ack block #%u: start = %u, end = %u] ", fragNo+1, EXTRACT_BE_U_4(sack->highestConseqTSN) + EXTRACT_BE_U_2(frag->fragmentStart), EXTRACT_BE_U_4(sack->highestConseqTSN) + EXTRACT_BE_U_2(frag->fragmentEnd)); } /* print duplicate TSNs */ for (tsnNo=0; chunkLengthRemaining != 0 && tsnNo<EXTRACT_BE_U_2(sack->numDupTsns); bp += 4, sctpPacketLengthRemaining -= 4, chunkLengthRemaining -= 4, tsnNo++) { if (chunkLengthRemaining < 4) { ND_PRINT("bogus chunk length %u]", chunkLength); return; } dupTSN = (const u_char *)bp; ND_PRINT("\n\t\t[dup TSN #%u: %u] ", tsnNo+1, EXTRACT_BE_U_4(dupTSN)); } break; } default : { bp += chunkLengthRemaining; sctpPacketLengthRemaining -= chunkLengthRemaining; chunkLengthRemaining = 0; break; } } /* * Any extra stuff at the end of the chunk? * XXX - report this? */ bp += chunkLengthRemaining; sctpPacketLengthRemaining -= chunkLengthRemaining; if (ndo->ndo_vflag < 2) sep = ", ("; if (align != 0) { /* * Fail if the alignment padding isn't in the captured data. * Otherwise, skip it. */ ND_TCHECK_LEN(bp, align); bp += align; sctpPacketLengthRemaining -= align; } } return; trunc: ND_PRINT("[|sctp]"); }
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]"); }
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); }