aboutsummaryrefslogtreecommitdiff
path: root/pcap-usb-linux-common.c
diff options
context:
space:
mode:
authorGuy Harris <gharris@sonic.net>2022-06-15 17:10:03 -0700
committerGuy Harris <gharris@sonic.net>2022-06-15 17:10:03 -0700
commitb4eeafb93955807464d8bb9327772fb217fcdd9a (patch)
treef67fc7c05296e8e33f24b31b1d8ce2362f43597b /pcap-usb-linux-common.c
parentbc594f185299d9d4e3b39ba94e91a5b9ca8a938d (diff)
Linux USB: make the capture file length fixup more conservative.
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.
Diffstat (limited to 'pcap-usb-linux-common.c')
-rw-r--r--pcap-usb-linux-common.c110
1 files changed, 66 insertions, 44 deletions
diff --git a/pcap-usb-linux-common.c b/pcap-usb-linux-common.c
index ef0bb462..2207f87e 100644
--- a/pcap-usb-linux-common.c
+++ b/pcap-usb-linux-common.c
@@ -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;
}
}