Handle the new LINKTYPE_CAN_SOCKETCAN_HOSTENDIAN.

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>
This commit is contained in:
Guy Harris 2016-08-18 18:39:43 -07:00
parent 2a4d6f1b3c
commit 95c4c432c4
4 changed files with 151 additions and 13 deletions

View 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;
dissector_handle_t socketcan_bigendian_handle;
dissector_handle_t socketcan_hostendian_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);
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);
}
/*

View 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;
}

View 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

View 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()