]> The Tcpdump Group git mirrors - libpcap/commitdiff
Linux USB: fix incorrect values for the packet length.
authorGuy Harris <[email protected]>
Sat, 4 Jun 2022 01:00:22 +0000 (18:00 -0700)
committerGuy Harris <[email protected]>
Sat, 4 Jun 2022 01:00:41 +0000 (18:00 -0700)
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.

CMakeLists.txt
Makefile.in
pcap-common.c
pcap-common.h
pcap-usb-linux-common.c [new file with mode: 0644]
pcap-usb-linux-common.h [new file with mode: 0644]
pcap-usb-linux.c
sf-pcap.c
sf-pcapng.c

index 5d8fe5d9d049bb9ba808e4a7a5623b72bb497b0b..fd2d525ea8a1a2795f7aca1850c0480f2d62d9a2 100644 (file)
@@ -1063,6 +1063,7 @@ set(PROJECT_SOURCE_LIST_C
     optimize.c
     pcap-common.c
     pcap-options.c
+    pcap-usb-linux-common.c
     pcap.c
     savefile.c
     sf-pcapng.c
index 1b8ad07df66e49c5ff5ebd20e5298a2741de01c8..8488420681ec63b2db2717c8c0218836fb3a29d7 100644 (file)
@@ -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 pcap-options.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 \
index fcfe8bffa9884faabf1bc09989b994573400e555..408717e1ecb32ff6c6eeba8aeaab89a6879188bd 100644 (file)
@@ -36,6 +36,8 @@
 
 #include "pflog.h"
 
+#include "pcap-usb-linux-common.h"
+
 #include "pcap-common.h"
 
 /*
@@ -1818,3 +1820,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);
+       }
+}
index 8795a8297a64499411397638791aa35417bd7851..a779afe9bce1ec6df913c4a0f9d7888e0590ff7d 100644 (file)
@@ -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 (file)
index 0000000..ef0bb46
--- /dev/null
@@ -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 (file)
index 0000000..05fca6a
--- /dev/null
@@ -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);
index d62f17dd6a60f5e67f0e80deda9f816c2ca233e2..ef495bd2852a8e3a51413d39d10e0ed9b82721df 100644 (file)
@@ -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;
 
index 78437031a76710620cb2880445fad606dfb9158d..35ca265eb2ddbe6592bf3ab7185e39071dfde812 100644 (file)
--- a/sf-pcap.c
+++ b/sf-pcap.c
@@ -708,6 +708,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);
 }
 
index 9e0a72e5cdb74b4ac7d73c141cceeb8a6f581fdc..4791b288c0fd108b74091be2addbb945cc5173c3 100644 (file)
@@ -1514,5 +1514,7 @@ found:
        if (p->swapped)
                swap_pseudo_headers(p->linktype, hdr, *data);
 
+       fixup_pcap_pkthdr(p->linktype, hdr, *data);
+
        return (1);
 }