* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* pcap-usb-linux-common.h - common code for everything that needs to
- * deal with Linux USB captures.
+ * deal with Linux USB captures, whether live or in a capture file;
+ * the later means that this is *not* Linux-only.
*/
-extern void fix_linux_usb_mmapped_length(struct pcap_pkthdr *pkth,
- const u_char *bp);
+#include <limits.h>
+
+/*
+ * Return the sum of the two u_int arguments if that sum fits in a u_int,
+ * and return UINT_MAX otherwise.
+ */
+static inline u_int
+u_int_sum(u_int a, u_int b)
+{
+ return (((b) <= UINT_MAX - (a)) ? (a) + (b) : UINT_MAX);
+}
+
+/*
+ * Is this a completion event for an isochronous transfer?
+ */
+static inline int
+is_isochronous_transfer_completion(const pcap_usb_header_mmapped *hdr)
+{
+ return (hdr->transfer_type == URB_ISOCHRONOUS &&
+ hdr->event_type == URB_COMPLETE &&
+ (hdr->endpoint_number & URB_TRANSFER_IN));
+}
+
+/*
+ * Total length of the pseudo-header, including the isochronous
+ * descriptors.
+ */
+static inline uint32_t
+iso_pseudo_header_len(const pcap_usb_header_mmapped *usb_hdr)
+{
+ return (sizeof(pcap_usb_header_mmapped) +
+ usb_hdr->ndesc * sizeof (usb_isodesc));
+}
+
+/*
+ * Calculate the packet length for a "this is complete" incoming
+ * isochronous transfer event.
+ *
+ * Calculating that from hdr->urb_len is not correct, because the
+ * data is not contiguous, and the isochroous descriptors show how
+ * it's scattered.
+ */
+static inline u_int
+incoming_isochronous_transfer_completed_len(struct pcap_pkthdr *phdr,
+ const u_char *bp)
+{
+ const pcap_usb_header_mmapped *hdr;
+ u_int bytes_left;
+ const usb_isodesc *descs;
+ u_int pre_truncation_data_len;
+
+ /*
+ * All callers of this routine must ensure that pkth->caplen is
+ * >= sizeof (pcap_usb_header_mmapped).
+ */
+ bytes_left = phdr->caplen;
+ bytes_left -= sizeof (pcap_usb_header_mmapped);
+
+ hdr = (const pcap_usb_header_mmapped *) bp;
+ descs = (const usb_isodesc *) (bp + sizeof(pcap_usb_header_mmapped));
+
+ /*
+ * 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;
+
+ if (descs[desc].len != 0) {
+ /*
+ * Compute the end offset of the data
+ * for this descriptor, i.e. the offset
+ * of the byte after the data. Clamp
+ * the sum at UINT_MAX, so that it fits
+ * in a u_int.
+ */
+ desc_end = u_int_sum(descs[desc].offset,
+ descs[desc].len);
+ if (desc_end > pre_truncation_data_len)
+ pre_truncation_data_len = desc_end;
+ }
+ }
+
+ /*
+ * Return the sum of the total header length (memory-mapped
+ * header and ISO descriptors) and the data length, clamped
+ * to UINT_MAX.
+ *
+ * We've made sure that the number of descriptors is
+ * <= USB_MAXDESC, so we know that the total size,
+ * in bytes, of the descriptors fits in a 32-bit
+ * integer.
+ */
+ return (u_int_sum(iso_pseudo_header_len(hdr), pre_truncation_data_len));
+}