diff options
author | Guy Harris <gharris@sonic.net> | 2022-06-15 17:10:03 -0700 |
---|---|---|
committer | Guy Harris <gharris@sonic.net> | 2022-06-15 17:10:03 -0700 |
commit | b4eeafb93955807464d8bb9327772fb217fcdd9a (patch) | |
tree | f67fc7c05296e8e33f24b31b1d8ce2362f43597b /pcap-usb-linux-common.c | |
parent | bc594f185299d9d4e3b39ba94e91a5b9ca8a938d (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.c | 110 |
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; } } |