aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuy Harris <gharris@sonic.net>2022-06-03 18:00:22 -0700
committerGuy Harris <gharris@sonic.net>2022-06-15 19:25:03 -0700
commite17af17e6e25057a72918264428aecb67a6dd23c (patch)
treef914c62f547086ba1c1f62e2376a79ce7425fe8a
parent1c073ea17ecc34f7b858650808d9bac143e9389c (diff)
downloadlibpcap-e17af17e6e25057a72918264428aecb67a6dd23c.tar.gz
Linux USB: fix incorrect values for the packet length.
Correctly compute the "real" length for isochronous transfers. When reading memory-mapped Linux capture files, fix up the "real" length field, in case the file was written by a program doing a capture with the bug. (backported from commit 08ab69f4fc5d432eb7de26ee8e33b40ea4b79744)
-rw-r--r--CMakeLists.txt1
-rw-r--r--Makefile.in3
-rw-r--r--pcap-common.c18
-rw-r--r--pcap-common.h3
-rw-r--r--pcap-usb-linux-common.c108
-rw-r--r--pcap-usb-linux-common.h26
-rw-r--r--pcap-usb-linux.c28
-rw-r--r--sf-pcap.c2
-rw-r--r--sf-pcapng.c2
9 files changed, 171 insertions, 20 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c6894e86..c8601a65 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1062,6 +1062,7 @@ set(PROJECT_SOURCE_LIST_C
nametoaddr.c
optimize.c
pcap-common.c
+ pcap-usb-linux-common.c
pcap.c
savefile.c
sf-pcapng.c
diff --git a/Makefile.in b/Makefile.in
index e7410333..5dc731c3 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -94,7 +94,7 @@ REMOTE_C_SRC = @REMOTE_C_SRC@
COMMON_C_SRC = pcap.c gencode.c optimize.c nametoaddr.c etherent.c \
fmtutils.c \
savefile.c sf-pcap.c sf-pcapng.c pcap-common.c \
- bpf_image.c bpf_filter.c bpf_dump.c
+ pcap-usb-linux-common.c bpf_image.c bpf_filter.c bpf_dump.c
GENERATED_C_SRC = scanner.c grammar.c
LIBOBJS = @LIBOBJS@
@@ -148,6 +148,7 @@ HDR = $(PUBHDR) \
pcap-int.h \
pcap-rpcap.h \
pcap-types.h \
+ pcap-usb-linux-common.h \
pflog.h \
portability.h \
ppp.h \
diff --git a/pcap-common.c b/pcap-common.c
index a576da5d..3af657a7 100644
--- a/pcap-common.c
+++ b/pcap-common.c
@@ -36,6 +36,8 @@
#include "pflog.h"
+#include "pcap-usb-linux-common.h"
+
#include "pcap-common.h"
/*
@@ -1792,3 +1794,19 @@ swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr, u_char *data)
break;
}
}
+
+void
+fixup_pcap_pkthdr(int linktype, struct pcap_pkthdr *hdr, u_char *data)
+{
+ if (linktype == DLT_USB_LINUX_MMAPPED) {
+ /*
+ * In older versions of libpcap, in memory-mapped captures,
+ * the "on-the-bus length" for isochronous transfers was
+ * miscalculated; it needed to be calculated based on the
+ * offsets and lengths in the descriptors, not on the raw
+ * URB length, but it wasn't. Recalculate it from the
+ * packet data.
+ */
+ set_linux_usb_mmapped_length(hdr, data);
+ }
+}
diff --git a/pcap-common.h b/pcap-common.h
index 8795a829..a779afe9 100644
--- a/pcap-common.h
+++ b/pcap-common.h
@@ -50,4 +50,7 @@ extern int linktype_to_dlt(int linktype);
extern void swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr,
u_char *data);
+extern void fixup_pcap_pkthdr(int linktype, struct pcap_pkthdr *hdr,
+ u_char *data);
+
extern u_int max_snaplen_for_dlt(int dlt);
diff --git a/pcap-usb-linux-common.c b/pcap-usb-linux-common.c
new file mode 100644
index 00000000..ef0bb462
--- /dev/null
+++ b/pcap-usb-linux-common.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * pcap-usb-linux-common.c - common code for everything that needs to
+ * deal with Linux USB captures.
+ */
+
+#include "pcap/pcap.h"
+#include "pcap/usb.h"
+
+#include "pcap-usb-linux-common.h"
+
+/*
+ * Compute, from the data provided by the Linux USB memory-mapped capture
+ * mechanism, the amount of packet data that would have been provided
+ * had the capture mechanism not chopped off any data at the end, if, in
+ * fact, it did so.
+ *
+ * Set the "unsliced length" field of the packet header to that value.
+ */
+void
+set_linux_usb_mmapped_length(struct pcap_pkthdr *pkth, const u_char *bp)
+{
+ const pcap_usb_header_mmapped *hdr;
+ u_int bytes_left;
+
+ bytes_left = pkth->caplen;
+ if (bytes_left < sizeof (pcap_usb_header_mmapped)) {
+ /*
+ * We don't have the full metadata header, so give up.
+ */
+ return;
+ }
+ bytes_left -= sizeof (pcap_usb_header_mmapped);
+
+ hdr = (const pcap_usb_header_mmapped *) bp;
+ if (hdr->data_flag) {
+ /*
+ * No data; just base the on-the-bus length on hdr->data_len
+ * (so that it's >= the captured length).
+ */
+ pkth->len = sizeof(pcap_usb_header_mmapped) + hdr->data_len;
+ } else {
+ /*
+ * We got data; calculate the on-the-bus length based on
+ * the data length prior to the USB monitor device discarding
+ * data due to its buffer being too small.
+ */
+ usb_isodesc *descs;
+ u_int pre_truncation_data_len;
+
+ descs = (usb_isodesc *) (bp + sizeof(pcap_usb_header_mmapped));
+
+ /*
+ * For most transfers, urb_len is that amount.
+ */
+ pre_truncation_data_len = hdr->urb_len;
+ if (hdr->transfer_type == URB_ISOCHRONOUS &&
+ hdr->event_type == URB_COMPLETE &&
+ (hdr->endpoint_number & URB_TRANSFER_IN)) {
+ /*
+ * For "this is complete" incoming isochronous
+ * transfer events, however, the data isn't
+ * contiguous, and the isochronous descriptos
+ * show how it's scattered.
+ *
+ * Find the end of the last chunk of data in
+ * the buffer referred to by the isochronous
+ * descriptors; that indicates how far into
+ * the buffer the data would have gone.
+ *
+ * Make sure we don't run past the end of the
+ * captured data while processing the isochronous
+ * descriptors.
+ */
+ pre_truncation_data_len = 0;
+ for (uint32_t desc = 0;
+ desc < hdr->ndesc && bytes_left >= sizeof (usb_isodesc);
+ desc++, bytes_left -= sizeof (usb_isodesc)) {
+ u_int desc_end;
+
+ desc_end = descs[desc].offset + descs[desc].len;
+ if (desc_end > pre_truncation_data_len)
+ pre_truncation_data_len = desc_end;
+ }
+ }
+ pkth->len = sizeof(pcap_usb_header_mmapped) +
+ (hdr->ndesc * sizeof (usb_isodesc)) +
+ pre_truncation_data_len;
+ }
+}
diff --git a/pcap-usb-linux-common.h b/pcap-usb-linux-common.h
new file mode 100644
index 00000000..05fca6a3
--- /dev/null
+++ b/pcap-usb-linux-common.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 1993, 1994, 1995, 1996, 1997
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * pcap-usb-linux-common.h - common code for everything that needs to
+ * deal with Linux USB captures.
+ */
+
+extern void set_linux_usb_mmapped_length(struct pcap_pkthdr *pkth,
+ const u_char *bp);
diff --git a/pcap-usb-linux.c b/pcap-usb-linux.c
index d62f17dd..ef495bd2 100644
--- a/pcap-usb-linux.c
+++ b/pcap-usb-linux.c
@@ -39,6 +39,7 @@
#include "pcap-int.h"
#include "pcap-usb-linux.h"
+#include "pcap-usb-linux-common.h"
#include "pcap/usb.h"
#include "extract.h"
@@ -756,6 +757,7 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch
struct mon_bin_mfetch fetch;
int32_t vec[VEC_SIZE];
struct pcap_pkthdr pkth;
+ u_char *bp;
pcap_usb_header_mmapped* hdr;
int nflush = 0;
int packets = 0;
@@ -839,8 +841,13 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch
* packets if we break out of the loop here.
*/
+ /* Get a pointer to this packet's buffer */
+ bp = &handlep->mmapbuf[vec[i]];
+
+ /* That begins with a metadata header */
+ hdr = (pcap_usb_header_mmapped*) bp;
+
/* discard filler */
- hdr = (pcap_usb_header_mmapped*) &handlep->mmapbuf[vec[i]];
if (hdr->event_type == '@')
continue;
@@ -868,24 +875,7 @@ usb_read_linux_mmap(pcap_t *handle, int max_packets, pcap_handler callback, u_ch
if (hdr->data_len < clen)
clen = hdr->data_len;
pkth.caplen = sizeof(pcap_usb_header_mmapped) + clen;
- if (hdr->data_flag) {
- /*
- * No data; just base the on-the-wire length
- * on hdr->data_len (so that it's >= the
- * captured length).
- */
- pkth.len = sizeof(pcap_usb_header_mmapped) +
- hdr->data_len;
- } else {
- /*
- * We got data; base the on-the-wire length
- * on hdr->urb_len, so that it includes
- * data discarded by the USB monitor device
- * due to its buffer being too small.
- */
- pkth.len = sizeof(pcap_usb_header_mmapped) +
- (hdr->ndesc * sizeof (usb_isodesc)) + hdr->urb_len;
- }
+ set_linux_usb_mmapped_length(&pkth, bp);
pkth.ts.tv_sec = (time_t)hdr->ts_sec;
pkth.ts.tv_usec = hdr->ts_usec;
diff --git a/sf-pcap.c b/sf-pcap.c
index f47f5db7..2245cc14 100644
--- a/sf-pcap.c
+++ b/sf-pcap.c
@@ -709,6 +709,8 @@ pcap_next_packet(pcap_t *p, struct pcap_pkthdr *hdr, u_char **data)
if (p->swapped)
swap_pseudo_headers(p->linktype, hdr, *data);
+ fixup_pcap_pkthdr(p->linktype, hdr, *data);
+
return (1);
}
diff --git a/sf-pcapng.c b/sf-pcapng.c
index 9e0a72e5..4791b288 100644
--- a/sf-pcapng.c
+++ b/sf-pcapng.c
@@ -1514,5 +1514,7 @@ found:
if (p->swapped)
swap_pseudo_headers(p->linktype, hdr, *data);
+ fixup_pcap_pkthdr(p->linktype, hdr, *data);
+
return (1);
}