}
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);
+ }
}
}
* 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;
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;
}
}