Handle the new LINKTYPE_CAN_SOCKETCAN_HOSTENDIAN.
authorGuy Harris <guy@alum.mit.edu>
Fri, 19 Aug 2016 01:39:43 +0000 (18:39 -0700)
committerGuy Harris <guy@alum.mit.edu>
Fri, 19 Aug 2016 01:42:06 +0000 (01:42 +0000)
Unfortunately, only one libpcap code path puts the CAN ID in the
SocketCAN header in network byte order; the others leave it in host byte
order.  Therefore, a new LINKTYPE_/DLT_ value was introduced, and
libpcap was changed to use that for the cases where the CAN ID is in
host byte order.  Support them both.

This means we need to, when reading pcap and pcapng files, fix up the
CAN ID if the host that wrote the file has a different byte order from
ours (as libpcap also now does).  This includes Linux "cooked" captures,
which can include CAN packets.

Change-Id: I75ff2d68d1fbdb42753ce85d18f04166f21736dd
Reviewed-on: https://code.wireshark.org/review/17155
Reviewed-by: Guy Harris <guy@alum.mit.edu>
epan/dissectors/packet-socketcan.c
wiretap/pcap-common.c
wiretap/wtap.c
wiretap/wtap.h

index f392a1d93c7af169e61d10eef764561cb18e8a18..dc7312aefad049219a3030d9979782ab0eef624c 100644 (file)
@@ -96,8 +96,14 @@ static gpointer can_value(packet_info *pinfo _U_)
        return 0;
 }
 
+typedef enum {
+       SOCKETCAN_BIG_ENDIAN,
+       SOCKETCAN_HOST_ENDIAN
+} socketcan_endianness;
+
 static int
-dissect_socketcan(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
+dissect_socketcan_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    socketcan_endianness endianness)
 {
        proto_tree *can_tree;
        proto_item *ti;
@@ -110,7 +116,9 @@ dissect_socketcan(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
        col_clear(pinfo->cinfo,COL_INFO);
 
        frame_len  = tvb_get_guint8( tvb, CAN_LEN_OFFSET);
-       can_id.id  = tvb_get_ntohl(tvb, 0);
+       can_id.id  = (endianness == SOCKETCAN_BIG_ENDIAN) ?
+           tvb_get_ntohl(tvb, 0) :
+           tvb_get_h_guint32(tvb, 0);
 
        if (can_id.id & CAN_RTR_FLAG)
        {
@@ -157,6 +165,20 @@ dissect_socketcan(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* dat
        return tvb_captured_length(tvb);
 }
 
+static int
+dissect_socketcan_bigendian(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    void* data _U_)
+{
+       return dissect_socketcan_common(tvb, pinfo, tree, SOCKETCAN_BIG_ENDIAN);
+}
+
+static int
+dissect_socketcan_hostendian(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    void* data _U_)
+{
+       return dissect_socketcan_common(tvb, pinfo, tree, SOCKETCAN_HOST_ENDIAN);
+}
+
 void
 proto_register_socketcan(void)
 {
@@ -227,7 +249,10 @@ proto_register_socketcan(void)
                "CAN",                          /* short name */
                "can"                           /* abbrev     */
                );
-       register_dissector("can", dissect_socketcan, proto_can);
+       register_dissector("can-bigendian", dissect_socketcan_bigendian,
+           proto_can);
+       register_dissector("can-hostendian", dissect_socketcan_hostendian,
+           proto_can);
 
        proto_register_field_array(proto_can, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
@@ -245,11 +270,19 @@ proto_register_socketcan(void)
 void
 proto_reg_handoff_socketcan(void)
 {
-       dissector_handle_t can_handle;
-
-       can_handle = create_dissector_handle(dissect_socketcan, proto_can);
-       dissector_add_uint("wtap_encap", WTAP_ENCAP_SOCKETCAN, can_handle);
-       dissector_add_uint("sll.ltype", LINUX_SLL_P_CAN, can_handle);
+       dissector_handle_t socketcan_bigendian_handle;
+       dissector_handle_t socketcan_hostendian_handle;
+
+       socketcan_bigendian_handle = create_dissector_handle(dissect_socketcan_bigendian,
+           proto_can);
+       dissector_add_uint("wtap_encap", WTAP_ENCAP_SOCKETCAN_BIGENDIAN,
+           socketcan_bigendian_handle);
+       socketcan_hostendian_handle = create_dissector_handle(dissect_socketcan_hostendian,
+           proto_can);
+       dissector_add_uint("wtap_encap", WTAP_ENCAP_SOCKETCAN_HOSTENDIAN,
+           socketcan_hostendian_handle);
+       dissector_add_uint("sll.ltype", LINUX_SLL_P_CAN,
+           socketcan_hostendian_handle);
 }
 
 /*
index bab864de86fd3668c1e42fec49c273a2183f2148..57b2207dcea965a3a9983262a963862513260a4a 100644 (file)
@@ -378,8 +378,8 @@ static const struct {
        { 225,          WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS },
        /* Solaris IPNET */
        { 226,          WTAP_ENCAP_IPNET },
-       /* SocketCAN frame */
-       { 227,          WTAP_ENCAP_SOCKETCAN },
+       /* SocketCAN frame, with CAN ID in big-endian byte order */
+       { 227,          WTAP_ENCAP_SOCKETCAN_BIGENDIAN },
        /* Raw IPv4 */
        { 228,          WTAP_ENCAP_RAW_IP4 },
        /* Raw IPv6 */
@@ -437,6 +437,9 @@ static const struct {
        /* ISO14443 contactless smartcard standards */
        { 264,          WTAP_ENCAP_ISO14443 },
 
+       /* SocketCAN frame, with CAN ID in host-endian byte order */
+       { 265,          WTAP_ENCAP_SOCKETCAN_HOSTENDIAN },
+
        /*
         * To repeat:
         *
@@ -744,6 +747,12 @@ wtap_encap_requires_phdr(int wtap_encap)
  */
 #define NOKIA_LEN      4       /* length of the header */
 
+/*
+ * The fake link-layer header of Linux cooked packets.
+ */
+#define LINUX_SLL_PROTOCOL_OFFSET      14      /* protocol */
+#define LINUX_SLL_LEN                  16      /* length of the header */
+
 /*
  * The fake link-layer header of IrDA packets as introduced by Jean Tourrilhes
  * to libpcap.
@@ -1136,7 +1145,6 @@ struct usb_device_setup_hdr {
        guint16 wLength;
 };
 
-
 /*
  * Offset of the *end* of a field within a particular structure.
  */
@@ -1171,6 +1179,57 @@ struct usb_device_setup_hdr {
                PBSWAP64((guint8 *)fieldp); \
        }
 
+struct can_socketcan_hdr {
+       guint32 can_id;                 /* CAN ID and flags */
+       guint8 payload_length;          /* Frame payload length */
+       guint8 padding;
+       guint8 reserved1;
+       guint8 reserved2;
+};
+
+static void
+pcap_byteswap_linux_sll_pseudoheader(struct wtap_pkthdr *phdr, guint8 *pd)
+{
+       guint packet_size;
+       guint16 protocol;
+       struct can_socketcan_hdr *can_socketcan_phdr;
+
+       /*
+        * Minimum of captured and actual length (just in case the
+        * actual length < the captured length, which Should Never
+        * Happen).
+        */
+       packet_size = phdr->caplen;
+       if (packet_size > phdr->len)
+               packet_size = phdr->len;
+
+       if (packet_size < LINUX_SLL_LEN) {
+               /* Not enough data to have the protocol */
+               return;
+       }
+
+       protocol = pntoh16(&pd[LINUX_SLL_PROTOCOL_OFFSET]);
+       if (protocol != 0x000C) {
+               /* Not a CAN packet; nothing to fix */
+               return;
+       }
+
+       /*
+        * Greasy hack, but we never directly dereference any of
+        * the fields in *can_socketcan_phdr, we just get offsets
+        * of and addresses of its members and byte-swap it with a
+        * byte-at-a-time macro, so it's alignment-safe.
+        */
+       can_socketcan_phdr = (struct can_socketcan_hdr *)(void *)(pd + LINUX_SLL_LEN);
+
+       if (packet_size < LINUX_SLL_LEN + sizeof(can_socketcan_phdr->can_id)) {
+               /* Not enough data to have the full CAN ID */
+               return;
+       }
+
+       PBSWAP32((guint8 *)&can_socketcan_phdr->can_id);
+}
+
 static void
 pcap_byteswap_linux_usb_pseudoheader(struct wtap_pkthdr *phdr, guint8 *pd,
     gboolean header_len_64_bytes)
@@ -1328,6 +1387,37 @@ pcap_byteswap_nflog_pseudoheader(struct wtap_pkthdr *phdr, guint8 *pd)
        }
 }
 
+static void
+pcap_byteswap_can_socketcan_pseudoheader(struct wtap_pkthdr *phdr, guint8 *pd)
+{
+       guint packet_size;
+       struct can_socketcan_hdr *can_socketcan_phdr;
+
+       /*
+        * Minimum of captured and actual length (just in case the
+        * actual length < the captured length, which Should Never
+        * Happen).
+        */
+       packet_size = phdr->caplen;
+       if (packet_size > phdr->len)
+               packet_size = phdr->len;
+
+       /*
+        * Greasy hack, but we never directly dereference any of
+        * the fields in *can_socketcan_phdr, we just get offsets
+        * of and addresses of its members and byte-swap it with a
+        * byte-at-a-time macro, so it's alignment-safe.
+        */
+       can_socketcan_phdr = (struct can_socketcan_hdr *)(void *)pd;
+
+       if (packet_size < sizeof(can_socketcan_phdr->can_id)) {
+               /* Not enough data to have the full CAN ID */
+               return;
+       }
+
+       PBSWAP32((guint8 *)&can_socketcan_phdr->can_id);
+}
+
 /*
  * Pseudo-header at the beginning of DLT_BLUETOOTH_HCI_H4_WITH_PHDR frames.
  * Values in network byte order.
@@ -1871,6 +1961,11 @@ pcap_read_post_process(int file_type, int wtap_encap,
                phdr->pseudo_header.eth.fcs_len = fcs_len;
                break;
 
+       case WTAP_ENCAP_SLL:
+               if (bytes_swapped)
+                       pcap_byteswap_linux_sll_pseudoheader(phdr, pd);
+               break;
+
        case WTAP_ENCAP_USB_LINUX:
                if (bytes_swapped)
                        pcap_byteswap_linux_usb_pseudoheader(phdr, pd, FALSE);
@@ -1894,6 +1989,7 @@ pcap_read_post_process(int file_type, int wtap_encap,
                if (bytes_swapped)
                        pcap_byteswap_nflog_pseudoheader(phdr, pd);
                break;
+
        case WTAP_ENCAP_ERF:
                /*
                 * Update packet size to account for ERF padding and snapping.
@@ -1904,6 +2000,11 @@ pcap_read_post_process(int file_type, int wtap_encap,
                phdr->caplen = MIN(phdr->len, phdr->caplen);
                break;
 
+       case WTAP_ENCAP_SOCKETCAN_HOSTENDIAN:
+               if (bytes_swapped)
+                       pcap_byteswap_can_socketcan_pseudoheader(phdr, pd);
+               break;
+
        default:
                break;
        }
index 01dfee42d3bd8190884fac7b111a30b5bb47bf3c..213ab867a5a8d78aafa6c288b18aed6c5d505290 100644 (file)
@@ -751,8 +751,8 @@ static struct encap_type_info encap_table_base[] = {
        /* WTAP_ENCAP_IPNET */
        { "Solaris IPNET", "ipnet" },
 
-       /* WTAP_ENCAP_SOCKETCAN */
-       { "SocketCAN", "socketcan" },
+       /* WTAP_ENCAP_SOCKETCAN_BIGENDIAN */
+       { "SocketCAN with big-endian CAN ID", "socketcan-bigendian" },
 
        /* WTAP_ENCAP_IEEE_802_11_NETMON */
        { "IEEE 802.11 plus Network Monitor radio header", "ieee-802-11-netmon" },
@@ -921,6 +921,9 @@ static struct encap_type_info encap_table_base[] = {
 
        /* WTAP_ENCAP_JUNIPER_VN */
        { "Juniper VN", "juniper-vn" },
+
+       /* WTAP_ENCAP_SOCKETCAN_HOSTENDIAN */
+       { "SocketCAN with host-endian CAN ID", "socketcan-hostendian" },
 };
 
 WS_DLL_LOCAL
index 20a3b188f35be437b8d8d90a298a22f10485d32d..fc73ebe5c9b7e0ed723d8d6ddbf5f5c12d72c397 100644 (file)
@@ -212,7 +212,7 @@ extern "C" {
 #define WTAP_ENCAP_FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS 122
 #define WTAP_ENCAP_JPEG_JFIF                    123 /* obsoleted by WTAP_ENCAP_MIME*/
 #define WTAP_ENCAP_IPNET                        124
-#define WTAP_ENCAP_SOCKETCAN                    125
+#define WTAP_ENCAP_SOCKETCAN_BIGENDIAN          125
 #define WTAP_ENCAP_IEEE_802_11_NETMON           126
 #define WTAP_ENCAP_IEEE802_15_4_NOFCS           127
 #define WTAP_ENCAP_RAW_IPFIX                    128
@@ -269,6 +269,7 @@ extern "C" {
 #define WTAP_ENCAP_GFP_F                        179
 #define WTAP_ENCAP_IP_OVER_IB_PCAP              180
 #define WTAP_ENCAP_JUNIPER_VN                   181
+#define WTAP_ENCAP_SOCKETCAN_HOSTENDIAN         182
 /* After adding new item here, please also add new item to encap_table_base array */
 
 #define WTAP_NUM_ENCAP_TYPES                    wtap_get_num_encap_types()