diff --git a/wiretap/libpcap.c b/wiretap/libpcap.c index 0435e880bc..18026b7596 100644 --- a/wiretap/libpcap.c +++ b/wiretap/libpcap.c @@ -55,6 +55,7 @@ typedef struct { guint16 version_major; guint16 version_minor; pcap_variant_t variant; + int fcs_len; void *encap_priv; } libpcap_t; @@ -87,6 +88,62 @@ static gboolean libpcap_dump_pcap_ss991029(wtap_dumper *wdh, static gboolean libpcap_dump_pcap_nokia(wtap_dumper *wdh, const wtap_rec *rec, const guint8 *pd, int *err, gchar **err_info); +/* + * Subfields of the field containing the link-layer header type. + * + * Link-layer header types are assigned for both pcap and + * pcapng, and the same value must work with both. In pcapng, + * the link-layer header type field in an Interface Description + * Block is 16 bits, so only the bottommost 16 bits of the + * link-layer header type in a pcap file can be used for the + * header type value. + * + * In libpcap, the upper 16 bits, from the top down, are divided into: + * + * A 4-bit "FCS length" field, to allow the FCS length to + * be specified, just as it can be specified in the if_fcslen + * field of the pcapng IDB. The field is in units of 16 bits, + * i.e. 1 means 16 bits of FCS, 2 means 32 bits of FCS, etc.. + * + * A reserved bit, which must be zero. + * + * An "FCS length present" flag; if 0, the "FCS length" field + * should be ignored, and if 1, the "FCS length" field should + * be used. + * + * 10 reserved bits, which must be zero. They were originally + * intended to be used as a "class" field, allowing additional + * classes of link-layer types to be defined, with a class value + * of 0 indicating that the link-layer type is a LINKTYPE_ value. + * A value of 0x224 was, at one point, used by NetBSD to define + * "raw" packet types, with the lower 16 bits containing a + * NetBSD AF_ value; see + * + * https://marc.info/?l=tcpdump-workers&m=98296750229149&w=2 + * + * It's unknown whether those were ever used in capture files, + * or if the intent was just to use it as a link-layer type + * for BPF programs; NetBSD's libpcap used to support them in + * the BPF code generator, but it no longer does so. If it + * was ever used in capture files, or if classes other than + * "LINKTYPE_ value" are ever useful in capture files, we could + * re-enable this, and use the reserved 16 bits following the + * link-layer type in pcapng files to hold the class information + * there. (Note, BTW, that LINKTYPE_RAW/DLT_RAW is now being + * interpreted by libpcap, tcpdump, and Wireshark as "raw IP", + * including both IPv4 and IPv6, with the version number in the + * header being checked to see which it is, not just "raw IPv4"; + * there are LINKTYPE_IPV4/DLT_IPV4 and LINKTYPE_IPV6/DLT_IPV6 + * values if "these are IPv{4,6} and only IPv{4,6} packets" + * types are needed.) + */ +#define LT_LINKTYPE(x) ((x) & 0x0000FFFF) +#define LT_CLASS(x) (((x) & 0x3FFF0000) >> 16) +#define LT_CLASS_LINKTYPE 0x0000 +#define LT_FCS_LENGTH_PRESENT(x) ((x) & 0x04000000) +#define LT_FCS_LENGTH(x) (((x) & 0xF0000000) >> 28) +#define LT_FCS_DATALINK_EXT(x) (((x) & 0xF) << 28) | 0x04000000) + /* * Private file type/subtype values; pcap and nanosecond-resolution * pcap are imported from wiretap/file_access.c. @@ -359,51 +416,44 @@ wtap_open_return_val libpcap_open(wtap *wth, int *err, gchar **err_info) break; } } + /* - * Map the "network" field from the header to a Wiretap - * encapsulation. - * - * Link-layer header types are assigned for both pcap and - * pcapng, and the same value must work with both. In pcapng, - * the link-layer header type field in an Interface Description - * Block is 16 bits, so only the bottommost 16 bits of the - * link-layer header type in a pcap file can be used for the - * header type value. - * - * In libpcap, the upper 16 bits are divided into: - * - * A "class" field, to support non-standard link-layer - * header types; class 0 is for standard header types, - * class 0x224 was reserved for a NetBSD feature, and - * all other class values are reserved. That is in the - * lower 10 bits of the upper 16 bits. - * - * An "FCS length" field, to allow the FCS length to - * be specified, just as it can be specified in the - * if_fcslen field of the pcapng IDB. That is in the - * topmost 4 bits of the upper 16 bits. The field is - * in units of 16 bits, i.e. 1 means 16 bits of FCS, - * 2 means 32 bits of FCS, etc.. - * - * An "FCS length present" flag; if 0, the "FCS length" - * field should be ignored, and if 1, the "FCS length" - * field should be used. That is in the bit just above - * the "class" field. - * - * The one remaining bit is reserved. - * - * We ignore the FCS information and reserved bit; we include - * the "class" field, in case there's ever a need to implement - * it - currently, any link-layer header type with a non-zero - * class value will fail. + * Check the "class" field of the "network" field in the header. */ - wth->file_encap = wtap_pcap_encap_to_wtap_encap(hdr.network & 0x03FFFFFF); - if (wth->file_encap == WTAP_ENCAP_UNKNOWN) { + switch (LT_CLASS(hdr.network)) { + + case LT_CLASS_LINKTYPE: + /* + * Map the link-layer type from the "network" field in + * the header to a Wiretap encapsulation. + */ + wth->file_encap = wtap_pcap_encap_to_wtap_encap(LT_LINKTYPE(hdr.network)); + if (wth->file_encap == WTAP_ENCAP_UNKNOWN) { + *err = WTAP_ERR_UNSUPPORTED; + *err_info = ws_strdup_printf("pcap: network type %u unknown or unsupported", + hdr.network); + return WTAP_OPEN_ERROR; + } + break; + + default: *err = WTAP_ERR_UNSUPPORTED; - *err_info = ws_strdup_printf("pcap: network type %u unknown or unsupported", - hdr.network); + *err_info = ws_strdup_printf("pcap: network type class 0x%04x not supported", + LT_CLASS(hdr.network)); return WTAP_OPEN_ERROR; } + + /* + * Extract the FCS information, if present. + */ + libpcap->fcs_len = -1; + if (LT_FCS_LENGTH_PRESENT(hdr.network)) { + /* + * We have an FCS length. + */ + libpcap->fcs_len = LT_FCS_LENGTH(hdr.network) * 16; + } + libpcap->encap_priv = NULL; /* @@ -967,7 +1017,7 @@ libpcap_read_packet(wtap *wth, FILE_T fh, wtap_rec *rec, return FALSE; /* failed */ pcap_read_post_process(is_nokia, wth->file_encap, rec, - ws_buffer_start_ptr(buf), libpcap->byte_swapped, -1); + ws_buffer_start_ptr(buf), libpcap->byte_swapped, libpcap->fcs_len); return TRUE; }