| /* |
| * Copyright (c) 1993, 1994, 1995, 1996, 1997 |
| * The Regents of the University of California. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that: (1) source code distributions |
| * retain the above copyright notice and this paragraph in its entirety, (2) |
| * distributions including binary code include the above copyright notice and |
| * this paragraph in its entirety in the documentation or other materials |
| * provided with the distribution, and (3) all advertising materials mentioning |
| * features or use of this software display the following acknowledgement: |
| * ``This product includes software developed by the University of California, |
| * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of |
| * the University nor the names of its contributors may be used to endorse |
| * or promote products derived from this software without specific prior |
| * written permission. |
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
| * |
| * pcap-util.c - common code for various files |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #include <pcap-types.h> |
| |
| #include "pcap/can_socketcan.h" |
| #include "pcap/sll.h" |
| #include "pcap/usb.h" |
| #include "pcap/nflog.h" |
| |
| #include "pcap-int.h" |
| #include "extract.h" |
| #include "pcap-usb-linux-common.h" |
| |
| #include "pcap-util.h" |
| #include "pflog.h" |
| |
| /* |
| * Most versions of the DLT_PFLOG pseudo-header have UID and PID fields |
| * that are saved in host byte order. |
| * |
| * When reading a DLT_PFLOG packet, we need to convert those fields from |
| * the byte order of the host that wrote the file to this host's byte |
| * order. |
| */ |
| static void |
| swap_pflog_header(const struct pcap_pkthdr *hdr, u_char *buf) |
| { |
| u_int caplen = hdr->caplen; |
| u_int length = hdr->len; |
| u_int pfloghdr_length; |
| struct pfloghdr *pflhdr = (struct pfloghdr *)buf; |
| |
| if (caplen < (u_int) (offsetof(struct pfloghdr, uid) + sizeof pflhdr->uid) || |
| length < (u_int) (offsetof(struct pfloghdr, uid) + sizeof pflhdr->uid)) { |
| /* Not enough data to have the uid field */ |
| return; |
| } |
| |
| pfloghdr_length = pflhdr->length; |
| |
| if (pfloghdr_length < (u_int) (offsetof(struct pfloghdr, uid) + sizeof pflhdr->uid)) { |
| /* Header doesn't include uid field */ |
| return; |
| } |
| pflhdr->uid = SWAPLONG(pflhdr->uid); |
| |
| if (caplen < (u_int) (offsetof(struct pfloghdr, pid) + sizeof pflhdr->pid) || |
| length < (u_int) (offsetof(struct pfloghdr, pid) + sizeof pflhdr->pid)) { |
| /* Not enough data to have the pid field */ |
| return; |
| } |
| if (pfloghdr_length < (u_int) (offsetof(struct pfloghdr, pid) + sizeof pflhdr->pid)) { |
| /* Header doesn't include pid field */ |
| return; |
| } |
| pflhdr->pid = SWAPLONG(pflhdr->pid); |
| |
| if (caplen < (u_int) (offsetof(struct pfloghdr, rule_uid) + sizeof pflhdr->rule_uid) || |
| length < (u_int) (offsetof(struct pfloghdr, rule_uid) + sizeof pflhdr->rule_uid)) { |
| /* Not enough data to have the rule_uid field */ |
| return; |
| } |
| if (pfloghdr_length < (u_int) (offsetof(struct pfloghdr, rule_uid) + sizeof pflhdr->rule_uid)) { |
| /* Header doesn't include rule_uid field */ |
| return; |
| } |
| pflhdr->rule_uid = SWAPLONG(pflhdr->rule_uid); |
| |
| if (caplen < (u_int) (offsetof(struct pfloghdr, rule_pid) + sizeof pflhdr->rule_pid) || |
| length < (u_int) (offsetof(struct pfloghdr, rule_pid) + sizeof pflhdr->rule_pid)) { |
| /* Not enough data to have the rule_pid field */ |
| return; |
| } |
| if (pfloghdr_length < (u_int) (offsetof(struct pfloghdr, rule_pid) + sizeof pflhdr->rule_pid)) { |
| /* Header doesn't include rule_pid field */ |
| return; |
| } |
| pflhdr->rule_pid = SWAPLONG(pflhdr->rule_pid); |
| } |
| |
| /* |
| * Linux cooked capture packets with a protocol type of LINUX_SLL_P_CAN or |
| * LINUX_SLL_P_CANFD have SocketCAN CAN classic/CAN FD headers in front |
| * of the payload,with the CAN ID being in the byte order of the host |
| * that wrote the packet, and Linux cooked capture packets with a protocol |
| * type of LINUX_SLL_P_CANXL have SocketCAN CAN XL headers in front of the |
| * payload with the protocol/VCID field, the payload length, and the |
| * acceptance field in the byte order of the host that wrote the packet. |
| * |
| * When reading a Linux cooked capture packet, we need to check for those |
| * packets and, if the byte order host that wrote the packet, as |
| * indicated by the byte order of the pcap file or pcapng section |
| * containing the packet, is the opposite of our byte order, convert |
| * the header files to our byte order by byte-swapping them. |
| */ |
| static void |
| swap_socketcan_header(uint16_t protocol, u_int caplen, u_int length, |
| u_char *buf) |
| { |
| pcap_can_socketcan_hdr *hdrp; |
| pcap_can_socketcan_xl_hdr *xl_hdrp; |
| |
| switch (protocol) { |
| |
| case LINUX_SLL_P_CAN: |
| case LINUX_SLL_P_CANFD: |
| /* |
| * CAN classic/CAN FD packet; fix up the packet's header |
| * by byte-swapping the CAN ID field. |
| */ |
| hdrp = (pcap_can_socketcan_hdr *)buf; |
| if (caplen < (u_int) (offsetof(pcap_can_socketcan_hdr, can_id) + sizeof hdrp->can_id) || |
| length < (u_int) (offsetof(pcap_can_socketcan_hdr, can_id) + sizeof hdrp->can_id)) { |
| /* Not enough data to have the can_id field */ |
| return; |
| } |
| hdrp->can_id = SWAPLONG(hdrp->can_id); |
| break; |
| |
| case LINUX_SLL_P_CANXL: |
| /* |
| * CAN XL packet; fix up the packet's header by |
| * byte-swapping the priority/VCID field, the |
| * payload length, and the acceptance field. |
| */ |
| xl_hdrp = (pcap_can_socketcan_xl_hdr *)buf; |
| if (caplen < (u_int) (offsetof(pcap_can_socketcan_xl_hdr, priority_vcid) + sizeof xl_hdrp->priority_vcid) || |
| length < (u_int) (offsetof(pcap_can_socketcan_xl_hdr, priority_vcid) + sizeof xl_hdrp->priority_vcid)) { |
| /* Not enough data to have the priority_vcid field */ |
| return; |
| } |
| xl_hdrp->priority_vcid = SWAPLONG(xl_hdrp->priority_vcid); |
| if (caplen < (u_int) (offsetof(pcap_can_socketcan_xl_hdr, payload_length) + sizeof xl_hdrp->payload_length) || |
| length < (u_int) (offsetof(pcap_can_socketcan_xl_hdr, payload_length) + sizeof xl_hdrp->payload_length)) { |
| /* Not enough data to have the payload_length field */ |
| return; |
| } |
| xl_hdrp->payload_length = SWAPSHORT(xl_hdrp->payload_length); |
| if (caplen < (u_int) (offsetof(pcap_can_socketcan_xl_hdr, acceptance_field) + sizeof xl_hdrp->acceptance_field) || |
| length < (u_int) (offsetof(pcap_can_socketcan_xl_hdr, acceptance_field) + sizeof xl_hdrp->acceptance_field)) { |
| /* Not enough data to have the acceptance_field field */ |
| return; |
| } |
| xl_hdrp->acceptance_field = SWAPLONG(xl_hdrp->acceptance_field); |
| break; |
| |
| default: |
| /* |
| * Not a CAN packet; nothing to do. |
| */ |
| break; |
| } |
| } |
| |
| /* |
| * DLT_LINUX_SLL packets with a protocol type of LINUX_SLL_P_CAN or |
| * LINUX_SLL_P_CANFD have SocketCAN headers in front of the payload, |
| * with the CAN ID being in host byte order. |
| * |
| * When reading a DLT_LINUX_SLL packet, we need to check for those |
| * packets and convert the CAN ID from the byte order of the host that |
| * wrote the file to this host's byte order. |
| */ |
| static void |
| swap_linux_sll_socketcan_header(const struct pcap_pkthdr *hdr, u_char *buf) |
| { |
| u_int caplen = hdr->caplen; |
| u_int length = hdr->len; |
| struct sll_header *shdr = (struct sll_header *)buf; |
| |
| if (caplen < (u_int) sizeof(struct sll_header) || |
| length < (u_int) sizeof(struct sll_header)) { |
| /* Not enough data to have the protocol field */ |
| return; |
| } |
| |
| /* |
| * Byte-swap what needs to be byte-swapped. |
| */ |
| swap_socketcan_header(EXTRACT_BE_U_2(&shdr->sll_protocol), |
| caplen - (u_int) sizeof(struct sll_header), |
| length - (u_int) sizeof(struct sll_header), |
| buf + sizeof(struct sll_header)); |
| } |
| |
| /* |
| * The same applies for DLT_LINUX_SLL2. |
| */ |
| static void |
| swap_linux_sll2_socketcan_header(const struct pcap_pkthdr *hdr, u_char *buf) |
| { |
| u_int caplen = hdr->caplen; |
| u_int length = hdr->len; |
| struct sll2_header *shdr = (struct sll2_header *)buf; |
| |
| if (caplen < (u_int) sizeof(struct sll2_header) || |
| length < (u_int) sizeof(struct sll2_header)) { |
| /* Not enough data to have the protocol field */ |
| return; |
| } |
| |
| /* |
| * Byte-swap what needs to be byte-swapped. |
| */ |
| swap_socketcan_header(EXTRACT_BE_U_2(&shdr->sll2_protocol), |
| caplen - (u_int) sizeof(struct sll2_header), |
| length - (u_int) sizeof(struct sll2_header), |
| buf + sizeof(struct sll2_header)); |
| } |
| |
| /* |
| * The DLT_USB_LINUX and DLT_USB_LINUX_MMAPPED headers are in host |
| * byte order when capturing (it's supplied directly from a |
| * memory-mapped buffer shared by the kernel). |
| * |
| * When reading a DLT_USB_LINUX or DLT_USB_LINUX_MMAPPED packet, we |
| * need to convert it from the byte order of the host that wrote the |
| * file to this host's byte order. |
| */ |
| static void |
| swap_linux_usb_header(const struct pcap_pkthdr *hdr, u_char *buf, |
| int header_len_64_bytes) |
| { |
| pcap_usb_header_mmapped *uhdr = (pcap_usb_header_mmapped *)buf; |
| bpf_u_int32 offset = 0; |
| |
| /* |
| * "offset" is the offset *past* the field we're swapping; |
| * we skip the field *before* checking to make sure |
| * the captured data length includes the entire field. |
| */ |
| |
| /* |
| * The URB id is a totally opaque value; do we really need to |
| * convert it to the reading host's byte order??? |
| */ |
| offset += 8; /* skip past id */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->id = SWAPLL(uhdr->id); |
| |
| offset += 4; /* skip past various 1-byte fields */ |
| |
| offset += 2; /* skip past bus_id */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->bus_id = SWAPSHORT(uhdr->bus_id); |
| |
| offset += 2; /* skip past various 1-byte fields */ |
| |
| offset += 8; /* skip past ts_sec */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->ts_sec = SWAPLL(uhdr->ts_sec); |
| |
| offset += 4; /* skip past ts_usec */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->ts_usec = SWAPLONG(uhdr->ts_usec); |
| |
| offset += 4; /* skip past status */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->status = SWAPLONG(uhdr->status); |
| |
| offset += 4; /* skip past urb_len */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->urb_len = SWAPLONG(uhdr->urb_len); |
| |
| offset += 4; /* skip past data_len */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->data_len = SWAPLONG(uhdr->data_len); |
| |
| if (uhdr->transfer_type == URB_ISOCHRONOUS) { |
| offset += 4; /* skip past s.iso.error_count */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->s.iso.error_count = SWAPLONG(uhdr->s.iso.error_count); |
| |
| offset += 4; /* skip past s.iso.numdesc */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->s.iso.numdesc = SWAPLONG(uhdr->s.iso.numdesc); |
| } else |
| offset += 8; /* skip USB setup header */ |
| |
| /* |
| * With the old header, there are no isochronous descriptors |
| * after the header. |
| * |
| * With the new header, the actual number of descriptors in |
| * the header is not s.iso.numdesc, it's ndesc - only the |
| * first N descriptors, for some value of N, are put into |
| * the header, and ndesc is set to the actual number copied. |
| * In addition, if s.iso.numdesc is negative, no descriptors |
| * are captured, and ndesc is set to 0. |
| */ |
| if (header_len_64_bytes) { |
| /* |
| * This is either the "version 1" header, with |
| * 16 bytes of additional fields at the end, or |
| * a "version 0" header from a memory-mapped |
| * capture, with 16 bytes of zeroed-out padding |
| * at the end. Byte swap them as if this were |
| * a "version 1" header. |
| */ |
| offset += 4; /* skip past interval */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->interval = SWAPLONG(uhdr->interval); |
| |
| offset += 4; /* skip past start_frame */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->start_frame = SWAPLONG(uhdr->start_frame); |
| |
| offset += 4; /* skip past xfer_flags */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->xfer_flags = SWAPLONG(uhdr->xfer_flags); |
| |
| offset += 4; /* skip past ndesc */ |
| if (hdr->caplen < offset) |
| return; |
| uhdr->ndesc = SWAPLONG(uhdr->ndesc); |
| |
| if (uhdr->transfer_type == URB_ISOCHRONOUS) { |
| /* swap the values in struct linux_usb_isodesc */ |
| usb_isodesc *pisodesc; |
| uint32_t i; |
| |
| pisodesc = (usb_isodesc *)(void *)(buf+offset); |
| for (i = 0; i < uhdr->ndesc; i++) { |
| offset += 4; /* skip past status */ |
| if (hdr->caplen < offset) |
| return; |
| pisodesc->status = SWAPLONG(pisodesc->status); |
| |
| offset += 4; /* skip past offset */ |
| if (hdr->caplen < offset) |
| return; |
| pisodesc->offset = SWAPLONG(pisodesc->offset); |
| |
| offset += 4; /* skip past len */ |
| if (hdr->caplen < offset) |
| return; |
| pisodesc->len = SWAPLONG(pisodesc->len); |
| |
| offset += 4; /* skip past padding */ |
| |
| pisodesc++; |
| } |
| } |
| } |
| } |
| |
| /* |
| * The DLT_NFLOG "packets" have a mixture of big-endian and host-byte-order |
| * data. They begin with a fixed-length header with big-endian fields, |
| * followed by a set of TLVs, where the type and length are in host |
| * byte order but the values are either big-endian or are a raw byte |
| * sequence that's the same regardless of the host's byte order. |
| * |
| * When reading a DLT_NFLOG packet, we need to convert the type and |
| * length values from the byte order of the host that wrote the file |
| * to the byte order of this host. |
| */ |
| static void |
| swap_nflog_header(const struct pcap_pkthdr *hdr, u_char *buf) |
| { |
| u_char *p = buf; |
| nflog_hdr_t *nfhdr = (nflog_hdr_t *)buf; |
| nflog_tlv_t *tlv; |
| u_int caplen = hdr->caplen; |
| u_int length = hdr->len; |
| u_int size; |
| |
| if (caplen < (u_int) sizeof(nflog_hdr_t) || |
| length < (u_int) sizeof(nflog_hdr_t)) { |
| /* Not enough data to have any TLVs. */ |
| return; |
| } |
| |
| if (nfhdr->nflog_version != 0) { |
| /* Unknown NFLOG version */ |
| return; |
| } |
| |
| length -= sizeof(nflog_hdr_t); |
| caplen -= sizeof(nflog_hdr_t); |
| p += sizeof(nflog_hdr_t); |
| |
| while (caplen >= sizeof(nflog_tlv_t)) { |
| tlv = (nflog_tlv_t *) p; |
| |
| /* Swap the type and length. */ |
| tlv->tlv_type = SWAPSHORT(tlv->tlv_type); |
| tlv->tlv_length = SWAPSHORT(tlv->tlv_length); |
| |
| /* Get the length of the TLV. */ |
| size = tlv->tlv_length; |
| if (size % 4 != 0) |
| size += 4 - size % 4; |
| |
| /* Is the TLV's length less than the minimum? */ |
| if (size < sizeof(nflog_tlv_t)) { |
| /* Yes. Give up now. */ |
| return; |
| } |
| |
| /* Do we have enough data for the full TLV? */ |
| if (caplen < size || length < size) { |
| /* No. */ |
| return; |
| } |
| |
| /* Skip over the TLV. */ |
| length -= size; |
| caplen -= size; |
| p += size; |
| } |
| } |
| |
| static void |
| swap_pseudo_headers(int linktype, struct pcap_pkthdr *hdr, u_char *data) |
| { |
| /* |
| * Convert pseudo-headers from the byte order of |
| * the host on which the file was saved to our |
| * byte order, as necessary. |
| */ |
| switch (linktype) { |
| |
| case DLT_PFLOG: |
| swap_pflog_header(hdr, data); |
| break; |
| |
| case DLT_LINUX_SLL: |
| swap_linux_sll_socketcan_header(hdr, data); |
| break; |
| |
| case DLT_LINUX_SLL2: |
| swap_linux_sll2_socketcan_header(hdr, data); |
| break; |
| |
| case DLT_USB_LINUX: |
| swap_linux_usb_header(hdr, data, 0); |
| break; |
| |
| case DLT_USB_LINUX_MMAPPED: |
| swap_linux_usb_header(hdr, data, 1); |
| break; |
| |
| case DLT_NFLOG: |
| swap_nflog_header(hdr, data); |
| break; |
| } |
| } |
| |
| static inline int |
| packet_length_might_be_wrong(struct pcap_pkthdr *hdr, |
| const pcap_usb_header_mmapped *usb_hdr) |
| { |
| uint32_t old_style_packet_length; |
| |
| /* |
| * Calculate the packet length the old way. |
| * We know that the multiplication won't overflow, but |
| * we don't know that the additions won't. Calculate |
| * it with no overflow checks, as that's how it |
| * would have been calculated when it was captured. |
| */ |
| old_style_packet_length = iso_pseudo_header_len(usb_hdr) + |
| usb_hdr->urb_len; |
| return (hdr->len == old_style_packet_length); |
| } |
| |
| void |
| pcapint_post_process(int linktype, int swapped, struct pcap_pkthdr *hdr, |
| u_char *data) |
| { |
| if (swapped) |
| swap_pseudo_headers(linktype, hdr, data); |
| |
| /* |
| * Is this a memory-mapped Linux USB capture? |
| */ |
| if (linktype == DLT_USB_LINUX_MMAPPED) { |
| /* |
| * Yes. |
| * |
| * In older versions of libpcap, in memory-mapped Linux |
| * USB captures, the original length of 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), it's a completion event |
| * for an incoming isochronous transfer, and the |
| * transfer length appears to have been calculated |
| * from the raw URB length, fix it. |
| * |
| * We only do this if we have the full USB pseudo-header, |
| * because we will have to look at that header and at |
| * all of the isochronous descriptors. |
| */ |
| if (hdr->caplen < sizeof (pcap_usb_header_mmapped)) { |
| /* |
| * We don't have the full pseudo-header. |
| */ |
| return; |
| } |
| |
| const pcap_usb_header_mmapped *usb_hdr = |
| (const pcap_usb_header_mmapped *) data; |
| |
| /* |
| * Make sure the number of descriptors is sane. |
| * |
| * The Linux binary USB monitor code limits the number of |
| * isochronous descriptors to 128; if the number in the file |
| * is larger than that, either 1) the file's been damaged |
| * or 2) the file was produced after the number was raised |
| * in the kernel. |
| * |
| * In case 1), the number can't be trusted, so don't rely on |
| * it to attempt to fix the original length field in the pcap |
| * or pcapng header. |
| * |
| * In case 2), the system was probably running a version of |
| * libpcap that didn't miscalculate the original length, so |
| * it probably doesn't need to be fixed. |
| * |
| * This avoids the possibility of the product of the number of |
| * descriptors and the size of descriptors won't overflow an |
| * unsigned 32-bit integer. |
| */ |
| if (usb_hdr->ndesc > USB_MAXDESC) |
| return; |
| |
| if (!usb_hdr->data_flag && |
| is_isochronous_transfer_completion(usb_hdr) && |
| packet_length_might_be_wrong(hdr, usb_hdr)) { |
| u_int len; |
| |
| /* |
| * Make sure we have all of the descriptors, |
| * as we will have to look at all of them. |
| * |
| * If not, we don't bother trying to fix |
| * anything. |
| */ |
| if (hdr->caplen < iso_pseudo_header_len(usb_hdr)) |
| return; |
| |
| /* |
| * Calculate what the length should have been. |
| */ |
| len = incoming_isochronous_transfer_completed_len(hdr, |
| data); |
| |
| /* |
| * len is the smaller of UINT_MAX and the total |
| * header plus data length. That's guaranteed |
| * to fit in a UINT_MAX. |
| * |
| * Don't reduce the original length to a value |
| * below the captured length, however, as that |
| * is bogus. |
| */ |
| if (len >= hdr->caplen) |
| hdr->len = 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 |
| * original length is always >= the captured length. |
| */ |
| if (hdr->caplen > hdr->len) |
| hdr->len = hdr->caplen; |
| } |
| } |
| } |