Ethernet: Allow specifying non-standard Ethernet header length

A fair number of proprietary Ethernet switch tagging protocols, such as
Broadcom tags for instance, will place their tag between the MAC SA and
the Type/Length field.

Move the body of ether_print() into ether_print_hdr_len() and specify
the Ethernet header length as an argument to that function.
ether_print() calls ether_print_hdr_len() with a standard Ethernet
header lenght of 14 bytes, while other callers could specify an
arbitrary length. We still assume that the first Length/Type field to
parse is located 2 bytes before the end of that Ethernet header length.

This will be used in a subsequent commit to parse Broadcom tags.
This commit is contained in:
Florian Fainelli 2019-01-18 15:19:38 -08:00
parent 0b3880c91e
commit 48e290d807
2 changed files with 46 additions and 16 deletions

View File

@ -538,6 +538,7 @@ extern void egp_print(netdissect_options *, const u_char *, u_int);
extern void eigrp_print(netdissect_options *, const u_char *, u_int);
extern int esp_print(netdissect_options *, const u_char *, const int, const u_char *, u_int *, u_int *);
extern u_int ether_print(netdissect_options *, const u_char *, u_int, u_int, void (*)(netdissect_options *, const u_char *), const u_char *);
extern u_int ether_print_hdr_len(netdissect_options *, const u_char *, u_int, u_int, void (*)(netdissect_options *, const u_char *), const u_char *, u_int);
extern int ethertype_print(netdissect_options *, u_short, const u_char *, u_int, u_int, const struct lladdr_info *, const struct lladdr_info *);
extern u_int fddi_print(netdissect_options *, const u_char *, u_int, u_int);
extern void forces_print(netdissect_options *, const u_char *, u_int);

View File

@ -106,7 +106,8 @@ const struct tok ethertype_values[] = {
static void
ether_hdr_print(netdissect_options *ndo,
const u_char *bp, u_int length)
const u_char *bp, u_int length,
u_int hdrlen)
{
const struct ether_header *ehp;
uint16_t length_type;
@ -117,7 +118,8 @@ ether_hdr_print(netdissect_options *ndo,
etheraddr_string(ndo, ehp->ether_shost),
etheraddr_string(ndo, ehp->ether_dhost));
length_type = EXTRACT_BE_U_2(ehp->ether_length_type);
length_type = EXTRACT_BE_U_2(bp +
(hdrlen - sizeof(ehp->ether_length_type)));
if (!ndo->ndo_qflag) {
if (length_type <= MAX_ETHERNET_LENGTH_VAL) {
ND_PRINT(", 802.3");
@ -138,7 +140,8 @@ ether_hdr_print(netdissect_options *ndo,
}
/*
* Print an Ethernet frame.
* 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.
@ -146,46 +149,52 @@ ether_hdr_print(netdissect_options *ndo,
* FIXME: caplen can and should be derived from ndo->ndo_snapend and p.
*/
u_int
ether_print(netdissect_options *ndo,
ether_print_hdr_len(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)
const u_char *encap_header_arg, u_int hdrlen)
{
const struct ether_header *ehp;
u_int orig_length;
u_short length_type;
u_int hdrlen;
int llc_hdrlen;
struct lladdr_info src, dst;
ndo->ndo_protocol = "ether";
if (caplen < ETHER_HDRLEN) {
/* Unless specified otherwise, assume a standard Ethernet header */
if (hdrlen == ETHER_HDRLEN)
ndo->ndo_protocol = "ether";
if (caplen < hdrlen) {
nd_print_trunc(ndo);
return (caplen);
}
if (length < ETHER_HDRLEN) {
if (length < hdrlen) {
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);
ether_hdr_print(ndo, p, length, hdrlen);
}
orig_length = length;
length -= ETHER_HDRLEN;
caplen -= ETHER_HDRLEN;
length -= hdrlen;
caplen -= hdrlen;
ehp = (const struct ether_header *)p;
p += ETHER_HDRLEN;
hdrlen = ETHER_HDRLEN;
p += hdrlen;
src.addr = ehp->ether_shost;
src.addr_string = etheraddr_string;
dst.addr = ehp->ether_dhost;
dst.addr_string = etheraddr_string;
length_type = EXTRACT_BE_U_2(ehp->ether_length_type);
length_type = EXTRACT_BE_U_2((const u_char *)ehp +
(hdrlen - sizeof(ehp->ether_length_type)));
recurse:
/*
@ -258,7 +267,8 @@ recurse:
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);
ether_hdr_print(ndo, (const u_char *)ehp, orig_length,
hdrlen);
}
if (!ndo->ndo_suppress_default_print)
@ -268,6 +278,25 @@ recurse:
return (hdrlen);
}
/*
* 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
* 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(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)
{
return (ether_print_hdr_len(ndo, p, length, caplen,
print_encap_header, encap_header_arg,
ETHER_HDRLEN));
}
/*
* This is the top level routine of the printer. 'p' points
* to the ether header of the packet, 'h->len' is the length