diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /plugins/epan/ethercat/packet-esl.c | |
parent | Initial commit. (diff) | |
download | wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip |
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/epan/ethercat/packet-esl.c')
-rw-r--r-- | plugins/epan/ethercat/packet-esl.c | 372 |
1 files changed, 372 insertions, 0 deletions
diff --git a/plugins/epan/ethercat/packet-esl.c b/plugins/epan/ethercat/packet-esl.c new file mode 100644 index 00000000..75f269b4 --- /dev/null +++ b/plugins/epan/ethercat/packet-esl.c @@ -0,0 +1,372 @@ +/* packet-esl.c + * Routines for EtherCAT Switch Link disassembly + * + * Copyright (c) 2007 by Beckhoff Automation GmbH + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1999 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <epan/packet.h> +#include <epan/prefs.h> + +void proto_register_esl(void); + +#if 0 +/* XXX: using bitfields is compiler dependent: See README.developer */ + +typedef union _EslFlagsUnion +{ + struct + { + guint16 port7 : 1; + guint16 port6 : 1; + guint16 port5 : 1; + guint16 port4 : 1; + guint16 port3 : 1; + guint16 port2 : 1; + guint16 port1 : 1; + guint16 port0 : 1; + guint16 extended : 1; + guint16 port11 : 1; + guint16 port10 : 1; + guint16 alignError : 1; + guint16 crcError : 1; + guint16 timeStampEna : 1; + guint16 port9 : 1; + guint16 port8 : 1; + }d; + struct + { + guint8 loPorts : 1; + guint8 flagsHiPorts : 1; + }lo_hi_flags; + guint flags; +} EslFlagsUnion; +#endif + +#define esl_port7_bitmask 0x0001 +#define esl_port6_bitmask 0x0002 +#define esl_port5_bitmask 0x0004 +#define esl_port4_bitmask 0x0008 +#define esl_port3_bitmask 0x0010 +#define esl_port2_bitmask 0x0020 +#define esl_port1_bitmask 0x0040 +#define esl_port0_bitmask 0x0080 +#define esl_extended_bitmask 0x0100 +#define esl_port11_bitmask 0x0200 +#define esl_port10_bitmask 0x0400 +#define esl_alignError_bitmask 0x0800 +#define esl_crcError_bitmask 0x1000 +#define esl_timeStampEna_bitmask 0x2000 +#define esl_port9_bitmask 0x4000 +#define esl_port8_bitmask 0x8000 + +#if 0 +typedef struct _EslHeader +{ + guint8 eslCookie[6]; /* 01 01 05 10 00 00 */ + EslFlagsUnion flags; + guint64 timeStamp; +} EslHeader, *PEslHeader; +#endif + + +#define SIZEOF_ESLHEADER 16 + +static dissector_handle_t eth_withoutfcs_handle; + +void proto_reg_handoff_esl(void); + +/* Define the esl proto */ +int proto_esl = -1; + +static int ett_esl = -1; + +static int hf_esl_timestamp = -1; +static int hf_esl_port = -1; +static int hf_esl_crcerror = -1; +static int hf_esl_alignerror = -1; + +/* Note: using external tfs strings apparently doesn't work in a plugin */ +static const true_false_string flags_yes_no = { + "yes", + "no" +}; + +#if 0 +/* XXX: using bitfields is compiler dependent: See README.developer */ +static guint16 flags_to_port(guint16 flagsValue) { + EslFlagsUnion flagsUnion; + flagsUnion.flags = flagsValue; + if ( flagsUnion.d.port0 ) + return 0; + else if ( flagsUnion.d.port1 ) + return 1; + else if ( flagsUnion.d.port2 ) + return 2; + else if ( flagsUnion.d.port3 ) + return 3; + else if ( flagsUnion.d.port4 ) + return 4; + else if ( flagsUnion.d.port5 ) + return 5; + else if ( flagsUnion.d.port6 ) + return 6; + else if ( flagsUnion.d.port7 ) + return 7; + else if ( flagsUnion.d.port8 ) + return 8; + else if ( flagsUnion.d.port9 ) + return 9; + + return -1; +} +#endif + +static guint16 flags_to_port(guint16 flagsValue) { + if ( (flagsValue & esl_port0_bitmask) != 0 ) + return 0; + else if ( (flagsValue & esl_port1_bitmask) != 0 ) + return 1; + else if ( (flagsValue & esl_port2_bitmask) != 0 ) + return 2; + else if ( (flagsValue & esl_port3_bitmask) != 0 ) + return 3; + else if ( (flagsValue & esl_port4_bitmask) != 0 ) + return 4; + else if ( (flagsValue & esl_port5_bitmask) != 0 ) + return 5; + else if ( (flagsValue & esl_port6_bitmask) != 0 ) + return 6; + else if ( (flagsValue & esl_port7_bitmask) != 0 ) + return 7; + else if ( (flagsValue & esl_port8_bitmask) != 0 ) + return 8; + else if ( (flagsValue & esl_port9_bitmask) != 0 ) + return 9; + else if ( (flagsValue & esl_port10_bitmask) != 0 ) + return 10; + else if ( (flagsValue & esl_port11_bitmask) != 0 ) + return 11; + + return -1; +} + +/*esl*/ +static int +dissect_esl_header(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree, void* data _U_) { + + proto_item *ti = NULL; + proto_tree *esl_header_tree; + gint offset = 0; + + guint esl_length = tvb_reported_length(tvb); + if ( esl_length >= SIZEOF_ESLHEADER ) + { + if (tree) + { + guint16 flags; + + ti = proto_tree_add_item(tree, proto_esl, tvb, 0, SIZEOF_ESLHEADER, ENC_NA); + esl_header_tree = proto_item_add_subtree(ti, ett_esl); + offset+=6; + + flags = tvb_get_letohs(tvb, offset); + proto_tree_add_uint(esl_header_tree, hf_esl_port, tvb, offset, 2, flags_to_port(flags)); + + proto_tree_add_item(esl_header_tree, hf_esl_alignerror, tvb, offset, 2, ENC_LITTLE_ENDIAN); + proto_tree_add_item(esl_header_tree, hf_esl_crcerror, tvb, offset, 2, ENC_LITTLE_ENDIAN); + + offset+=2; + + proto_tree_add_item(esl_header_tree, hf_esl_timestamp, tvb, offset, 8, ENC_LITTLE_ENDIAN); + } + } + return tvb_captured_length(tvb); +} + +typedef struct _ref_time_frame_info +{ + frame_data *fd; + guint64 esl_ts; + nstime_t abs_ts; + guint32 num; +} ref_time_frame_info; + +static ref_time_frame_info ref_time_frame; + +static gboolean is_esl_header(tvbuff_t *tvb, gint offset) +{ + return tvb_get_guint8(tvb, offset) == 0x01 && + tvb_get_guint8(tvb, offset+1) == 0x01 && + tvb_get_guint8(tvb, offset+2) == 0x05 && + (tvb_get_guint8(tvb, offset+3) == 0x10 ||tvb_get_guint8(tvb, offset+3) == 0x11)&& + tvb_get_guint8(tvb, offset+4) == 0x00 && + tvb_get_guint8(tvb, offset+5) == 0x00; +} + +static void modify_times(tvbuff_t *tvb, gint offset, packet_info *pinfo) +{ + if ( ref_time_frame.fd == NULL ) + { + ref_time_frame.esl_ts = tvb_get_letoh64(tvb, offset+8); + ref_time_frame.fd = pinfo->fd; + ref_time_frame.num = pinfo->num; + ref_time_frame.abs_ts = pinfo->abs_ts; + } + else if ( !pinfo->fd->visited ) + { + guint64 nsecs = tvb_get_letoh64(tvb, offset+8) - ref_time_frame.esl_ts; + guint64 secs = nsecs/1000000000; + nstime_t ts; + nstime_t ts_delta; + + ts.nsecs = ref_time_frame.abs_ts.nsecs + (int)(nsecs-(secs*1000000000)); + if ( ts.nsecs > 1000000000 ) + { + ts.nsecs-=1000000000; + secs++; + } + + ts.secs = ref_time_frame.abs_ts.secs+(int)secs; + nstime_delta(&ts_delta, &ts, &pinfo->abs_ts); + + pinfo->abs_ts = ts; + pinfo->fd->abs_ts = ts; + nstime_add(&pinfo->rel_ts, &ts_delta); + } +} + +static gboolean +dissect_esl_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + static gboolean in_heur = FALSE; + gboolean result; + tvbuff_t *next_tvb; + guint esl_length = tvb_captured_length(tvb); + + if ( in_heur ) + return FALSE; + + in_heur = TRUE; + /*TRY */ + { + if ( ref_time_frame.fd != NULL && !pinfo->fd->visited && pinfo->num <= ref_time_frame.num ) + ref_time_frame.fd = NULL; + + /* Check that there's enough data */ + if ( esl_length < SIZEOF_ESLHEADER ) + return FALSE; + + /* check for Esl frame, this has a unique destination MAC from Beckhoff range + First 6 bytes must be: 01 01 05 10 00 00 */ + if ( is_esl_header(tvb, 0) ) + { + dissect_esl_header(tvb, pinfo, tree, data); + if ( eth_withoutfcs_handle != NULL ) + { + next_tvb = tvb_new_subset_remaining(tvb, SIZEOF_ESLHEADER); + call_dissector(eth_withoutfcs_handle, next_tvb, pinfo, tree); + } + modify_times(tvb, 0, pinfo); + result = TRUE; + } + else if ( is_esl_header(tvb, esl_length-SIZEOF_ESLHEADER) ) + { + if ( eth_withoutfcs_handle != NULL ) + { + next_tvb = tvb_new_subset_length(tvb, 0, esl_length-SIZEOF_ESLHEADER); + call_dissector(eth_withoutfcs_handle, next_tvb, pinfo, tree); + } + next_tvb = tvb_new_subset_length(tvb, esl_length-SIZEOF_ESLHEADER, SIZEOF_ESLHEADER); + dissect_esl_header(next_tvb, pinfo, tree, data); + modify_times(tvb, esl_length-SIZEOF_ESLHEADER, pinfo); + + result = TRUE; + } + else + { + result = FALSE; + } + } + /*CATCH_ALL{ + in_heur = FALSE; + RETHROW; + }ENDTRY;*/ + in_heur = FALSE; + return result; +} + +void +proto_register_esl(void) { + static hf_register_info hf[] = { + { &hf_esl_port, + { "Port", "esl.port", + FT_UINT16, BASE_DEC, NULL, 0x00, + NULL, HFILL } + }, + { &hf_esl_crcerror, + { "Crc Error", "esl.crcerror", + FT_BOOLEAN, 16, TFS(&flags_yes_no), esl_crcError_bitmask, + NULL, HFILL } + }, + { &hf_esl_alignerror, + { "Alignment Error", "esl.alignerror", + FT_BOOLEAN, 16, TFS(&flags_yes_no), esl_alignError_bitmask, + NULL, HFILL } + }, + { &hf_esl_timestamp, + { "timestamp", "esl.timestamp", + FT_UINT64, BASE_HEX, NULL, 0x0, + NULL, HFILL } + }, + }; + + static gint *ett[] = { + &ett_esl, + }; + + module_t *esl_module; + + proto_esl = proto_register_protocol("EtherCAT Switch Link", + "ESL","esl"); + + esl_module = prefs_register_protocol_obsolete(proto_esl); + + prefs_register_obsolete_preference(esl_module, "enable"); + + proto_register_field_array(proto_esl,hf,array_length(hf)); + proto_register_subtree_array(ett,array_length(ett)); + + register_dissector("esl", dissect_esl_header, proto_esl); +} + +void +proto_reg_handoff_esl(void) { + static gboolean initialized = FALSE; + + if (!initialized) { + eth_withoutfcs_handle = find_dissector_add_dependency("eth_withoutfcs", proto_esl); + heur_dissector_add("eth", dissect_esl_heur, "EtherCAT over Ethernet", "esl_eth", proto_esl, HEURISTIC_DISABLE); + initialized = TRUE; + } +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |