diff options
author | Guy Harris <guy@alum.mit.edu> | 2019-04-23 15:26:02 -0700 |
---|---|---|
committer | Guy Harris <guy@alum.mit.edu> | 2019-04-23 15:26:02 -0700 |
commit | b258556bb81a53e61ad3a2ce642b50959e0ddc06 (patch) | |
tree | 9240006737c1cc4911a705edb6b1f8b14c52ae9a /print-ether.c | |
parent | d338a0a1a95d021e9654b4c80ee64fb4d3e40827 (diff) | |
download | tcpdump-b258556bb81a53e61ad3a2ce642b50959e0ddc06.tar.gz |
Handle switch tags more cleanly.
Have the switch tag dissectors handle *only* the switch tag, not
anything else in the Ethernet header.
Have a routine ether_print_switch_tag() that takes a pointer to a
routine to dissect the switch tag, and a switch tag length, as an
argument, and have a common Ethernet dissection routine called by
ether_print_switch_tag() and by ether_print(), passing a null pointer
for the switch tag dissector and 0 for the switch tag length.
Dissect the switch tag after the MAC addresses, if there's a non-null
switch tag routine dissector pointer.
Clean up the processing logic in the common Ethernet dissection code -
have a loop to process VLAN tags, if any, and, when it's done, handle
frames with a length field, frames with a regular type field, and Alteon
jumbo frames.
Diffstat (limited to 'print-ether.c')
-rw-r--r-- | print-ether.c | 269 |
1 files changed, 159 insertions, 110 deletions
diff --git a/print-ether.c b/print-ether.c index 4e0d6f0f..6202a699 100644 --- a/print-ether.c +++ b/print-ether.c @@ -104,136 +104,112 @@ const struct tok ethertype_values[] = { }; static void -ether_hdr_print(netdissect_options *ndo, - const u_char *bp, u_int length, - u_int hdrlen) +ether_addresses_print(netdissect_options *ndo, const u_char *src, + const u_char *dst) { - const struct ether_header *ehp; - uint16_t length_type; - - ehp = (const struct ether_header *)bp; - - ND_PRINT("%s > %s", - etheraddr_string(ndo, ehp->ether_shost), - etheraddr_string(ndo, ehp->ether_dhost)); + ND_PRINT("%s > %s, ", + etheraddr_string(ndo, src), etheraddr_string(ndo, dst)); +} - length_type = GET_BE_U_2(bp + (hdrlen - sizeof(ehp->ether_length_type))); - if (length_type <= MAX_ETHERNET_LENGTH_VAL) { - /* - * It's a length field. - */ - ND_PRINT(", 802.3, length %u", length_type); - if (length_type > length - hdrlen) - ND_PRINT(" (too large, > %u)", length - hdrlen); - ND_PRINT(": "); - } else { - /* - * It's a type field. - */ - if (!ndo->ndo_qflag) - ND_PRINT(", ethertype %s (0x%04x), length %u: ", - tok2str(ethertype_values,"Unknown", length_type), - length_type, length); - else - ND_PRINT(", %s, length %u: ", - tok2str(ethertype_values,"Unknown Ethertype (0x%04x)", length_type), - length); - } +static void +ether_type_print(netdissect_options *ndo, uint16_t type) +{ + if (!ndo->ndo_qflag) + ND_PRINT("ethertype %s (0x%04x)", + tok2str(ethertype_values, "Unknown", type), type); + else + ND_PRINT("%s", + tok2str(ethertype_values, "Unknown Ethertype (0x%04x)", type)); } /* - * Print an Ethernet frame while specyfing a non-standard Ethernet header - * length. - * This might be encapsulated within another frame; we might be passed - * a pointer to a function that can print header information for that - * frame's protocol, and an argument to pass to that function. + * Common code for printing Ethernet frames. * - * FIXME: caplen can and should be derived from ndo->ndo_snapend and p. + * It can handle Ethernet headers with extra tag information inserted + * after the destination and source addresses, as is inserted by some + * switch chips, and extra encapsulation header information before + * printing Ethernet header information (such as a LANE ID for ATM LANE). */ -u_int -ether_hdr_len_print(netdissect_options *ndo, - const u_char *p, u_int length, u_int caplen, - void (*print_encap_header)(netdissect_options *ndo, const u_char *), - const u_char *encap_header_arg, u_int hdrlen) +static u_int +ether_print_common(netdissect_options *ndo, const u_char *p, u_int length, + u_int caplen, + void (*print_switch_tag)(netdissect_options *ndo, const u_char *), + u_int switch_tag_len, + void (*print_encap_header)(netdissect_options *ndo, const u_char *), + const u_char *encap_header_arg) { const struct ether_header *ehp; u_int orig_length; + u_int hdrlen; u_short length_type; + int printed_length; int llc_hdrlen; struct lladdr_info src, dst; - /* Unless specified otherwise, assume a standard Ethernet header */ - if (hdrlen == ETHER_HDRLEN) - ndo->ndo_protocol = "ether"; - - if (caplen < hdrlen) { + if (caplen < ETHER_HDRLEN + switch_tag_len) { nd_print_trunc(ndo); return (caplen); } - if (length < hdrlen) { + if (length < ETHER_HDRLEN + switch_tag_len) { nd_print_trunc(ndo); return (length); } - /* If the offset is set, then the upper printer is responsible for - * printing the relevant part of the Ethernet header. - */ - if (ndo->ndo_eflag) { - if (print_encap_header != NULL) - (*print_encap_header)(ndo, encap_header_arg); - ether_hdr_print(ndo, p, length, hdrlen); - } + if (print_encap_header != NULL) + (*print_encap_header)(ndo, encap_header_arg); orig_length = length; - length -= hdrlen; - caplen -= hdrlen; + /* + * Get the source and destination addresses, skip past them, + * and print them if we're printing the link-layer header. + */ ehp = (const struct ether_header *)p; - p += hdrlen; - src.addr = ehp->ether_shost; src.addr_string = etheraddr_string; dst.addr = ehp->ether_dhost; dst.addr_string = etheraddr_string; - length_type = GET_BE_U_2((const u_char *)ehp + (hdrlen - sizeof(ehp->ether_length_type))); -recurse: + length -= 2*MAC_ADDR_LEN; + caplen -= 2*MAC_ADDR_LEN; + p += 2*MAC_ADDR_LEN; + hdrlen = 2*MAC_ADDR_LEN; + + if (ndo->ndo_eflag) + ether_addresses_print(ndo, src.addr, dst.addr); + /* - * Is it (gag) an 802.3 encapsulation? + * Print the switch tag, if we have one, and skip past it. */ - if (length_type <= MAX_ETHERNET_LENGTH_VAL) { - /* - * The length/type field contains the length of the - * remaining payload; use it as such, as long as - * it's not too large (bigger than the actual payload). - */ - if (length_type < length) { - length = length_type; - if (caplen > length) - caplen = length; - } + if (print_switch_tag != NULL) + (*print_switch_tag)(ndo, p); - /* - * Cut off the snapshot length to the end of the payload. - */ - nd_push_snapend(ndo, p + length); + length -= switch_tag_len; + caplen -= switch_tag_len; + p += switch_tag_len; + hdrlen += switch_tag_len; - /* Try to print the LLC-layer header & higher layers */ - llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst); - if (llc_hdrlen < 0) { - /* packet type not known, print raw packet */ - if (!ndo->ndo_suppress_default_print) - ND_DEFAULTPRINT(p, caplen); - llc_hdrlen = -llc_hdrlen; - } - hdrlen += llc_hdrlen; - nd_pop_packet_info(ndo); - return (hdrlen); - } else if (length_type == ETHERTYPE_8021Q || + /* + * Get the length/type field, skip past it, and print it + * if we're printing the link-layer header. + */ + length_type = GET_BE_U_2(p); + + length -= 2; + caplen += 2; + p += 2; + hdrlen += 2; + + /* + * Process VLAN tag types. + */ + printed_length = 0; + while (length_type == ETHERTYPE_8021Q || length_type == ETHERTYPE_8021Q9100 || length_type == ETHERTYPE_8021Q9200 || length_type == ETHERTYPE_8021QinQ) { /* + * It has a VLAN tag. * Print VLAN information, and then go back and process * the enclosed type field. */ @@ -250,27 +226,65 @@ recurse: if (ndo->ndo_eflag) { uint16_t tag = GET_BE_U_2(p); + ether_type_print(ndo, length_type); + if (!printed_length) { + ND_PRINT(", length %u: ", orig_length); + printed_length = 1; + } else + ND_PRINT(", "); ND_PRINT("%s, ", ieee8021q_tci_string(tag)); } length_type = GET_BE_U_2(p + 2); - if (ndo->ndo_eflag && length_type > MAX_ETHERNET_LENGTH_VAL) { - if (!ndo->ndo_qflag) - ND_PRINT("ethertype %s (0x%04x), ", - tok2str(ethertype_values,"Unknown", length_type), - length_type); - else - ND_PRINT("%s, ", - tok2str(ethertype_values,"Unknown Ethertype (0x%04x)", length_type)); - } p += 4; length -= 4; caplen -= 4; hdrlen += 4; - goto recurse; + } + + /* + * We now have the final length/type field. + */ + if (length_type <= MAX_ETHERNET_LENGTH_VAL) { + /* + * It's a length field, containing the length of the + * remaining payload; use it as such, as long as + * it's not too large (bigger than the actual payload). + */ + if (length_type < length) { + length = length_type; + if (caplen > length) + caplen = length; + } + + /* + * Cut off the snapshot length to the end of the + * payload. + */ + nd_push_snapend(ndo, p + length); + + if (ndo->ndo_eflag) { + ND_PRINT("802.3"); + if (!printed_length) + ND_PRINT(", length %u: ", length); + } + + /* + * An LLC header follows the length. Print that and + * higher layers. + */ + llc_hdrlen = llc_print(ndo, p, length, caplen, &src, &dst); + if (llc_hdrlen < 0) { + /* packet type not known, print raw packet */ + if (!ndo->ndo_suppress_default_print) + ND_DEFAULTPRINT(p, caplen); + llc_hdrlen = -llc_hdrlen; + } + hdrlen += llc_hdrlen; + nd_pop_packet_info(ndo); } else if (length_type == ETHERTYPE_JUMBO) { /* - * Alteon jumbo frames. + * It's a type field, with the type for Alteon jumbo frames. * See * * http://tools.ietf.org/html/draft-ietf-isis-ext-eth-01 @@ -288,13 +302,29 @@ recurse: } hdrlen += llc_hdrlen; } else { + /* + * It's a type field with some other value. + */ + if (ndo->ndo_eflag) { + ether_type_print(ndo, length_type); + if (!printed_length) + ND_PRINT(", length %u: ", orig_length); + else + ND_PRINT(", "); + } if (ethertype_print(ndo, length_type, p, length, caplen, &src, &dst) == 0) { /* type not known, print raw packet */ if (!ndo->ndo_eflag) { - if (print_encap_header != NULL) - (*print_encap_header)(ndo, encap_header_arg); - ether_hdr_print(ndo, (const u_char *)ehp, orig_length, - hdrlen); + /* + * We didn't print the full link-layer + * header, as -e wasn't specified, so + * print only the source and destination + * MAC addresses and the final Ethernet + * type. + */ + ether_addresses_print(ndo, src.addr, dst.addr); + ether_type_print(ndo, length_type); + ND_PRINT(", length %u: ", orig_length); } if (!ndo->ndo_suppress_default_print) @@ -305,6 +335,25 @@ recurse: } /* + * Print an Ethernet frame while specyfing a non-standard Ethernet header + * length. + * This might be encapsulated within another frame; we might be passed + * a pointer to a function that can print header information for that + * frame's protocol, and an argument to pass to that function. + * + * FIXME: caplen can and should be derived from ndo->ndo_snapend and p. + */ +u_int +ether_print_switch_tag(netdissect_options *ndo, const u_char *p, u_int length, + u_int caplen, + void (*print_switch_tag)(netdissect_options *, const u_char *), + u_int switch_tag_len) +{ + return (ether_print_common(ndo, p, length, caplen, print_switch_tag, + switch_tag_len, NULL, NULL)); +} + +/* * Print an Ethernet frame. * This might be encapsulated within another frame; we might be passed * a pointer to a function that can print header information for that @@ -318,9 +367,9 @@ ether_print(netdissect_options *ndo, void (*print_encap_header)(netdissect_options *ndo, const u_char *), const u_char *encap_header_arg) { - return (ether_hdr_len_print(ndo, p, length, caplen, - print_encap_header, encap_header_arg, - ETHER_HDRLEN)); + ndo->ndo_protocol = "ether"; + return (ether_print_common(ndo, p, length, caplen, NULL, 0, + print_encap_header, encap_header_arg)); } /* |