/* * Print out a file name and return pointer to 32-bit word past it. * If packet was truncated, return 0. */ static const uint32_t * parsefn(netdissect_options *ndo, const uint32_t *dp) { uint32_t len; const u_char *cp; /* Bail if we don't have the string length */ ND_TCHECK_4(dp); /* Fetch big-endian string length */ len = EXTRACT_BE_U_4(dp); dp++; ND_TCHECK_LEN(dp, ((len + 3) & ~3)); cp = (const u_char *)dp; /* Update 32-bit pointer (NFS filenames padded to 32-bit boundaries) */ dp += ((len + 3) & ~3) / sizeof(*dp); ND_PRINT("\""); if (nd_printn(ndo, cp, len, ndo->ndo_snapend)) { ND_PRINT("\""); goto trunc; } ND_PRINT("\""); return (dp); trunc: return NULL; }
void hsrp_print(netdissect_options *ndo, const u_char *bp, u_int len) { const struct hsrp *hp = (const struct hsrp *) bp; uint8_t version; ndo->ndo_protocol = "hsrp"; ND_TCHECK_1(hp->hsrp_version); version = GET_U_1(hp->hsrp_version); ND_PRINT("HSRPv%u", version); if (version != 0) return; ND_TCHECK_1(hp->hsrp_op_code); ND_PRINT("-"); ND_PRINT("%s ", tok2strary(op_code_str, "unknown (%u)", GET_U_1(hp->hsrp_op_code))); ND_PRINT("%u: ", len); ND_TCHECK_1(hp->hsrp_state); ND_PRINT("state=%s ", tok2str(states, "Unknown (%u)", GET_U_1(hp->hsrp_state))); ND_TCHECK_1(hp->hsrp_group); ND_PRINT("group=%u ", GET_U_1(hp->hsrp_group)); ND_TCHECK_1(hp->hsrp_reserved); if (GET_U_1(hp->hsrp_reserved) != 0) { ND_PRINT("[reserved=%u!] ", GET_U_1(hp->hsrp_reserved)); } ND_TCHECK_4(hp->hsrp_virtaddr); ND_PRINT("addr=%s", ipaddr_string(ndo, hp->hsrp_virtaddr)); if (ndo->ndo_vflag) { ND_PRINT(" hellotime="); unsigned_relts_print(ndo, GET_U_1(hp->hsrp_hellotime)); ND_PRINT(" holdtime="); unsigned_relts_print(ndo, GET_U_1(hp->hsrp_holdtime)); ND_PRINT(" priority=%u", GET_U_1(hp->hsrp_priority)); ND_PRINT(" auth=\""); if (nd_printn(ndo, hp->hsrp_authdata, sizeof(hp->hsrp_authdata), ndo->ndo_snapend)) { ND_PRINT("\""); goto trunc; } ND_PRINT("\""); } return; trunc: nd_print_trunc(ndo); }
/* * Print NTP time requests and responses */ static void ntp_time_print(netdissect_options *ndo, const struct ntp_time_data *bp, u_int length) { uint8_t stratum; if (length < NTP_TIMEMSG_MINLEN) goto invalid; ND_TCHECK_1(bp->stratum); stratum = EXTRACT_U_1(bp->stratum); ND_PRINT(", Stratum %u (%s)", stratum, tok2str(ntp_stratum_values, (stratum >=2 && stratum<=15) ? "secondary reference" : "reserved", stratum)); ND_TCHECK_1(bp->ppoll); ND_PRINT(", poll %d", EXTRACT_S_1(bp->ppoll)); p_poll(ndo, EXTRACT_S_1(bp->ppoll)); ND_TCHECK_1(bp->precision); ND_PRINT(", precision %d", EXTRACT_S_1(bp->precision)); ND_TCHECK_SIZE(&bp->root_delay); ND_PRINT("\n\tRoot Delay: "); p_sfix(ndo, &bp->root_delay); ND_TCHECK_SIZE(&bp->root_dispersion); ND_PRINT(", Root dispersion: "); p_sfix(ndo, &bp->root_dispersion); ND_TCHECK_4(bp->refid); ND_PRINT(", Reference-ID: "); /* Interpretation depends on stratum */ switch (stratum) { case UNSPECIFIED: ND_PRINT("(unspec)"); break; case PRIM_REF: if (nd_printn(ndo, (const u_char *)&(bp->refid), 4, ndo->ndo_snapend)) goto trunc; break; case INFO_QUERY: ND_PRINT("%s INFO_QUERY", ipaddr_string(ndo, bp->refid)); /* this doesn't have more content */ return; case INFO_REPLY: ND_PRINT("%s INFO_REPLY", ipaddr_string(ndo, bp->refid)); /* this is too complex to be worth printing */ return; default: /* In NTPv4 (RFC 5905) refid is an IPv4 address or first 32 bits of MD5 sum of IPv6 address */ ND_PRINT("0x%08x", EXTRACT_BE_U_4(bp->refid)); break; } ND_TCHECK_SIZE(&bp->ref_timestamp); ND_PRINT("\n\t Reference Timestamp: "); p_ntp_time(ndo, &(bp->ref_timestamp)); ND_TCHECK_SIZE(&bp->org_timestamp); ND_PRINT("\n\t Originator Timestamp: "); p_ntp_time(ndo, &(bp->org_timestamp)); ND_TCHECK_SIZE(&bp->rec_timestamp); ND_PRINT("\n\t Receive Timestamp: "); p_ntp_time(ndo, &(bp->rec_timestamp)); ND_TCHECK_SIZE(&bp->xmt_timestamp); ND_PRINT("\n\t Transmit Timestamp: "); p_ntp_time(ndo, &(bp->xmt_timestamp)); ND_PRINT("\n\t Originator - Receive Timestamp: "); p_ntp_delta(ndo, &(bp->org_timestamp), &(bp->rec_timestamp)); ND_PRINT("\n\t Originator - Transmit Timestamp: "); p_ntp_delta(ndo, &(bp->org_timestamp), &(bp->xmt_timestamp)); /* FIXME: this code is not aware of any extension fields */ if (length == NTP_TIMEMSG_MINLEN + 4) { /* Optional: key-id (crypto-NAK) */ ND_TCHECK_4(bp->key_id); ND_PRINT("\n\tKey id: %u", EXTRACT_BE_U_4(bp->key_id)); } else if (length == NTP_TIMEMSG_MINLEN + 4 + 16) { /* Optional: key-id + 128-bit digest */ ND_TCHECK_4(bp->key_id); ND_PRINT("\n\tKey id: %u", EXTRACT_BE_U_4(bp->key_id)); ND_TCHECK_LEN(bp->message_digest, 16); ND_PRINT("\n\tAuthentication: %08x%08x%08x%08x", EXTRACT_BE_U_4(bp->message_digest), EXTRACT_BE_U_4(bp->message_digest + 4), EXTRACT_BE_U_4(bp->message_digest + 8), EXTRACT_BE_U_4(bp->message_digest + 12)); } else if (length == NTP_TIMEMSG_MINLEN + 4 + 20) { /* Optional: key-id + 160-bit digest */ ND_TCHECK_4(bp->key_id); ND_PRINT("\n\tKey id: %u", EXTRACT_BE_U_4(bp->key_id)); ND_TCHECK_LEN(bp->message_digest, 20); ND_PRINT("\n\tAuthentication: %08x%08x%08x%08x%08x", EXTRACT_BE_U_4(bp->message_digest), EXTRACT_BE_U_4(bp->message_digest + 4), EXTRACT_BE_U_4(bp->message_digest + 8), EXTRACT_BE_U_4(bp->message_digest + 12), EXTRACT_BE_U_4(bp->message_digest + 16)); } else if (length > NTP_TIMEMSG_MINLEN) { ND_PRINT("\n\t(%u more bytes after the header)", length - NTP_TIMEMSG_MINLEN); } return; invalid: nd_print_invalid(ndo); ND_TCHECK_LEN(bp, length); return; trunc: nd_print_trunc(ndo); }
/* * Print a single PDU. */ static u_int rpki_rtr_pdu_print(netdissect_options *ndo, const u_char *tptr, const u_int len, const u_char recurse, const u_int indent) { const rpki_rtr_pdu *pdu_header; u_int pdu_type, pdu_len, hexdump; const u_char *msg; /* Protocol Version */ ND_TCHECK_1(tptr); if (GET_U_1(tptr) != 0) { /* Skip the rest of the input buffer because even if this is * a well-formed PDU of a future RPKI-Router protocol version * followed by a well-formed PDU of RPKI-Router protocol * version 0, there is no way to know exactly how to skip the * current PDU. */ ND_PRINT("%sRPKI-RTRv%u (unknown)", indent_string(8), GET_U_1(tptr)); return len; } if (len < sizeof(rpki_rtr_pdu)) { ND_PRINT("(%u bytes is too few to decode)", len); goto invalid; } ND_TCHECK_LEN(tptr, sizeof(rpki_rtr_pdu)); pdu_header = (const rpki_rtr_pdu *)tptr; pdu_type = pdu_header->pdu_type; pdu_len = GET_BE_U_4(pdu_header->length); /* Do not check bounds with pdu_len yet, do it in the case blocks * below to make it possible to decode at least the beginning of * a truncated Error Report PDU or a truncated encapsulated PDU. */ hexdump = FALSE; ND_PRINT("%sRPKI-RTRv%u, %s PDU (%u), length: %u", indent_string(8), pdu_header->version, tok2str(rpki_rtr_pdu_values, "Unknown", pdu_type), pdu_type, pdu_len); if (pdu_len < sizeof(rpki_rtr_pdu) || pdu_len > len) goto invalid; switch (pdu_type) { /* * The following PDUs share the message format. */ case RPKI_RTR_SERIAL_NOTIFY_PDU: case RPKI_RTR_SERIAL_QUERY_PDU: case RPKI_RTR_END_OF_DATA_PDU: if (pdu_len != sizeof(rpki_rtr_pdu) + 4) goto invalid; ND_TCHECK_LEN(tptr, pdu_len); msg = (const u_char *)(pdu_header + 1); ND_PRINT("%sSession ID: 0x%04x, Serial: %u", indent_string(indent+2), GET_BE_U_2(pdu_header->u.session_id), GET_BE_U_4(msg)); break; /* * The following PDUs share the message format. */ case RPKI_RTR_RESET_QUERY_PDU: case RPKI_RTR_CACHE_RESET_PDU: if (pdu_len != sizeof(rpki_rtr_pdu)) goto invalid; /* no additional boundary to check */ /* * Zero payload PDUs. */ break; case RPKI_RTR_CACHE_RESPONSE_PDU: if (pdu_len != sizeof(rpki_rtr_pdu)) goto invalid; /* no additional boundary to check */ ND_PRINT("%sSession ID: 0x%04x", indent_string(indent+2), GET_BE_U_2(pdu_header->u.session_id)); break; case RPKI_RTR_IPV4_PREFIX_PDU: { const rpki_rtr_pdu_ipv4_prefix *pdu; if (pdu_len != sizeof(rpki_rtr_pdu) + 12) goto invalid; ND_TCHECK_LEN(tptr, pdu_len); pdu = (const rpki_rtr_pdu_ipv4_prefix *)tptr; ND_PRINT("%sIPv4 Prefix %s/%u-%u, origin-as %u, flags 0x%02x", indent_string(indent+2), ipaddr_string(ndo, pdu->prefix), pdu->prefix_length, pdu->max_length, GET_BE_U_4(pdu->as), pdu->flags); } break; case RPKI_RTR_IPV6_PREFIX_PDU: { const rpki_rtr_pdu_ipv6_prefix *pdu; if (pdu_len != sizeof(rpki_rtr_pdu) + 24) goto invalid; ND_TCHECK_LEN(tptr, pdu_len); pdu = (const rpki_rtr_pdu_ipv6_prefix *)tptr; ND_PRINT("%sIPv6 Prefix %s/%u-%u, origin-as %u, flags 0x%02x", indent_string(indent+2), ip6addr_string(ndo, pdu->prefix), pdu->prefix_length, pdu->max_length, GET_BE_U_4(pdu->as), pdu->flags); } break; case RPKI_RTR_ERROR_REPORT_PDU: { const rpki_rtr_pdu_error_report *pdu; u_int encapsulated_pdu_length, text_length, tlen, error_code; tlen = sizeof(rpki_rtr_pdu); /* Do not test for the "Length of Error Text" data element yet. */ if (pdu_len < tlen + 4) goto invalid; ND_TCHECK_LEN(tptr, tlen + 4); /* Safe up to and including the "Length of Encapsulated PDU" * data element, more data elements may be present. */ pdu = (const rpki_rtr_pdu_error_report *)tptr; encapsulated_pdu_length = GET_BE_U_4(pdu->encapsulated_pdu_length); tlen += 4; error_code = GET_BE_U_2(pdu->pdu_header.u.error_code); ND_PRINT("%sError code: %s (%u), Encapsulated PDU length: %u", indent_string(indent+2), tok2str(rpki_rtr_error_codes, "Unknown", error_code), error_code, encapsulated_pdu_length); if (encapsulated_pdu_length) { /* Section 5.10 of RFC 6810 says: * "An Error Report PDU MUST NOT be sent for an Error Report PDU." * * However, as far as the protocol encoding goes Error Report PDUs can * happen to be nested in each other, however many times, in which case * the decoder should still print such semantically incorrect PDUs. * * That said, "the Erroneous PDU field MAY be truncated" (ibid), thus * to keep things simple this implementation decodes only the two * outermost layers of PDUs and makes bounds checks in the outer and * the inner PDU independently. */ if (pdu_len < tlen + encapsulated_pdu_length) goto invalid; if (! recurse) { ND_TCHECK_LEN(tptr, tlen + encapsulated_pdu_length); } else { ND_PRINT("%s-----encapsulated PDU-----", indent_string(indent+4)); rpki_rtr_pdu_print(ndo, tptr + tlen, encapsulated_pdu_length, 0, indent + 2); } tlen += encapsulated_pdu_length; } if (pdu_len < tlen + 4) goto invalid; ND_TCHECK_LEN(tptr, tlen + 4); /* Safe up to and including the "Length of Error Text" data element, * one more data element may be present. */ /* * Extract, trail-zero and print the Error message. */ text_length = GET_BE_U_4(tptr + tlen); tlen += 4; if (text_length) { if (pdu_len < tlen + text_length) goto invalid; /* nd_printn() makes the bounds check */ ND_PRINT("%sError text: ", indent_string(indent+2)); if (nd_printn(ndo, tptr + tlen, text_length, ndo->ndo_snapend)) goto trunc; } } break; default: ND_TCHECK_LEN(tptr, pdu_len); /* * Unknown data, please hexdump. */ hexdump = TRUE; } /* do we also want to see a hex dump ? */ if (ndo->ndo_vflag > 1 || (ndo->ndo_vflag && hexdump)) { print_unknown_data(ndo,tptr,"\n\t ", pdu_len); } return pdu_len; invalid: nd_print_invalid(ndo); ND_TCHECK_LEN(tptr, len); return len; trunc: nd_print_trunc(ndo); return len; }
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; }
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); }
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); }