/* packet-esl.c * Routines for EtherCAT Switch Link disassembly * * Copyright (c) 2007 by Beckhoff Automation GmbH * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1999 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "config.h" #include #include 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; 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(proto_esl, proto_reg_handoff_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 - http://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: */