aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors
diff options
context:
space:
mode:
authorGuy Harris <gharris@sonic.net>2021-10-14 19:15:07 -0700
committerGuy Harris <gharris@sonic.net>2021-10-14 19:15:07 -0700
commit39604740898f01fbed0777d3f9b8948bf23ec34a (patch)
tree21064347c0c3c8f80e5ac1ed748a20de20030692 /epan/dissectors
parent2d569e116ef9a634ad8eee939dd53e16af3c2a0f (diff)
socketcan: support the CANFD_FDF flag for identifying CAN FD frames.
The Linux SocketCAN header now uses the formerly-reserved byte in the SocketCAN header after the "payload length" field as an "FD flags" field, with a flag bit reserved to indicate whether the frame is a classic CAN frame or a CAN FD frame, with two other bits giving frame information for FD frames. For LINKTYPE_CAN_SOCKETCAN, use that flag bit to determine whether the frame is classic CAN or CAN FD. As some older LINKTYPE_CAN_SOCKETCAN captures have SocketCAN headers in which the fields after the "payload length" field were uninitialized, so trust that thge "FD flags" was filled in, rather than possibly randomly uninitialized, only if the only bits set in that field are the bits defined to be in that field and the two reserved bytes after it are zero. This will be needed when the current main-branch libpcap is released, as it uses LINKTYPE_CAN_SOCKETCAN rather than LINKTYPE_LINUX_SLL for ARPHRD_CAN devices; we add it now to future-proof the Wireshark releases to which this is being committed. It also handles what existing CAN FD captures using LINKTYPE_CAN_SOCKETCAN exist. For LINKTYPE_LINUX_SLL frames, we have the protocol field to distinguish between classic CAN and CAN FD, so we use that to determine the frame type, rather than looking at the CANFD_FDF flag. dissect_socketcan_common() now handles both classic CAN and CAN FD frames.
Diffstat (limited to 'epan/dissectors')
-rw-r--r--epan/dissectors/packet-socketcan.c186
-rw-r--r--epan/dissectors/packet-socketcan.h4
2 files changed, 105 insertions, 85 deletions
diff --git a/epan/dissectors/packet-socketcan.c b/epan/dissectors/packet-socketcan.c
index 739cfb0f6e..82abeab193 100644
--- a/epan/dissectors/packet-socketcan.c
+++ b/epan/dissectors/packet-socketcan.c
@@ -106,8 +106,7 @@ static heur_dtbl_entry_t *heur_dtbl_entry;
static dissector_table_t can_id_dissector_table = NULL;
static dissector_table_t can_extended_id_dissector_table = NULL;
static dissector_table_t subdissector_table = NULL;
-static dissector_handle_t socketcan_bigendian_handle;
-static dissector_handle_t socketcan_hostendian_handle;
+static dissector_handle_t socketcan_classic_handle;
static dissector_handle_t socketcan_fd_handle;
static const value_string can_err_prot_error_location_vals[] =
@@ -359,8 +358,29 @@ socketcan_call_subdissectors(tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree
return TRUE;
}
+/*
+ * Either:
+ *
+ * 1) a given SocketCAN frame is known to contain a classic CAN
+ * packet based on information outside the SocketCAN header;
+ *
+ * 2) a given SocketCAN frame is known to contain a CAN FD
+ * packet based on information outside the SocketCAN header;
+ *
+ * 3) we don't know whether the given SocketCAN frame is a
+ * classic CAN packet or a CAN FD packet, and will have
+ * to check the CANFD_FDF bit in the "FD flags" field of
+ * the SocketCAN headder to determine that.
+ */
+typedef enum {
+ PACKET_TYPE_CAN,
+ PACKET_TYPE_CAN_FD,
+ PACKET_TYPE_UNKNOWN
+} can_packet_type_t;
+
static int
-dissect_socketcan_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, guint encoding)
+dissect_socketcan_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+ guint encoding, can_packet_type_t can_packet_type)
{
proto_tree *can_tree;
proto_item *ti;
@@ -382,6 +402,21 @@ dissect_socketcan_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
&hf_can_errflag,
NULL,
};
+ static int * const can_std_flags_fd[] = {
+ &hf_can_infoent_std,
+ &hf_can_extflag,
+ NULL,
+ };
+ static int * const can_ext_flags_fd[] = {
+ &hf_can_infoent_ext,
+ &hf_can_extflag,
+ NULL,
+ };
+ static int * const canfd_flag_fields[] = {
+ &hf_canfd_brsflag,
+ &hf_canfd_esiflag,
+ NULL,
+ };
static int * const can_err_flags[] = {
&hf_can_errflag,
&hf_can_err_tx_timeout,
@@ -399,11 +434,44 @@ dissect_socketcan_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
can_info.id = tvb_get_guint32(tvb, 0, encoding);
can_info.len = tvb_get_guint8(tvb, CAN_LEN_OFFSET);
- can_info.fd = FALSE;
+ /*
+ * If we weren't told the type of this frame, check
+ * whether the CANFD_FDF flag is set in the FD flags
+ * field of the header; if so, it's a CAN FD frame.
+ * otherwise, it's a CAN frame.
+ *
+ * However, trust the CANFD_FDF flag only if the only
+ * bits set in the FD flags field are the known bits,
+ * and the two bytes following that field are both
+ * zero. This is because some older LINKTYPE_CAN_SOCKETCAN
+ * frames had uninitialized junk in the FD flags field,
+ * so we treat a frame with what appears to be uninitialized
+ * junk as being CAN rather than CAN FD, under the assumption
+ * that the CANFD_FDF bit is set because the field is
+ * uninitialized, not because it was explicitly set because
+ * it's a CAN FD frame. At least some newer code that sets
+ * that flag also makes sure that the fields in question are
+ * initialized, so we assume that if they're not initialized
+ * the code is older code that didn't support CAN FD.
+ */
+ if (can_packet_type == PACKET_TYPE_UNKNOWN) {
+ guint8 fd_flags;
+
+ fd_flags = tvb_get_guint8(tvb, CANFD_FLAG_OFFSET);
+
+ if ((fd_flags & CANFD_FDF) &&
+ ((fd_flags & ~(CANFD_BRS|CANFD_ESI|CANFD_FDF)) == 0) &&
+ tvb_get_guint8(tvb, CANFD_FLAG_OFFSET + 1) == 0 &&
+ tvb_get_guint8(tvb, CANFD_FLAG_OFFSET + 2) == 0)
+ can_packet_type = PACKET_TYPE_CAN_FD;
+ else
+ can_packet_type = PACKET_TYPE_CAN;
+ }
+ can_info.fd = (can_packet_type == PACKET_TYPE_CAN_FD);
can_info.bus_id = get_bus_id(pinfo);
/* Error Message Frames are only encapsulated in Classic CAN frames */
- if (can_info.id & CAN_ERR_FLAG)
+ if (can_packet_type == PACKET_TYPE_CAN && (can_info.id & CAN_ERR_FLAG))
{
frame_type = LINUX_CAN_ERR;
can_flags = can_err_flags;
@@ -412,23 +480,23 @@ dissect_socketcan_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
{
frame_type = LINUX_CAN_EXT;
can_info.id &= (CAN_EFF_MASK | CAN_FLAG_MASK);
- can_flags = can_ext_flags;
+ can_flags = (can_packet_type == PACKET_TYPE_CAN_FD) ? can_ext_flags_fd : can_ext_flags;
}
else
{
frame_type = LINUX_CAN_STD;
can_info.id &= (CAN_SFF_MASK | CAN_FLAG_MASK);
- can_flags = can_std_flags;
+ can_flags = (can_packet_type == PACKET_TYPE_CAN_FD) ? can_std_flags_fd : can_std_flags;
}
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "CAN");
+ col_set_str(pinfo->cinfo, COL_PROTOCOL, (can_packet_type == PACKET_TYPE_CAN_FD) ? "CANFD" : "CAN");
col_clear(pinfo->cinfo, COL_INFO);
guint32 effective_can_id = (can_info.id & CAN_EFF_FLAG) ? can_info.id & CAN_EFF_MASK : can_info.id & CAN_SFF_MASK;
char* id_name = (can_info.id & CAN_EFF_FLAG) ? "Ext. ID" : "ID";
col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %d (0x%" G_GINT32_MODIFIER "x), Length: %d", id_name, effective_can_id, effective_can_id, can_info.len);
- ti = proto_tree_add_item(tree, proto_can, tvb, 0, -1, ENC_NA);
+ ti = proto_tree_add_item(tree, (can_packet_type == PACKET_TYPE_CAN_FD) ? proto_canfd : proto_can, tvb, 0, -1, ENC_NA);
can_tree = proto_item_add_subtree(ti, ett_can);
proto_item_append_text(can_tree, ", %s: %d (0x%" G_GINT32_MODIFIER "x), Length: %d", id_name, effective_can_id, effective_can_id, can_info.len);
@@ -439,7 +507,11 @@ dissect_socketcan_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gu
{
proto_tree_add_expert(tree, pinfo, &ei_can_err_dlc_mismatch, tvb, CAN_LEN_OFFSET, 1);
}
- proto_tree_add_item(can_tree, hf_can_reserved, tvb, CAN_LEN_OFFSET+1, 3, ENC_NA);
+ if (can_packet_type == PACKET_TYPE_CAN_FD) {
+ proto_tree_add_bitmask_list(can_tree, tvb, CANFD_FLAG_OFFSET, 1, canfd_flag_fields, ENC_NA);
+ proto_tree_add_item(can_tree, hf_can_reserved, tvb, CANFD_FLAG_OFFSET+1, 2, ENC_NA);
+ } else
+ proto_tree_add_item(can_tree, hf_can_reserved, tvb, CANFD_FLAG_OFFSET, 3, ENC_NA);
if (frame_type == LINUX_CAN_ERR)
{
@@ -533,90 +605,23 @@ dissect_socketcan_bigendian(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void* data _U_)
{
return dissect_socketcan_common(tvb, pinfo, tree,
- byte_swap ? ENC_LITTLE_ENDIAN : ENC_BIG_ENDIAN);
+ byte_swap ? ENC_LITTLE_ENDIAN : ENC_BIG_ENDIAN, PACKET_TYPE_UNKNOWN);
}
static int
-dissect_socketcan_hostendian(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+dissect_socketcan_classic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void* data _U_)
{
return dissect_socketcan_common(tvb, pinfo, tree,
- byte_swap ? ENC_ANTI_HOST_ENDIAN : ENC_HOST_ENDIAN);
-}
-
-static int
-dissect_socketcanfd_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
- guint encoding)
-{
- proto_tree *can_tree;
- proto_item *ti;
- struct can_info can_info;
- tvbuff_t* next_tvb;
- int * can_flags_fd[] = {
- &hf_can_infoent_ext,
- &hf_can_extflag,
- NULL,
- };
- static int * const canfd_flag_fields[] = {
- &hf_canfd_brsflag,
- &hf_canfd_esiflag,
- NULL,
- };
-
- can_info.id = tvb_get_guint32(tvb, 0, encoding);
- can_info.len = tvb_get_guint8(tvb, CAN_LEN_OFFSET);
- can_info.fd = TRUE;
- can_info.bus_id = get_bus_id(pinfo);
-
- if (can_info.id & CAN_EFF_FLAG)
- {
- can_info.id &= (CAN_EFF_MASK | CAN_FLAG_MASK);
- }
- else
- {
- can_info.id &= (CAN_SFF_MASK | CAN_FLAG_MASK);
- can_flags_fd[0] = &hf_can_infoent_std;
- }
-
- col_set_str(pinfo->cinfo, COL_PROTOCOL, "CANFD");
- col_clear(pinfo->cinfo, COL_INFO);
-
- guint32 effective_can_id = (can_info.id & CAN_EFF_FLAG) ? can_info.id & CAN_EFF_MASK : can_info.id & CAN_SFF_MASK;
- char* id_name = (can_info.id & CAN_EFF_FLAG) ? "Ext. ID" : "ID";
- col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %d (0x%" G_GINT32_MODIFIER "x), Length: %d", id_name, effective_can_id, effective_can_id, can_info.len);
-
- ti = proto_tree_add_item(tree, proto_canfd, tvb, 0, -1, ENC_NA);
- can_tree = proto_item_add_subtree(ti, ett_can_fd);
-
- proto_item_append_text(can_tree, ", %s: %d (0x%" G_GINT32_MODIFIER "x), Length: %d", id_name, effective_can_id, effective_can_id, can_info.len);
-
- proto_tree_add_bitmask_list(can_tree, tvb, 0, 4, can_flags_fd, encoding);
-
- proto_tree_add_item(can_tree, hf_can_len, tvb, CAN_LEN_OFFSET, 1, ENC_NA);
- proto_tree_add_bitmask_list(can_tree, tvb, CANFD_FLAG_OFFSET, 1, canfd_flag_fields, ENC_NA);
- proto_tree_add_item(can_tree, hf_can_reserved, tvb, CANFD_FLAG_OFFSET+1, 2, ENC_NA);
-
- next_tvb = tvb_new_subset_length(tvb, CAN_DATA_OFFSET, can_info.len);
-
- if (!socketcan_call_subdissectors(next_tvb, pinfo, tree, &can_info, heuristic_first))
- {
- call_data_dissector(next_tvb, pinfo, tree);
- }
-
- if (tvb_captured_length_remaining(tvb, CAN_DATA_OFFSET+can_info.len) > 0)
- {
- proto_tree_add_item(can_tree, hf_can_padding, tvb, CAN_DATA_OFFSET+can_info.len, -1, ENC_NA);
- }
-
- return tvb_captured_length(tvb);
+ byte_swap ? ENC_ANTI_HOST_ENDIAN : ENC_HOST_ENDIAN, PACKET_TYPE_CAN);
}
static int
dissect_socketcan_fd(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
void* data _U_)
{
- return dissect_socketcanfd_common(tvb, pinfo, tree,
- byte_swap ? ENC_ANTI_HOST_ENDIAN : ENC_HOST_ENDIAN);
+ return dissect_socketcan_common(tvb, pinfo, tree,
+ byte_swap ? ENC_ANTI_HOST_ENDIAN : ENC_HOST_ENDIAN, PACKET_TYPE_CAN_FD);
}
void
@@ -1006,8 +1011,15 @@ proto_register_socketcan(void)
module_t *can_module;
proto_can = proto_register_protocol("Controller Area Network", "CAN", "can");
- socketcan_bigendian_handle = register_dissector("can-bigendian", dissect_socketcan_bigendian, proto_can);
- socketcan_hostendian_handle = register_dissector("can-hostendian", dissect_socketcan_hostendian, proto_can);
+ /*
+ * "can-hostendian" is a legacy name (there never was, in any libpcap
+ * release, a SocketCAN LINKTYPE_ value for a host-endian CAN ID
+ * and flags field); we need to keep it around in case some candump
+ * or Busmaster capture that was saved as a pcap or pcapng file,
+ * as those use a linktype of LINKTYPE_WIRESHARK_UPPER_PDU with
+ * "can-hostendian" as the dissector name.
+ */
+ socketcan_classic_handle = register_dissector("can-hostendian", dissect_socketcan_classic, proto_can);
proto_canfd = proto_register_protocol("Controller Area Network FD", "CANFD", "canfd");
socketcan_fd_handle = register_dissector("canfd", dissect_socketcan_fd, proto_canfd);
@@ -1069,8 +1081,12 @@ proto_register_socketcan(void)
void
proto_reg_handoff_socketcan(void)
{
+ dissector_handle_t socketcan_bigendian_handle;
+
+ socketcan_bigendian_handle = create_dissector_handle(dissect_socketcan_bigendian, proto_can);
dissector_add_uint("wtap_encap", WTAP_ENCAP_SOCKETCAN, socketcan_bigendian_handle);
- dissector_add_uint("sll.ltype", LINUX_SLL_P_CAN, socketcan_hostendian_handle);
+
+ dissector_add_uint("sll.ltype", LINUX_SLL_P_CAN, socketcan_classic_handle);
dissector_add_uint("sll.ltype", LINUX_SLL_P_CANFD, socketcan_fd_handle);
}
diff --git a/epan/dissectors/packet-socketcan.h b/epan/dissectors/packet-socketcan.h
index 6315fdb9f3..a18395c329 100644
--- a/epan/dissectors/packet-socketcan.h
+++ b/epan/dissectors/packet-socketcan.h
@@ -14,6 +14,10 @@
#include <epan/packet_info.h>
#include <epan/proto.h>
+/* Flags for CAN FD frames. */
+#define CANFD_BRS 0x01 /* Bit Rate Switch (second bitrate for payload data) */
+#define CANFD_ESI 0x02 /* Error State Indicator of the transmitting node */
+#define CANFD_FDF 0x04 /* FD flag - if set, this is an FD frame */
/* Structure that gets passed between dissectors. */
struct can_info