]> The Tcpdump Group git mirrors - libpcap/commitdiff
Linux USB: make the capture file length fixup more conservative.
authorGuy Harris <[email protected]>
Thu, 16 Jun 2022 00:10:03 +0000 (17:10 -0700)
committerGuy Harris <[email protected]>
Thu, 16 Jun 2022 00:10:03 +0000 (17:10 -0700)
When retroactively fixing up capture file on-the-network lengths for
memory-apped Linux USB captures, only fix up the on-the-network length
if it has the value that would be expected from the old capture code.

In the fixup code, make sure it's both >= the proper length, as best we
can calculate it, and the captured length.

pcap-common.c
pcap-common.h
pcap-usb-linux-common.c
pcap-usb-linux-common.h
pcap-usb-linux.c

index 408717e1ecb32ff6c6eeba8aeaab89a6879188bd..de8b5981526c475183abb707e1dfde9d13f98a6c 100644 (file)
@@ -1822,17 +1822,33 @@ swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr, u_char *data)
 }
 
 void
-fixup_pcap_pkthdr(int linktype, struct pcap_pkthdr *hdr, u_char *data)
+fixup_pcap_pkthdr(int linktype, struct pcap_pkthdr *hdr, const u_char *data)
 {
+       const pcap_usb_header_mmapped *usb_hdr;
+
+       usb_hdr = (const pcap_usb_header_mmapped *) 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.
+                * the "on-the-bus length" for completion events for
+                * incoming 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.
+                *
+                * If this packet contains transferred data (yes, data_flag
+                * is 0 if we *do* have data), and the total on-the-network
+                * length is equal to the value calculated from the raw URB
+                * length, then it might be one of those tranfers.
                 */
-               set_linux_usb_mmapped_length(hdr, data);
+               if (!usb_hdr->data_flag &&
+                   hdr->len == sizeof(pcap_usb_header_mmapped) +
+                     (usb_hdr->ndesc * sizeof (usb_isodesc)) + usb_hdr->urb_len) {
+                       /*
+                        * It might leed fixing; fix it if it's a completion
+                        * event for an incoming isochronous transfer.
+                        */
+                       fix_linux_usb_mmapped_length(hdr, data);
+               }
        }
 }
index a779afe9bce1ec6df913c4a0f9d7888e0590ff7d..cc944ef6a2e3794da2cbe7f817dd62ee5c0485b0 100644 (file)
@@ -51,6 +51,6 @@ 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);
+    const u_char *data);
 
 extern u_int max_snaplen_for_dlt(int dlt);
index ef0bb4621ae57987aa33afdaa2c5dbfe45305db5..2207f87e09d3b02b60305875d1502c323a8dc6f2 100644 (file)
@@ -36,7 +36,7 @@
  * 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)
+fix_linux_usb_mmapped_length(struct pcap_pkthdr *pkth, const u_char *bp)
 {
        const pcap_usb_header_mmapped *hdr;
        u_int bytes_left;
@@ -51,58 +51,80 @@ set_linux_usb_mmapped_length(struct pcap_pkthdr *pkth, const u_char *bp)
        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.
-                */
+       if (!hdr->data_flag && hdr->transfer_type == URB_ISOCHRONOUS &&
+           hdr->event_type == URB_COMPLETE &&
+           (hdr->endpoint_number & URB_TRANSFER_IN) &&
+           pkth->len == sizeof(pcap_usb_header_mmapped) +
+                        (hdr->ndesc * sizeof (usb_isodesc)) + hdr->urb_len) {
                usb_isodesc *descs;
-               u_int pre_truncation_data_len;
+               u_int pre_truncation_data_len, pre_truncation_len;
 
                descs = (usb_isodesc *) (bp + sizeof(pcap_usb_header_mmapped));
 
                /*
-                * For most transfers, urb_len is that amount.
+                * We have data (yes, data_flag is 0 if we *do* have data),
+                * and this is a "this is complete" incoming isochronous
+                * transfer event, and the length was calculated based
+                * on the URB length.
+                *
+                * That's not correct, because 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 = 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;
+               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;
-                       }
+                       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) +
+
+               /*
+                * Now calculate the total length based on that data
+                * length.
+                */
+               pre_truncation_len = sizeof(pcap_usb_header_mmapped) +
                    (hdr->ndesc * sizeof (usb_isodesc)) +
                    pre_truncation_data_len;
+
+               /*
+                * If that's greater than or equal to the captured length,
+                * use that as the length.
+                */
+               if (pre_truncation_len >= pkth->caplen)
+                       pkth->len = pre_truncation_len;
+
+               /*
+                * If the captured length is greater than the length,
+                * use the captured length.
+                *
+                * For completion events for incoming isochronous transfers,
+                * it's based on data_len, which is calculated the same way
+                * we calculated pre_truncation_data_len above, except that
+                * it has access to all the isochronous descriptors, not
+                * just the ones that the kernel were able to provide us or,
+                * for a capture file, that weren't sliced off by a snapshot
+                * length.
+                *
+                * However, it might have been reduced by the USB capture
+                * mechanism arbitrarily limiting the amount of data it
+                * provides to userland, or by the libpcap capture code
+                * limiting it to being no more than the snapshot, so
+                * we don't want to just use it all the time; we only
+                * do so to try to get a better estimate of the actual
+                * length - and to make sure the on-the-network length
+                * is always >= the captured length.
+                */
+               if (pkth->caplen > pkth->len)
+                       pkth->len = pkth->caplen;
        }
 }
index 05fca6a3dbae7c921783036791426460d6923181..8cff7ba1cf6bc1c15e3ec20ed5204b03b1bd018a 100644 (file)
@@ -22,5 +22,5 @@
  * deal with Linux USB captures.
  */
 
-extern void set_linux_usb_mmapped_length(struct pcap_pkthdr *pkth,
+extern void fix_linux_usb_mmapped_length(struct pcap_pkthdr *pkth,
     const u_char *bp);
index ef495bd2852a8e3a51413d39d10e0ed9b82721df..7020577ca6cb459206f828131d755357f90a9ea3 100644 (file)
@@ -875,7 +875,31 @@ 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;
-                       set_linux_usb_mmapped_length(&pkth, bp);
+                       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;
+
+                               /*
+                                * Now clean it up if it's a completion
+                                * event for an incoming isochronous
+                                * transfer.
+                                */
+                               fix_linux_usb_mmapped_length(&pkth, bp);
+                       }
                        pkth.ts.tv_sec = (time_t)hdr->ts_sec;
                        pkth.ts.tv_usec = hdr->ts_usec;