2003-08-16 01:53:41 +00:00
|
|
|
/* packet-enip.c
|
2003-06-11 22:36:18 +00:00
|
|
|
* Routines for EtherNet/IP (Industrial Protocol) dissection
|
2003-06-11 09:02:19 +00:00
|
|
|
* EtherNet/IP Home: www.odva.org
|
|
|
|
*
|
2004-02-04 20:34:53 +00:00
|
|
|
* Copyright 2003-2004
|
2003-06-11 09:02:19 +00:00
|
|
|
* Magnus Hansson <mah@hms.se>
|
|
|
|
* Joakim Wiberg <jow@hms.se>
|
|
|
|
*
|
2009-06-26 02:38:23 +00:00
|
|
|
* Conversation data support for CIP
|
|
|
|
* Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG
|
|
|
|
* Copyright 2007
|
|
|
|
*
|
2004-07-18 00:24:25 +00:00
|
|
|
* $Id$
|
2003-06-11 09:02:19 +00:00
|
|
|
*
|
2006-05-21 04:49:01 +00:00
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
2003-06-11 09:02:19 +00:00
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
#include <glib.h>
|
|
|
|
|
|
|
|
#include <epan/packet.h>
|
2009-06-26 02:38:23 +00:00
|
|
|
#include <epan/emem.h>
|
|
|
|
#include <epan/conversation.h>
|
|
|
|
#include <epan/prefs.h>
|
2009-07-09 20:44:28 +00:00
|
|
|
#include <epan/etypes.h>
|
2004-01-27 04:43:35 +00:00
|
|
|
#include "packet-tcp.h"
|
2009-06-26 02:38:23 +00:00
|
|
|
#include "packet-enip.h"
|
2004-09-23 17:34:35 +00:00
|
|
|
#include "packet-cip.h"
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
#define se_new(type) ((type*)se_alloc(sizeof(type)))
|
2004-09-23 17:34:35 +00:00
|
|
|
|
|
|
|
/* Communication Ports */
|
2009-06-26 02:38:23 +00:00
|
|
|
#define ENIP_ENCAP_PORT 44818 /* EtherNet/IP located on port 44818 */
|
|
|
|
#define ENIP_IO_PORT 2222 /* EtherNet/IP IO located on port 2222 */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
/* EtherNet/IP function codes */
|
2003-06-11 09:02:19 +00:00
|
|
|
#define NOP 0x0000
|
|
|
|
#define LIST_SERVICES 0x0004
|
|
|
|
#define LIST_IDENTITY 0x0063
|
|
|
|
#define LIST_INTERFACES 0x0064
|
|
|
|
#define REGISTER_SESSION 0x0065
|
|
|
|
#define UNREGISTER_SESSION 0x0066
|
|
|
|
#define SEND_RR_DATA 0x006F
|
|
|
|
#define SEND_UNIT_DATA 0x0070
|
|
|
|
#define INDICATE_STATUS 0x0072
|
|
|
|
#define CANCEL 0x0073
|
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
/* EtherNet/IP status codes */
|
2003-06-11 09:02:19 +00:00
|
|
|
#define SUCCESS 0x0000
|
|
|
|
#define INVALID_CMD 0x0001
|
|
|
|
#define NO_RESOURCES 0x0002
|
|
|
|
#define INCORRECT_DATA 0x0003
|
|
|
|
#define INVALID_SESSION 0x0064
|
|
|
|
#define INVALID_LENGTH 0x0065
|
|
|
|
#define UNSUPPORTED_PROT_REV 0x0069
|
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
/* EtherNet/IP Common Data Format Type IDs */
|
2003-06-11 09:02:19 +00:00
|
|
|
#define CDF_NULL 0x0000
|
|
|
|
#define LIST_IDENTITY_RESP 0x000C
|
|
|
|
#define CONNECTION_BASED 0x00A1
|
|
|
|
#define CONNECTION_TRANSPORT 0x00B1
|
|
|
|
#define UNCONNECTED_MSG 0x00B2
|
|
|
|
#define LIST_SERVICES_RESP 0x0100
|
|
|
|
#define SOCK_ADR_INFO_OT 0x8000
|
|
|
|
#define SOCK_ADR_INFO_TO 0x8001
|
|
|
|
#define SEQ_ADDRESS 0x8002
|
|
|
|
|
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
/* Initialize the protocol and registered fields */
|
|
|
|
static int proto_enip = -1;
|
|
|
|
|
|
|
|
static int hf_enip_command = -1;
|
|
|
|
static int hf_enip_options = -1;
|
|
|
|
static int hf_enip_sendercontex = -1;
|
|
|
|
static int hf_enip_status = -1;
|
|
|
|
static int hf_enip_session = -1;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
static int hf_enip_lir_sinfamily = -1;
|
|
|
|
static int hf_enip_lir_sinport = -1;
|
|
|
|
static int hf_enip_lir_sinaddr = -1;
|
|
|
|
static int hf_enip_lir_sinzero = -1;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
static int hf_enip_lir_vendor = -1;
|
|
|
|
static int hf_enip_lir_devtype = -1;
|
|
|
|
static int hf_enip_lir_prodcode = -1;
|
|
|
|
static int hf_enip_lir_status = -1;
|
|
|
|
static int hf_enip_lir_serial = -1;
|
|
|
|
static int hf_enip_lir_name = -1;
|
|
|
|
static int hf_enip_lir_state = -1;
|
|
|
|
|
|
|
|
static int hf_enip_lsr_tcp = -1;
|
|
|
|
static int hf_enip_lsr_udp = -1;
|
|
|
|
|
|
|
|
static int hf_enip_srrd_ifacehnd = -1;
|
|
|
|
|
|
|
|
static int hf_enip_sud_ifacehnd = -1;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
static int hf_enip_cpf_typeid = -1;
|
|
|
|
static int hf_enip_cpf_sai_connid = -1;
|
|
|
|
static int hf_enip_cpf_sai_seqnum = -1;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
static int hf_enip_response_in = -1;
|
|
|
|
static int hf_enip_response_to = -1;
|
|
|
|
static int hf_enip_time = -1;
|
|
|
|
|
2003-06-11 09:02:19 +00:00
|
|
|
/* Initialize the subtree pointers */
|
2004-09-23 17:34:35 +00:00
|
|
|
static gint ett_enip = -1;
|
|
|
|
static gint ett_count_tree = -1;
|
|
|
|
static gint ett_type_tree = -1;
|
|
|
|
static gint ett_command_tree = -1;
|
|
|
|
static gint ett_sockadd = -1;
|
|
|
|
static gint ett_lsrcf = -1;
|
|
|
|
|
|
|
|
static proto_tree *g_tree;
|
|
|
|
static dissector_table_t subdissector_srrd_table;
|
|
|
|
static dissector_table_t subdissector_sud_table;
|
|
|
|
static dissector_handle_t data_handle;
|
|
|
|
|
|
|
|
static gboolean enip_desegment = TRUE;
|
2003-06-11 22:36:18 +00:00
|
|
|
|
2009-07-09 20:44:28 +00:00
|
|
|
static int proto_dlr = -1;
|
|
|
|
|
|
|
|
static int hf_dlr_ringsubtype = -1;
|
|
|
|
static int hf_dlr_ringprotoversion = -1;
|
|
|
|
static int hf_dlr_frametype = -1;
|
|
|
|
static int hf_dlr_sourceport = -1;
|
|
|
|
static int hf_dlr_sourceip = -1;
|
|
|
|
static int hf_dlr_sequenceid = -1;
|
|
|
|
|
|
|
|
static int hf_dlr_ringstate = -1;
|
|
|
|
static int hf_dlr_supervisorprecedence = -1;
|
|
|
|
static int hf_dlr_beaconinterval = -1;
|
|
|
|
static int hf_dlr_beacontimeout = -1;
|
|
|
|
static int hf_dlr_beaconreserved = -1;
|
|
|
|
|
|
|
|
static int hf_dlr_nreqreserved = -1;
|
|
|
|
|
|
|
|
static int hf_dlr_nressourceport = -1;
|
|
|
|
static int hf_dlr_nresreserved = -1;
|
|
|
|
|
|
|
|
static int hf_dlr_lnknbrstatus = -1;
|
|
|
|
static int hf_dlr_lnknbrreserved = -1;
|
|
|
|
|
|
|
|
static int hf_dlr_lfreserved = -1;
|
|
|
|
|
|
|
|
static int hf_dlr_anreserved = -1;
|
|
|
|
|
|
|
|
static int hf_dlr_sonumnodes = -1;
|
|
|
|
static int hf_dlr_somac = -1;
|
|
|
|
static int hf_dlr_soip = -1;
|
|
|
|
static int hf_dlr_soreserved = -1;
|
|
|
|
|
|
|
|
static gint ett_dlr = -1;
|
|
|
|
|
2003-06-11 09:02:19 +00:00
|
|
|
/* Translate function to string - Encapsulation commands */
|
|
|
|
static const value_string encap_cmd_vals[] = {
|
2009-06-26 02:38:23 +00:00
|
|
|
{ NOP, "NOP" },
|
|
|
|
{ LIST_SERVICES, "List Services" },
|
|
|
|
{ LIST_IDENTITY, "List Identity" },
|
|
|
|
{ LIST_INTERFACES, "List Interfaces" },
|
|
|
|
{ REGISTER_SESSION, "Register Session" },
|
|
|
|
{ UNREGISTER_SESSION,"Unregister Session" },
|
|
|
|
{ SEND_RR_DATA, "Send RR Data" },
|
|
|
|
{ SEND_UNIT_DATA, "Send Unit Data" },
|
|
|
|
{ INDICATE_STATUS, "Indicate Status" },
|
|
|
|
{ CANCEL, "Cancel" },
|
|
|
|
|
|
|
|
{ 0, NULL }
|
2003-06-11 09:02:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Translate function to string - Encapsulation status */
|
|
|
|
static const value_string encap_status_vals[] = {
|
2009-06-26 02:38:23 +00:00
|
|
|
{ SUCCESS, "Success" },
|
|
|
|
{ INVALID_CMD, "Invalid Command" },
|
|
|
|
{ NO_RESOURCES, "No Memory Resources" },
|
|
|
|
{ INCORRECT_DATA, "Incorrect Data" },
|
|
|
|
{ INVALID_SESSION, "Invalid Session Handle" },
|
|
|
|
{ INVALID_LENGTH, "Invalid Length" },
|
|
|
|
{ UNSUPPORTED_PROT_REV, "Unsupported Protocol Revision" },
|
|
|
|
|
|
|
|
{ 0, NULL }
|
2003-06-11 09:02:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* Translate function to Common data format values */
|
|
|
|
static const value_string cdf_type_vals[] = {
|
2009-06-26 02:38:23 +00:00
|
|
|
{ CDF_NULL, "Null Address Item" },
|
|
|
|
{ LIST_IDENTITY_RESP, "List Identity Response" },
|
|
|
|
{ CONNECTION_BASED, "Connected Address Item" },
|
|
|
|
{ CONNECTION_TRANSPORT, "Connected Data Item" },
|
|
|
|
{ UNCONNECTED_MSG, "Unconnected Data Item" },
|
|
|
|
{ LIST_SERVICES_RESP, "List Services Response" },
|
|
|
|
{ SOCK_ADR_INFO_OT, "Socket Address Info O->T" },
|
|
|
|
{ SOCK_ADR_INFO_TO, "Socket Address Info T->O" },
|
|
|
|
{ SEQ_ADDRESS, "Sequenced Address Item" },
|
|
|
|
|
|
|
|
{ 0, NULL }
|
2003-06-11 09:02:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Translate function to string - True/False */
|
|
|
|
static const value_string enip_true_false_vals[] = {
|
2009-06-26 02:38:23 +00:00
|
|
|
{ 0, "False" },
|
|
|
|
{ 1, "True" },
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
{ 0, NULL }
|
2003-06-11 09:02:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
/* Translate interface handle to string */
|
|
|
|
static const value_string enip_interface_handle_vals[] = {
|
2009-06-26 02:38:23 +00:00
|
|
|
{ 0, "CIP" },
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
{ 0, NULL }
|
2003-06-11 09:02:19 +00:00
|
|
|
};
|
|
|
|
|
2009-07-09 20:44:28 +00:00
|
|
|
/* Translate function to DLR Frame Type values */
|
|
|
|
static const value_string dlr_frame_type_vals[] = {
|
|
|
|
{ DLR_FT_BEACON, "Beacon" },
|
|
|
|
{ DLR_FT_NEIGHBOR_REQ, "Neighbor_Check_Request" },
|
|
|
|
{ DLR_FT_NEIGHBOR_RES, "Neighbor_Check_Response" },
|
|
|
|
{ DLR_FT_LINK_STAT, "Link_Status / Neighbor_Status" },
|
|
|
|
{ DLR_FT_LOCATE_FLT, "Locate_Fault" },
|
|
|
|
{ DLR_FT_ANNOUNCE, "Announce" },
|
|
|
|
{ DLR_FT_SIGN_ON, "Sign_On" },
|
|
|
|
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Translate function to DLR Source Port values */
|
|
|
|
static const value_string dlr_source_port_vals[] = {
|
|
|
|
{ 0, "Port 1 or Port 2" },
|
|
|
|
{ 1, "Port 1" },
|
|
|
|
{ 2, "Port 2" },
|
|
|
|
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Translate function to DLR Ring State values */
|
|
|
|
static const value_string dlr_ring_state_vals[] = {
|
|
|
|
{ 1, "RING_NORMAL_STATE" },
|
|
|
|
{ 2, "RING_FAULT_STATE" },
|
|
|
|
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Translate function to DLR Link_Status/Neighbor_Status Status values */
|
|
|
|
static const value_string dlr_lnk_nbr_status_vals[] = {
|
|
|
|
{ 0x01, "PORT_1_UP" },
|
|
|
|
{ 0x02, "PORT_2_UP" },
|
|
|
|
{ 0x80, "NEIGHBOR_STATUS_FLAG" },
|
|
|
|
|
|
|
|
{ 0, NULL }
|
|
|
|
};
|
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
static GHashTable *enip_request_hashtable = NULL;
|
|
|
|
|
|
|
|
/* Return codes of function classifying packets as query/response */
|
|
|
|
#define ENIP_REQUEST_PACKET 0
|
|
|
|
#define ENIP_RESPONSE_PACKET 1
|
|
|
|
#define ENIP_CANNOT_CLASSIFY 2
|
|
|
|
|
|
|
|
enum enip_packet_data_type { EPDT_UNKNOWN, EPDT_CONNECTED_TRANSPORT, EPDT_UNCONNECTED };
|
|
|
|
|
|
|
|
typedef struct enip_request_key {
|
|
|
|
gint requesttype;
|
|
|
|
enum enip_packet_data_type type;
|
|
|
|
guint32 session_handle;
|
|
|
|
guint64 sender_context;
|
|
|
|
guint32 conversation;
|
|
|
|
union {
|
|
|
|
struct {
|
|
|
|
guint32 connid;
|
|
|
|
guint16 sequence;
|
|
|
|
} connected_transport;
|
|
|
|
} data;
|
|
|
|
} enip_request_key_t;
|
|
|
|
|
|
|
|
typedef struct enip_request_val {
|
|
|
|
emem_tree_t *frames;
|
|
|
|
} enip_request_val_t;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Hash Functions
|
|
|
|
*/
|
|
|
|
static gint
|
|
|
|
enip_request_equal(gconstpointer v, gconstpointer w)
|
|
|
|
{
|
|
|
|
const enip_request_key_t *v1 = (const enip_request_key_t *)v;
|
|
|
|
const enip_request_key_t *v2 = (const enip_request_key_t *)w;
|
|
|
|
|
|
|
|
if ( v1->conversation == v2->conversation
|
|
|
|
&& v1->session_handle == v2->session_handle
|
|
|
|
&& v1->type == v2->type
|
|
|
|
&& ( ( v1->sender_context == v2->sender_context /* heuristic approach */
|
|
|
|
&& v1->type == EPDT_UNCONNECTED
|
|
|
|
)
|
|
|
|
||
|
|
|
|
( v1->data.connected_transport.connid == v2->data.connected_transport.connid
|
|
|
|
&& v1->data.connected_transport.sequence == v2->data.connected_transport.sequence
|
|
|
|
&& v1->type == EPDT_CONNECTED_TRANSPORT
|
|
|
|
)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint
|
|
|
|
enip_request_hash (gconstpointer v)
|
|
|
|
{
|
|
|
|
const enip_request_key_t *key = (const enip_request_key_t *)v;
|
|
|
|
guint val;
|
|
|
|
|
|
|
|
val = (guint)( key->conversation * 37 + key->session_handle * 93 + key->type * 765
|
|
|
|
+ key->sender_context * 23
|
|
|
|
+ key->data.connected_transport.connid * 87 + key->data.connected_transport.sequence * 834 );
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
static enip_request_info_t *
|
|
|
|
enip_match_request( packet_info *pinfo, proto_tree *tree, enip_request_key_t *prequest_key )
|
|
|
|
{
|
|
|
|
enip_request_key_t *new_request_key;
|
|
|
|
enip_request_val_t *request_val;
|
|
|
|
enip_request_info_t *request_info = NULL;
|
|
|
|
|
|
|
|
request_info = NULL;
|
|
|
|
request_val = g_hash_table_lookup( enip_request_hashtable, prequest_key );
|
|
|
|
if(!pinfo->fd->flags.visited)
|
|
|
|
{
|
|
|
|
if ( prequest_key && prequest_key->requesttype == ENIP_REQUEST_PACKET )
|
|
|
|
{
|
|
|
|
if ( request_val == NULL )
|
|
|
|
{
|
|
|
|
new_request_key = se_alloc(sizeof(enip_request_key_t));
|
|
|
|
memcpy( new_request_key, prequest_key, sizeof(enip_request_key_t) );
|
|
|
|
|
|
|
|
request_val = se_alloc(sizeof(enip_request_val_t));
|
|
|
|
request_val->frames = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "enip_frames");
|
|
|
|
|
|
|
|
g_hash_table_insert(enip_request_hashtable, new_request_key, request_val );
|
|
|
|
}
|
|
|
|
|
|
|
|
request_info = se_alloc(sizeof(enip_request_info_t));
|
|
|
|
request_info->req_num = pinfo->fd->num;
|
|
|
|
request_info->rep_num = 0;
|
|
|
|
request_info->req_time = pinfo->fd->abs_ts;
|
|
|
|
request_info->cip_info = NULL;
|
|
|
|
se_tree_insert32(request_val->frames, pinfo->fd->num, (void *)request_info);
|
|
|
|
}
|
|
|
|
if( request_val && prequest_key && prequest_key->requesttype == ENIP_RESPONSE_PACKET )
|
|
|
|
{
|
|
|
|
request_info = (enip_request_info_t*)se_tree_lookup32_le( request_val->frames, pinfo->fd->num );
|
|
|
|
if ( request_info )
|
|
|
|
{
|
|
|
|
request_info->rep_num = pinfo->fd->num;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( request_val )
|
|
|
|
request_info = (enip_request_info_t*)se_tree_lookup32_le( request_val->frames, pinfo->fd->num );
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( tree && request_info )
|
|
|
|
{
|
|
|
|
/* print state tracking in the tree */
|
|
|
|
if ( prequest_key && prequest_key->requesttype == ENIP_REQUEST_PACKET )
|
|
|
|
{
|
|
|
|
/* This is a request */
|
|
|
|
if (request_info->rep_num)
|
|
|
|
{
|
|
|
|
proto_item *it;
|
|
|
|
|
|
|
|
it = proto_tree_add_uint(tree, hf_enip_response_in,
|
|
|
|
NULL, 0, 0, request_info->rep_num);
|
|
|
|
PROTO_ITEM_SET_GENERATED(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( prequest_key && prequest_key->requesttype == ENIP_RESPONSE_PACKET )
|
|
|
|
{
|
|
|
|
/* This is a reply */
|
|
|
|
if (request_info->req_num)
|
|
|
|
{
|
|
|
|
proto_item *it;
|
|
|
|
nstime_t ns;
|
|
|
|
|
|
|
|
it = proto_tree_add_uint(tree, hf_enip_response_to,
|
|
|
|
NULL, 0, 0, request_info->req_num);
|
|
|
|
PROTO_ITEM_SET_GENERATED(it);
|
|
|
|
|
|
|
|
nstime_delta(&ns, &pinfo->fd->abs_ts, &request_info->req_time);
|
|
|
|
it = proto_tree_add_time(tree, hf_enip_time, NULL, 0, 0, &ns);
|
|
|
|
PROTO_ITEM_SET_GENERATED(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return request_info;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Connection management
|
|
|
|
*/
|
|
|
|
|
|
|
|
typedef struct enip_conn_key {
|
|
|
|
guint16 ConnSerialNumber;
|
|
|
|
guint16 VendorID;
|
|
|
|
guint32 DeviceSerialNumber;
|
|
|
|
} enip_conn_key_t;
|
|
|
|
|
|
|
|
typedef struct enip_conn_val {
|
|
|
|
guint16 ConnSerialNumber;
|
|
|
|
guint16 VendorID;
|
|
|
|
guint32 DeviceSerialNumber;
|
|
|
|
guint32 O2TConnID;
|
|
|
|
guint32 T2OConnID;
|
|
|
|
guint32 openframe;
|
|
|
|
guint32 closeframe;
|
|
|
|
guint32 connid;
|
|
|
|
} enip_conn_val_t;
|
|
|
|
|
|
|
|
typedef struct _enip_conv_info_t {
|
|
|
|
emem_tree_t *O2TConnIDs;
|
|
|
|
emem_tree_t *T2OConnIDs;
|
|
|
|
} enip_conv_info_t;
|
|
|
|
|
|
|
|
static GHashTable *enip_conn_hashtable = NULL;
|
|
|
|
static guint32 enip_unique_connid = 1;
|
|
|
|
|
|
|
|
static gint
|
|
|
|
enip_conn_equal(gconstpointer v, gconstpointer w)
|
|
|
|
{
|
|
|
|
const enip_conn_key_t *v1 = (const enip_conn_key_t *)v;
|
|
|
|
const enip_conn_key_t *v2 = (const enip_conn_key_t *)w;
|
|
|
|
|
|
|
|
if ( v1->ConnSerialNumber == v2->ConnSerialNumber
|
|
|
|
&& v1->VendorID == v2->VendorID
|
|
|
|
&& v1->DeviceSerialNumber == v2->DeviceSerialNumber
|
|
|
|
)
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint
|
|
|
|
enip_conn_hash (gconstpointer v)
|
|
|
|
{
|
|
|
|
const enip_conn_key_t *key = (const enip_conn_key_t *)v;
|
|
|
|
guint val;
|
|
|
|
|
|
|
|
val = (guint)( key->ConnSerialNumber + key->VendorID + key->DeviceSerialNumber );
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
|
|
|
void enip_open_cip_connection( packet_info *pinfo,
|
|
|
|
guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber,
|
|
|
|
guint32 O2TConnID, guint32 T2OConnID )
|
|
|
|
{
|
|
|
|
enip_conn_key_t *conn_key;
|
|
|
|
enip_conn_val_t *conn_val;
|
|
|
|
conversation_t *conversation;
|
|
|
|
enip_conv_info_t *enip_info;
|
|
|
|
|
|
|
|
if (pinfo->fd->flags.visited)
|
|
|
|
return;
|
|
|
|
|
|
|
|
conn_key = se_alloc(sizeof(enip_conn_key_t));
|
|
|
|
conn_key->ConnSerialNumber = ConnSerialNumber;
|
|
|
|
conn_key->VendorID = VendorID;
|
|
|
|
conn_key->DeviceSerialNumber = DeviceSerialNumber;
|
|
|
|
|
|
|
|
conn_val = g_hash_table_lookup( enip_conn_hashtable, conn_key );
|
|
|
|
if ( conn_val == NULL )
|
|
|
|
{
|
|
|
|
conn_val = se_alloc(sizeof(enip_conn_val_t));
|
|
|
|
|
|
|
|
conn_val->ConnSerialNumber = ConnSerialNumber;
|
|
|
|
conn_val->VendorID = VendorID;
|
|
|
|
conn_val->DeviceSerialNumber = DeviceSerialNumber;
|
|
|
|
conn_val->O2TConnID = O2TConnID;
|
|
|
|
conn_val->T2OConnID = T2OConnID;
|
|
|
|
conn_val->openframe = pinfo->fd->num;
|
|
|
|
conn_val->closeframe = 0;
|
|
|
|
conn_val->connid = enip_unique_connid++;
|
|
|
|
|
|
|
|
g_hash_table_insert(enip_conn_hashtable, conn_key, conn_val );
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we have a conversation for this connection?
|
|
|
|
*/
|
|
|
|
conversation = find_conversation(pinfo->fd->num,
|
|
|
|
&pinfo->src, &pinfo->dst,
|
|
|
|
pinfo->ptype,
|
|
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
|
|
if (conversation == NULL)
|
|
|
|
{
|
|
|
|
/* We don't yet have a conversation, so create one. */
|
|
|
|
conversation = conversation_new(pinfo->fd->num,
|
|
|
|
&pinfo->src, &pinfo->dst,
|
|
|
|
pinfo->ptype,
|
|
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Do we already have a state structure for this conv
|
|
|
|
*/
|
|
|
|
enip_info = conversation_get_proto_data(conversation, proto_enip);
|
|
|
|
if (!enip_info)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* No. Attach that information to the conversation, and add
|
|
|
|
* it to the list of information structures.
|
|
|
|
*/
|
|
|
|
enip_info = se_alloc(sizeof(enip_conv_info_t));
|
|
|
|
enip_info->O2TConnIDs = se_tree_create_non_persistent(
|
|
|
|
EMEM_TREE_TYPE_RED_BLACK, "enip_O2T");
|
|
|
|
enip_info->T2OConnIDs = se_tree_create_non_persistent(
|
|
|
|
EMEM_TREE_TYPE_RED_BLACK, "enip_T2O");
|
|
|
|
|
|
|
|
conversation_add_proto_data(conversation, proto_enip, enip_info);
|
|
|
|
}
|
|
|
|
se_tree_insert32(enip_info->O2TConnIDs, O2TConnID, (void *)conn_val);
|
|
|
|
se_tree_insert32(enip_info->O2TConnIDs, T2OConnID, (void *)conn_val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void enip_close_cip_connection( packet_info *pinfo,
|
|
|
|
guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber )
|
|
|
|
{
|
|
|
|
enip_conn_key_t conn_key;
|
|
|
|
enip_conn_val_t *conn_val;
|
|
|
|
|
|
|
|
if (pinfo->fd->flags.visited)
|
|
|
|
return;
|
|
|
|
|
|
|
|
conn_key.ConnSerialNumber = ConnSerialNumber;
|
|
|
|
conn_key.VendorID = VendorID;
|
|
|
|
conn_key.DeviceSerialNumber = DeviceSerialNumber;
|
|
|
|
|
|
|
|
conn_val = g_hash_table_lookup( enip_conn_hashtable, &conn_key );
|
|
|
|
if ( conn_val )
|
|
|
|
{
|
|
|
|
conn_val->closeframe = pinfo->fd->num;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static guint32 enip_get_connid( packet_info *pinfo, enip_request_key_t *prequest_key, guint32 connid )
|
|
|
|
{
|
|
|
|
conversation_t *conversation;
|
|
|
|
enip_conv_info_t *enip_info;
|
|
|
|
enip_conn_val_t *conn_val;
|
|
|
|
|
|
|
|
if ( prequest_key == NULL
|
|
|
|
|| ( prequest_key->requesttype != ENIP_REQUEST_PACKET && prequest_key->requesttype != ENIP_RESPONSE_PACKET )
|
|
|
|
)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we have a conversation for this connection?
|
|
|
|
*/
|
|
|
|
conversation = find_conversation(pinfo->fd->num,
|
|
|
|
&pinfo->src, &pinfo->dst,
|
|
|
|
pinfo->ptype,
|
|
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
|
|
if (conversation == NULL)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we already have a state structure for this conv
|
|
|
|
*/
|
|
|
|
enip_info = conversation_get_proto_data(conversation, proto_enip);
|
|
|
|
if (!enip_info)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
conn_val = NULL;
|
|
|
|
switch ( prequest_key->requesttype )
|
|
|
|
{
|
|
|
|
case ENIP_REQUEST_PACKET:
|
|
|
|
conn_val = se_tree_lookup32( enip_info->O2TConnIDs, connid );
|
|
|
|
if ( conn_val == NULL )
|
|
|
|
conn_val = se_tree_lookup32( enip_info->T2OConnIDs, connid );
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ENIP_RESPONSE_PACKET:
|
|
|
|
conn_val = se_tree_lookup32( enip_info->T2OConnIDs, connid );
|
|
|
|
if ( conn_val == NULL )
|
|
|
|
conn_val = se_tree_lookup32( enip_info->O2TConnIDs, connid );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( conn_val == NULL )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if ( conn_val->openframe > pinfo->fd->num )
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return conn_val->connid;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Protocol initialization
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
enip_init_protocol(void)
|
|
|
|
{
|
|
|
|
if (enip_request_hashtable)
|
|
|
|
g_hash_table_destroy(enip_request_hashtable);
|
|
|
|
enip_request_hashtable = g_hash_table_new(enip_request_hash, enip_request_equal);
|
|
|
|
|
|
|
|
if (enip_conn_hashtable)
|
|
|
|
g_hash_table_destroy(enip_conn_hashtable);
|
|
|
|
enip_conn_hashtable = g_hash_table_new(enip_conn_hash, enip_conn_equal);
|
|
|
|
}
|
|
|
|
|
|
|
|
static proto_item*
|
|
|
|
add_byte_array_text_to_proto_tree( proto_tree *tree, tvbuff_t *tvb, gint start, gint length, const char* str )
|
|
|
|
{
|
|
|
|
const char *tmp;
|
|
|
|
char *tmp2, *tmp2start;
|
|
|
|
proto_item *pi;
|
|
|
|
int i,tmp_length,tmp2_length;
|
|
|
|
guint32 octet;
|
|
|
|
/* At least one version of Apple's C compiler/linker is buggy, causing
|
|
|
|
a complaint from the linker about the "literal C string section"
|
|
|
|
not ending with '\0' if we initialize a 16-element "char" array with
|
|
|
|
a 16-character string, the fact that initializing such an array with
|
|
|
|
such a string is perfectly legitimate ANSI C nonwithstanding, the 17th
|
|
|
|
'\0' byte in the string nonwithstanding. */
|
|
|
|
static const char my_hex_digits[16] =
|
|
|
|
{ '0', '1', '2', '3', '4', '5', '6', '7',
|
|
|
|
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
|
|
|
|
|
|
|
|
|
|
|
|
if( ( length * 2 ) > 32 )
|
|
|
|
{
|
|
|
|
tmp_length = 16;
|
|
|
|
tmp2_length = 36;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tmp_length = length;
|
|
|
|
tmp2_length = ( length * 2 ) + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = (const char *)tvb_get_ptr( tvb, start, tmp_length );
|
|
|
|
tmp2 = (char *)ep_alloc( tmp2_length );
|
|
|
|
|
|
|
|
tmp2start = tmp2;
|
|
|
|
|
|
|
|
for( i = 0; i < tmp_length; i++ )
|
|
|
|
{
|
|
|
|
octet = tmp[i];
|
|
|
|
octet >>= 4;
|
|
|
|
*tmp2++ = my_hex_digits[octet&0xF];
|
|
|
|
octet = tmp[i];
|
|
|
|
*tmp2++ = my_hex_digits[octet&0xF];
|
|
|
|
}
|
|
|
|
|
|
|
|
if( tmp_length != length )
|
|
|
|
{
|
|
|
|
*tmp2++ = '.';
|
|
|
|
*tmp2++ = '.';
|
|
|
|
*tmp2++ = '.';
|
|
|
|
}
|
|
|
|
|
|
|
|
*tmp2 = '\0';
|
|
|
|
|
|
|
|
pi = proto_tree_add_text( tree, tvb, start, length, "%s%s", str, tmp2start );
|
|
|
|
|
|
|
|
return( pi );
|
|
|
|
|
|
|
|
} /* end of add_byte_array_text_to_proto_tree() */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
/* Disssect Common Packet Format */
|
2003-06-11 09:02:19 +00:00
|
|
|
static void
|
2009-06-26 02:38:23 +00:00
|
|
|
dissect_cpf( enip_request_key_t *request_key, int command, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 ifacehndl )
|
2003-06-11 09:02:19 +00:00
|
|
|
{
|
2004-09-23 17:34:35 +00:00
|
|
|
proto_item *temp_item, *count_item, *type_item, *sockaddr_item;
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_tree *temp_tree, *count_tree, *item_tree, *sockaddr_tree;
|
|
|
|
int temp_data, item_count, item_length, item;
|
|
|
|
unsigned char name_length;
|
|
|
|
tvbuff_t *next_tvb;
|
|
|
|
enip_request_info_t *request_info;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Create item count tree */
|
|
|
|
item_count = tvb_get_letohs( tvb, offset );
|
|
|
|
count_item = proto_tree_add_text( tree, tvb, offset, 2, "Item Count: %d", item_count );
|
|
|
|
count_tree = proto_item_add_subtree( count_item, ett_count_tree );
|
2003-06-11 22:36:18 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
while( item_count-- )
|
|
|
|
{
|
|
|
|
/* Add item type tree to item count tree*/
|
|
|
|
type_item = proto_tree_add_item( count_tree, hf_enip_cpf_typeid, tvb, offset+2, 2, TRUE );
|
|
|
|
item_tree = proto_item_add_subtree( type_item, ett_type_tree );
|
2004-02-04 20:34:53 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Add length field to item type tree*/
|
|
|
|
proto_tree_add_text( item_tree, tvb, offset+4, 2, "Length: %d", tvb_get_letohs( tvb, offset+4 ) );
|
2003-06-11 22:36:18 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
item = tvb_get_letohs( tvb, offset+2 );
|
|
|
|
item_length = tvb_get_letohs( tvb, offset+4 );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
if( item_length )
|
|
|
|
{
|
|
|
|
/* Add item data field */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
switch( item )
|
|
|
|
{
|
|
|
|
case CONNECTION_BASED:
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
if ( request_key )
|
|
|
|
{
|
|
|
|
request_key->type = EPDT_CONNECTED_TRANSPORT;
|
|
|
|
request_key->data.connected_transport.connid = enip_get_connid( pinfo, request_key, tvb_get_letohl( tvb, offset+6 ) );
|
|
|
|
}
|
|
|
|
/* Add Connection identifier */
|
|
|
|
proto_tree_add_text( item_tree, tvb, offset+6, 4, "Connection Identifier: 0x%08X", tvb_get_letohl( tvb, offset + 6 ) );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Add Connection ID to Info col */
|
|
|
|
if(check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
{
|
|
|
|
col_append_fstr(pinfo->cinfo, COL_INFO,
|
|
|
|
", CONID: 0x%08X",
|
|
|
|
tvb_get_letohl( tvb, offset+6 ) );
|
|
|
|
}
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case UNCONNECTED_MSG:
|
|
|
|
request_info = NULL;
|
|
|
|
if ( request_key )
|
|
|
|
{
|
|
|
|
request_key->type = EPDT_UNCONNECTED;
|
|
|
|
request_info = enip_match_request( pinfo, tree, request_key );
|
|
|
|
}
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Call dissector for interface */
|
|
|
|
next_tvb = tvb_new_subset( tvb, offset+6, item_length, item_length );
|
|
|
|
p_add_proto_data(pinfo->fd, proto_enip, request_info);
|
|
|
|
if( tvb_length_remaining(next_tvb, 0) == 0 || !dissector_try_port(subdissector_srrd_table, ifacehndl, next_tvb, pinfo, g_tree) )
|
|
|
|
{
|
|
|
|
/* Show the undissected payload */
|
|
|
|
if( tvb_length_remaining(tvb, offset) > 0 )
|
|
|
|
call_dissector( data_handle, next_tvb, pinfo, g_tree );
|
|
|
|
}
|
|
|
|
p_remove_proto_data(pinfo->fd, proto_enip);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case CONNECTION_TRANSPORT:
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
if( command == SEND_UNIT_DATA )
|
|
|
|
{
|
|
|
|
request_info = NULL;
|
|
|
|
|
|
|
|
if ( request_key )
|
|
|
|
{
|
|
|
|
request_key->type = EPDT_CONNECTED_TRANSPORT;
|
|
|
|
request_key->data.connected_transport.sequence = tvb_get_letohs( tvb, offset+6 );
|
|
|
|
request_info = enip_match_request( pinfo, tree, request_key );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
** If the encapsulation service is SendUnit Data, this is a
|
|
|
|
** encapsulated connected message
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Add sequence count ( Transport Class 1,2,3 )*/
|
|
|
|
proto_tree_add_text( item_tree, tvb, offset+6, 2, "Sequence Count: 0x%04X", tvb_get_letohs( tvb, offset+6 ) );
|
|
|
|
|
|
|
|
/* Call dissector for interface */
|
|
|
|
next_tvb = tvb_new_subset (tvb, offset+8, item_length-2, item_length-2);
|
|
|
|
p_add_proto_data(pinfo->fd, proto_enip, request_info);
|
|
|
|
if( tvb_length_remaining(next_tvb, 0) == 0 || !dissector_try_port(subdissector_sud_table, ifacehndl, next_tvb, pinfo, g_tree) )
|
|
|
|
{
|
|
|
|
/* Show the undissected payload */
|
|
|
|
if( tvb_length_remaining(tvb, offset) > 0 )
|
|
|
|
call_dissector( data_handle, next_tvb, pinfo, g_tree );
|
|
|
|
}
|
|
|
|
p_remove_proto_data(pinfo->fd, proto_enip);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Display data */
|
|
|
|
add_byte_array_text_to_proto_tree( item_tree, tvb, offset+6, item_length, "Data: " );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
} /* End of if send unit data */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case LIST_IDENTITY_RESP:
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Encapsulation version */
|
|
|
|
temp_data = tvb_get_letohs( tvb, offset+6 );
|
|
|
|
proto_tree_add_text( item_tree, tvb, offset+6, 2, "Encapsulation Version: %d", temp_data );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket Address */
|
|
|
|
sockaddr_item = proto_tree_add_text( item_tree, tvb, offset+8, 16, "Socket Address");
|
|
|
|
sockaddr_tree = proto_item_add_subtree( sockaddr_item, ett_sockadd );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket address struct - sin_family */
|
|
|
|
proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinfamily,
|
|
|
|
tvb, offset+8, 2, FALSE );
|
2004-06-02 06:30:15 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket address struct - sin_port */
|
|
|
|
proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinport,
|
|
|
|
tvb, offset+10, 2, FALSE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket address struct - sin_address */
|
|
|
|
proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinaddr,
|
|
|
|
tvb, offset+12, 4, FALSE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket address struct - sin_zero */
|
|
|
|
proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinzero,
|
|
|
|
tvb, offset+16, 8, FALSE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Vendor ID */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_vendor,
|
|
|
|
tvb, offset+24, 2, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Device Type */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_devtype,
|
|
|
|
tvb, offset+26, 2, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Product Code */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_prodcode,
|
|
|
|
tvb, offset+28, 2, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Revision */
|
|
|
|
temp_data = tvb_get_letohs( tvb, offset+30 );
|
|
|
|
proto_tree_add_text( item_tree, tvb, offset+30, 2, "Revision: %d.%02d", temp_data & 0xFF, ( temp_data & 0xFF00 ) >> 8 );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Status */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_status,
|
|
|
|
tvb, offset+32, 2, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Serial Number */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_serial,
|
|
|
|
tvb, offset+34, 4, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Product Name Length */
|
|
|
|
name_length = tvb_get_guint8( tvb, offset+38 );
|
|
|
|
proto_tree_add_text( item_tree, tvb, offset+38, 1, "Product Name Length: %d", name_length );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Product Name */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_name,
|
|
|
|
tvb, offset+39, name_length, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Append product name to info column */
|
|
|
|
if(check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
{
|
|
|
|
col_append_fstr( pinfo->cinfo, COL_INFO, ", %s",
|
|
|
|
tvb_format_text(tvb, offset+39, name_length));
|
|
|
|
}
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* State */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_state,
|
|
|
|
tvb, offset+name_length+39, 1, TRUE );
|
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case SOCK_ADR_INFO_OT:
|
|
|
|
case SOCK_ADR_INFO_TO:
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket address struct - sin_family */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_sinfamily,
|
|
|
|
tvb, offset+6, 2, FALSE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket address struct - sin_port */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_sinport,
|
|
|
|
tvb, offset+8, 2, FALSE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket address struct - sin_address */
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_lir_sinaddr,
|
|
|
|
tvb, offset+10, 4, FALSE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Socket address struct - sin_zero */
|
|
|
|
proto_tree_add_item( item_tree, hf_enip_lir_sinzero,
|
|
|
|
tvb, offset+14, 8, FALSE );
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case SEQ_ADDRESS:
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_cpf_sai_connid,
|
|
|
|
tvb, offset+6, 4, TRUE );
|
|
|
|
|
|
|
|
proto_tree_add_item(item_tree, hf_enip_cpf_sai_seqnum,
|
|
|
|
tvb, offset+10, 4, TRUE );
|
|
|
|
|
|
|
|
/* Add info to column */
|
|
|
|
|
|
|
|
if(check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
{
|
|
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
col_add_fstr(pinfo->cinfo, COL_INFO,
|
|
|
|
"Connection: ID=0x%08X, SEQ=%010d",
|
|
|
|
tvb_get_letohl( tvb, offset+6 ),
|
|
|
|
tvb_get_letohl( tvb, offset+10 ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2004-06-02 06:30:15 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case LIST_SERVICES_RESP:
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Encapsulation version */
|
|
|
|
temp_data = tvb_get_letohs( tvb, offset+6 );
|
|
|
|
proto_tree_add_text( item_tree, tvb, offset+6, 2, "Encapsulation Version: %d", temp_data );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Capability flags */
|
|
|
|
temp_data = tvb_get_letohs( tvb, offset+8 );
|
|
|
|
temp_item = proto_tree_add_text(item_tree, tvb, offset+8, 2, "Capability Flags: 0x%04X", temp_data );
|
|
|
|
temp_tree = proto_item_add_subtree(temp_item, ett_lsrcf);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_tree_add_item(temp_tree, hf_enip_lsr_tcp,
|
|
|
|
tvb, offset+8, 2, TRUE );
|
|
|
|
proto_tree_add_item(temp_tree, hf_enip_lsr_udp,
|
|
|
|
tvb, offset+8, 2, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Name of service */
|
|
|
|
temp_item = proto_tree_add_text( item_tree, tvb, offset+10, 16, "Name of Service: %s",
|
|
|
|
tvb_format_stringzpad(tvb, offset+10, 16) );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Append service name to info column */
|
|
|
|
if(check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
{
|
|
|
|
col_append_fstr( pinfo->cinfo, COL_INFO, ", %s",
|
|
|
|
tvb_format_stringzpad(tvb, offset+10, 16) );
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
|
|
|
|
add_byte_array_text_to_proto_tree( item_tree, tvb, offset+6, item_length, "Data: " );
|
|
|
|
break;
|
|
|
|
|
|
|
|
} /* end of switch( item type ) */
|
|
|
|
|
|
|
|
} /* end of if( item length ) */
|
|
|
|
|
|
|
|
offset = offset + item_length + 4;
|
|
|
|
|
|
|
|
} /* end of while( item count ) */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
} /* end of dissect_cpf() */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
classify_packet(packet_info *pinfo)
|
|
|
|
{
|
2009-06-26 02:38:23 +00:00
|
|
|
/* see if nature of packets can be derived from src/dst ports */
|
|
|
|
/* if so, return as found */
|
|
|
|
if ( ( ENIP_ENCAP_PORT == pinfo->srcport && ENIP_ENCAP_PORT != pinfo->destport ) ||
|
|
|
|
( ENIP_ENCAP_PORT != pinfo->srcport && ENIP_ENCAP_PORT == pinfo->destport ) ) {
|
|
|
|
if ( ENIP_ENCAP_PORT == pinfo->srcport )
|
|
|
|
return ENIP_RESPONSE_PACKET;
|
|
|
|
else if ( ENIP_ENCAP_PORT == pinfo->destport )
|
|
|
|
return ENIP_REQUEST_PACKET;
|
|
|
|
}
|
|
|
|
/* else, cannot classify */
|
|
|
|
return ENIP_CANNOT_CLASSIFY;
|
2003-06-11 09:02:19 +00:00
|
|
|
}
|
|
|
|
|
2004-01-27 04:43:35 +00:00
|
|
|
static guint
|
2006-10-31 09:29:07 +00:00
|
|
|
get_enip_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
|
2004-01-27 04:43:35 +00:00
|
|
|
{
|
|
|
|
guint16 plen;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the length of the data from the encapsulation header.
|
|
|
|
*/
|
|
|
|
plen = tvb_get_letohs(tvb, offset + 2);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* That length doesn't include the encapsulation header itself;
|
|
|
|
* add that in.
|
|
|
|
*/
|
|
|
|
return plen + 24;
|
|
|
|
}
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
/* Code to actually dissect the packets */
|
2004-01-27 04:43:35 +00:00
|
|
|
static void
|
2004-09-23 17:34:35 +00:00
|
|
|
dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
2003-06-11 09:02:19 +00:00
|
|
|
{
|
2009-06-26 02:38:23 +00:00
|
|
|
int packet_type;
|
2003-10-01 21:51:59 +00:00
|
|
|
guint16 encap_cmd, encap_data_length;
|
2007-04-23 10:59:26 +00:00
|
|
|
const char *pkt_type_str = "";
|
2004-09-23 17:34:35 +00:00
|
|
|
guint32 ifacehndl;
|
2009-06-26 02:38:23 +00:00
|
|
|
enip_request_key_t request_key;
|
|
|
|
conversation_t *conversation;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
/* Set up structures needed to add the protocol subtree and manage it */
|
2003-10-01 21:51:59 +00:00
|
|
|
proto_item *ti, *encaph, *csf;
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_tree *enip_tree, *header_tree = NULL, *csftree;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2003-10-01 21:51:59 +00:00
|
|
|
/* Make entries in Protocol column and Info column on summary display */
|
|
|
|
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "ENIP");
|
2004-01-27 04:43:35 +00:00
|
|
|
if (check_col(pinfo->cinfo, COL_INFO))
|
|
|
|
col_clear(pinfo->cinfo, COL_INFO);
|
2003-10-01 21:51:59 +00:00
|
|
|
|
2004-01-27 04:43:35 +00:00
|
|
|
encap_cmd = tvb_get_letohs( tvb, 0 );
|
2009-06-26 02:38:23 +00:00
|
|
|
|
|
|
|
packet_type = classify_packet(pinfo);
|
2004-02-04 20:34:53 +00:00
|
|
|
|
|
|
|
if( check_col(pinfo->cinfo, COL_INFO) )
|
2003-10-01 21:51:59 +00:00
|
|
|
{
|
2003-06-11 09:02:19 +00:00
|
|
|
switch ( packet_type )
|
2003-10-01 21:51:59 +00:00
|
|
|
{
|
2009-06-26 02:38:23 +00:00
|
|
|
case ENIP_REQUEST_PACKET:
|
2005-10-11 23:38:57 +00:00
|
|
|
pkt_type_str="Req";
|
2003-06-11 09:02:19 +00:00
|
|
|
break;
|
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case ENIP_RESPONSE_PACKET:
|
2005-10-11 23:38:57 +00:00
|
|
|
pkt_type_str="Rsp";
|
2003-06-11 09:02:19 +00:00
|
|
|
break;
|
|
|
|
|
2003-10-01 21:51:59 +00:00
|
|
|
default:
|
2005-10-11 23:38:57 +00:00
|
|
|
pkt_type_str="?";
|
2003-06-11 09:02:19 +00:00
|
|
|
}
|
|
|
|
|
2004-02-04 20:34:53 +00:00
|
|
|
/* Add service and request/response to info column */
|
2003-10-01 21:51:59 +00:00
|
|
|
col_add_fstr(pinfo->cinfo, COL_INFO,
|
2009-06-26 02:38:23 +00:00
|
|
|
"%s (%s)",
|
2008-10-23 21:01:04 +00:00
|
|
|
val_to_str(encap_cmd, encap_cmd_vals, "Unknown (0x%04x)"),
|
|
|
|
pkt_type_str );
|
2004-02-04 20:34:53 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
|
2003-10-01 21:51:59 +00:00
|
|
|
} /* end of if( col exists ) */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We need to track some state for this protocol on a per conversation
|
|
|
|
* basis so we can do neat things like request/response tracking
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* Do we have a conversation for this connection?
|
|
|
|
*/
|
|
|
|
conversation = find_conversation(pinfo->fd->num,
|
|
|
|
&pinfo->src, &pinfo->dst,
|
|
|
|
pinfo->ptype,
|
|
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
|
|
if (conversation == NULL) {
|
|
|
|
/* We don't yet have a conversation, so create one. */
|
|
|
|
conversation = conversation_new(pinfo->fd->num,
|
|
|
|
&pinfo->src, &pinfo->dst,
|
|
|
|
pinfo->ptype,
|
|
|
|
pinfo->srcport, pinfo->destport, 0);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* No. Attach that information to the conversation, and add
|
|
|
|
* it to the list of information structures later before dissection.
|
|
|
|
*/
|
|
|
|
memset( &request_key, 0, sizeof(enip_request_key_t) );
|
|
|
|
request_key.requesttype = packet_type;
|
|
|
|
request_key.type = EPDT_UNKNOWN;
|
|
|
|
request_key.session_handle = tvb_get_letohl( tvb, 4 );
|
|
|
|
request_key.sender_context = tvb_get_letoh64( tvb, 12 );
|
|
|
|
request_key.conversation = conversation->index;
|
|
|
|
|
|
|
|
encap_data_length = tvb_get_letohs( tvb, 2 );
|
|
|
|
enip_tree = NULL;
|
2003-06-11 09:02:19 +00:00
|
|
|
/* In the interest of speed, if "tree" is NULL, don't do any work not
|
2003-10-01 21:51:59 +00:00
|
|
|
necessary to generate protocol tree items. */
|
|
|
|
if (tree) {
|
2003-06-11 09:02:19 +00:00
|
|
|
/* create display subtree for the protocol */
|
2004-09-23 17:34:35 +00:00
|
|
|
ti = proto_tree_add_item(tree, proto_enip, tvb, 0, -1, FALSE);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
enip_tree = proto_item_add_subtree(ti, ett_enip);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
/* Add encapsulation header tree */
|
2004-09-23 17:34:35 +00:00
|
|
|
encaph = proto_tree_add_text( enip_tree, tvb, 0, 24, "Encapsulation Header");
|
|
|
|
header_tree = proto_item_add_subtree(encaph, ett_enip);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
/* Add EtherNet/IP encapsulation header */
|
|
|
|
proto_tree_add_item( header_tree, hf_enip_command, tvb, 0, 2, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
encap_data_length = tvb_get_letohs( tvb, 2 );
|
2004-09-23 17:34:35 +00:00
|
|
|
proto_tree_add_text( header_tree, tvb, 2, 2, "Length: %u", encap_data_length );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
proto_tree_add_item( header_tree, hf_enip_session, tvb, 4, 4, TRUE );
|
|
|
|
proto_tree_add_item( header_tree, hf_enip_status, tvb, 8, 4, TRUE );
|
|
|
|
proto_tree_add_item( header_tree, hf_enip_sendercontex, tvb, 12, 8, TRUE );
|
|
|
|
proto_tree_add_item( header_tree, hf_enip_options, tvb, 20, 4, TRUE );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
/* Append session and command to the protocol tree */
|
|
|
|
proto_item_append_text( ti, ", Session: 0x%08X, %s", tvb_get_letohl( tvb, 4 ),
|
|
|
|
val_to_str( encap_cmd, encap_cmd_vals, "Unknown (0x%04x)" ) );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/*
|
|
|
|
** For some commands we want to add some info to the info column
|
|
|
|
*/
|
2004-02-04 20:34:53 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
if( check_col( pinfo->cinfo, COL_INFO ) )
|
2009-06-19 06:15:52 +00:00
|
|
|
{
|
2004-02-04 20:34:53 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
switch( encap_cmd )
|
|
|
|
{
|
|
|
|
case REGISTER_SESSION:
|
|
|
|
case UNREGISTER_SESSION:
|
|
|
|
col_append_fstr( pinfo->cinfo, COL_INFO, ", Session: 0x%08X",
|
|
|
|
tvb_get_letohl( tvb, 4 ) );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
} /* end of switch() */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
} /* end of id info column */
|
|
|
|
} /* end of tree */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Command specific data - create tree */
|
|
|
|
if( encap_data_length )
|
|
|
|
{
|
|
|
|
/* The packet have some command specific data, buid a sub tree for it */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
csf = proto_tree_add_text( enip_tree, tvb, 24, encap_data_length,
|
|
|
|
"Command Specific Data");
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
csftree = proto_item_add_subtree(csf, ett_command_tree);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
switch( encap_cmd )
|
|
|
|
{
|
|
|
|
case NOP:
|
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case LIST_SERVICES:
|
|
|
|
dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 );
|
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case LIST_IDENTITY:
|
|
|
|
dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 );
|
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case LIST_INTERFACES:
|
|
|
|
dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 );
|
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case REGISTER_SESSION:
|
|
|
|
proto_tree_add_text( csftree, tvb, 24, 2, "Protocol Version: 0x%04X",
|
|
|
|
tvb_get_letohs( tvb, 24 ) );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_tree_add_text( csftree, tvb, 26, 2, "Option Flags: 0x%04X",
|
|
|
|
tvb_get_letohs( tvb, 26 ) );
|
2004-09-23 17:34:35 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
break;
|
2004-09-23 17:34:35 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case UNREGISTER_SESSION:
|
|
|
|
break;
|
2004-09-23 17:34:35 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case SEND_RR_DATA:
|
|
|
|
proto_tree_add_item(csftree, hf_enip_srrd_ifacehnd, tvb, 24, 4, TRUE);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u",
|
|
|
|
tvb_get_letohs( tvb, 28 ) );
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
ifacehndl = tvb_get_letohl( tvb, 24 );
|
|
|
|
dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 30, ifacehndl );
|
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case SEND_UNIT_DATA:
|
|
|
|
proto_tree_add_item(csftree, hf_enip_sud_ifacehnd, tvb, 24, 4, TRUE);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u",
|
|
|
|
tvb_get_letohs( tvb, 28 ) );
|
2009-06-19 05:45:40 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
ifacehndl = tvb_get_letohl( tvb, 24 );
|
|
|
|
dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 30, ifacehndl );
|
|
|
|
break;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
case INDICATE_STATUS:
|
|
|
|
case CANCEL:
|
|
|
|
default:
|
|
|
|
|
|
|
|
/* Can not decode - Just show the data */
|
|
|
|
add_byte_array_text_to_proto_tree( header_tree, tvb, 24, encap_data_length, "Encap Data: " );
|
|
|
|
break;
|
2009-06-19 06:15:52 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
} /* end of switch() */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
} /* end of if( encapsulated data ) */
|
2004-09-23 17:34:35 +00:00
|
|
|
} /* end of dissect_enip_pdu() */
|
2004-01-27 04:43:35 +00:00
|
|
|
|
|
|
|
static int
|
2004-09-23 17:34:35 +00:00
|
|
|
dissect_enip_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
2004-01-27 04:43:35 +00:00
|
|
|
{
|
|
|
|
guint16 encap_cmd;
|
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
g_tree = tree;
|
|
|
|
|
2004-01-27 04:43:35 +00:00
|
|
|
/* An ENIP packet is at least 4 bytes long - we need the command type. */
|
2009-06-26 02:38:23 +00:00
|
|
|
if (!tvb_bytes_exist(tvb, 0, 4))
|
2004-01-27 04:43:35 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Get the command type and see if it's valid. */
|
|
|
|
encap_cmd = tvb_get_letohs( tvb, 0 );
|
|
|
|
if (match_strval(encap_cmd, encap_cmd_vals) == NULL)
|
2009-06-26 02:38:23 +00:00
|
|
|
return 0; /* not a known command */
|
2003-08-16 01:53:41 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
dissect_enip_pdu(tvb, pinfo, tree);
|
2003-10-01 21:51:59 +00:00
|
|
|
return tvb_length(tvb);
|
2004-01-27 04:43:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2004-09-23 17:34:35 +00:00
|
|
|
dissect_enip_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
2004-01-27 04:43:35 +00:00
|
|
|
{
|
|
|
|
guint16 encap_cmd;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
g_tree = tree;
|
|
|
|
|
2004-01-27 04:43:35 +00:00
|
|
|
/* An ENIP packet is at least 4 bytes long - we need the command type. */
|
2009-06-26 02:38:23 +00:00
|
|
|
if (!tvb_bytes_exist(tvb, 0, 4))
|
2004-01-27 04:43:35 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Get the command type and see if it's valid. */
|
|
|
|
encap_cmd = tvb_get_letohs( tvb, 0 );
|
|
|
|
if (match_strval(encap_cmd, encap_cmd_vals) == NULL)
|
2009-06-26 02:38:23 +00:00
|
|
|
return 0; /* not a known command */
|
2004-01-27 04:43:35 +00:00
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
tcp_dissect_pdus(tvb, pinfo, tree, enip_desegment, 4,
|
2009-06-26 02:38:23 +00:00
|
|
|
get_enip_pdu_len, dissect_enip_pdu);
|
2004-01-27 04:43:35 +00:00
|
|
|
return tvb_length(tvb);
|
|
|
|
}
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
/* Code to actually dissect the io packets*/
|
2009-06-26 02:38:23 +00:00
|
|
|
static void
|
2003-06-11 09:02:19 +00:00
|
|
|
dissect_enipio(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
|
|
{
|
|
|
|
/* Set up structures needed to add the protocol subtree and manage it */
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_item *ti;
|
|
|
|
proto_tree *enip_tree;
|
2009-06-19 06:15:52 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
g_tree = tree;
|
2007-12-11 04:33:08 +00:00
|
|
|
|
2003-06-11 09:02:19 +00:00
|
|
|
/* Make entries in Protocol column and Info column on summary display */
|
2009-06-26 02:38:23 +00:00
|
|
|
if (check_col(pinfo->cinfo, COL_PROTOCOL))
|
|
|
|
col_set_str(pinfo->cinfo, COL_PROTOCOL, "ENIP");
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
/* In the interest of speed, if "tree" is NULL, don't do any work not
|
|
|
|
necessary to generate protocol tree items. */
|
2009-06-26 02:38:23 +00:00
|
|
|
if (tree)
|
|
|
|
{
|
|
|
|
/* create display subtree for the protocol */
|
|
|
|
ti = proto_tree_add_item(tree, proto_enip, tvb, 0, -1, FALSE);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
enip_tree = proto_item_add_subtree(ti, ett_enip);
|
2003-08-16 01:53:41 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
dissect_cpf( NULL, 0xFFFF, tvb, pinfo, enip_tree, 0, 0 );
|
|
|
|
}
|
2008-10-23 21:01:04 +00:00
|
|
|
|
2003-08-16 01:53:41 +00:00
|
|
|
} /* end of dissect_enipio() */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
|
2009-07-09 20:44:28 +00:00
|
|
|
static gboolean
|
|
|
|
dissect_dlr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
|
|
|
|
{
|
|
|
|
proto_item *ti;
|
|
|
|
proto_tree *dlr_tree = NULL;
|
|
|
|
guint8 dlr_subtype;
|
|
|
|
guint8 dlr_protover;
|
|
|
|
guint8 dlr_frametype;
|
|
|
|
|
|
|
|
/* Make entries in Protocol column and Info column on summary display */
|
|
|
|
if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
|
|
|
|
col_set_str( pinfo->cinfo, COL_PROTOCOL, "DLR" );
|
|
|
|
|
|
|
|
if (check_col( pinfo->cinfo, COL_INFO ) )
|
|
|
|
col_clear( pinfo->cinfo, COL_INFO );
|
|
|
|
|
|
|
|
if( tree )
|
|
|
|
{
|
|
|
|
/* Create display subtree for the protocol */
|
|
|
|
ti = proto_tree_add_item(tree, proto_dlr, tvb, 0, -1, FALSE);
|
|
|
|
dlr_tree = proto_item_add_subtree( ti, ett_dlr );
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get values for the Common Frame Header Format */
|
|
|
|
dlr_subtype = tvb_get_guint8(tvb, DLR_CFH_SUB_TYPE);
|
|
|
|
dlr_protover = tvb_get_guint8(tvb, DLR_CFH_PROTO_VERSION);
|
|
|
|
|
|
|
|
/* Dissect the Common Frame Header Format */
|
|
|
|
proto_tree_add_uint( dlr_tree, hf_dlr_ringsubtype, tvb, DLR_CFH_SUB_TYPE, 1, dlr_subtype );
|
|
|
|
proto_tree_add_uint( dlr_tree, hf_dlr_ringprotoversion, tvb, DLR_CFH_PROTO_VERSION, 1, dlr_protover );
|
|
|
|
|
|
|
|
/* Get values for the DLR Message Payload Fields */
|
|
|
|
dlr_frametype = tvb_get_guint8(tvb, DLR_MPF_FRAME_TYPE);
|
|
|
|
|
|
|
|
/* Dissect the DLR Message Payload Fields */
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_frametype, tvb, DLR_MPF_FRAME_TYPE, 1, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_sourceport, tvb, DLR_MPF_SOURCE_PORT, 1, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_sourceip, tvb, DLR_MPF_SOURCE_IP, 4, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_sequenceid, tvb, DLR_MPF_SEQUENCE_ID, 4, FALSE );
|
|
|
|
|
|
|
|
/* Add frame type to col info */
|
|
|
|
if( check_col(pinfo->cinfo, COL_INFO) )
|
|
|
|
{
|
|
|
|
col_add_fstr(pinfo->cinfo, COL_INFO,
|
|
|
|
"%s", val_to_str(dlr_frametype, dlr_frame_type_vals, "Unknown (0x%04x)") );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( dlr_frametype == DLR_FT_BEACON )
|
|
|
|
{
|
|
|
|
/* Beacon */
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_ringstate, tvb, DLR_BE_RING_STATE, 1, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_supervisorprecedence, tvb, DLR_BE_SUPERVISOR_PRECEDENCE, 1, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_beaconinterval, tvb, DLR_BE_BEACON_INTERVAL, 4, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_beacontimeout, tvb, DLR_BE_BEACON_TIMEOUT, 4, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_beaconreserved, tvb, DLR_BE_RESERVED, 20, FALSE );
|
|
|
|
}
|
|
|
|
else if( dlr_frametype == DLR_FT_NEIGHBOR_REQ )
|
|
|
|
{
|
|
|
|
/* Neighbor_Check_Request */
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_nreqreserved, tvb, DLR_NREQ_RESERVED, 30, FALSE );
|
|
|
|
}
|
|
|
|
else if( dlr_frametype == DLR_FT_NEIGHBOR_RES )
|
|
|
|
{
|
|
|
|
/* Neighbor_Check_Response */
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_nressourceport, tvb, DLR_NRES_SOURCE_PORT, 1, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_nresreserved, tvb, DLR_NRES_RESERVED, 29, FALSE );
|
|
|
|
}
|
|
|
|
else if( dlr_frametype == DLR_FT_LINK_STAT )
|
|
|
|
{
|
|
|
|
/* Link_Status/Neighbor_Status */
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_lnknbrstatus, tvb, DLR_LNS_SOURCE_PORT, 1, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_lnknbrreserved, tvb, DLR_LNS_RESERVED, 29, FALSE );
|
|
|
|
}
|
|
|
|
else if( dlr_frametype == DLR_FT_LOCATE_FLT )
|
|
|
|
{
|
|
|
|
/* Locate_Fault */
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_lfreserved, tvb, DLR_LF_RESERVED, 30, FALSE );
|
|
|
|
}
|
|
|
|
else if( dlr_frametype == DLR_FT_ANNOUNCE )
|
|
|
|
{
|
|
|
|
/* Announce */
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_ringstate, tvb, DLR_AN_RING_STATE, 1, FALSE );
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_anreserved, tvb, DLR_AN_RESERVED, 29, FALSE );
|
|
|
|
}
|
|
|
|
else if( dlr_frametype == DLR_FT_SIGN_ON )
|
|
|
|
{
|
|
|
|
guint16 nCnt;
|
|
|
|
guint16 nNumNodes;
|
|
|
|
guint16 nOffset;
|
|
|
|
|
|
|
|
|
|
|
|
/* Sign_On */
|
|
|
|
nNumNodes = tvb_get_ntohs(tvb, DLR_SO_NUM_NODES);
|
|
|
|
|
|
|
|
proto_tree_add_uint( dlr_tree, hf_dlr_sonumnodes, tvb, DLR_SO_NUM_NODES, 2, nNumNodes );
|
|
|
|
|
|
|
|
/* Add each node in the list */
|
|
|
|
for( nCnt = 0, nOffset = DLR_SO_NODE_1_MAC; nCnt < nNumNodes; nCnt++ )
|
|
|
|
{
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_somac, tvb, nOffset, 6, FALSE );
|
|
|
|
nOffset += 6;
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_soip, tvb, nOffset, 4, FALSE );
|
|
|
|
nOffset += 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
if( nOffset < 42 )
|
|
|
|
{
|
|
|
|
proto_tree_add_item( dlr_tree, hf_dlr_soreserved, tvb, nOffset, 42 - nOffset, FALSE );
|
|
|
|
nOffset += (42 - nOffset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Unknown Frame type */
|
|
|
|
}
|
|
|
|
|
|
|
|
return tvb_length(tvb);
|
|
|
|
|
|
|
|
} /* end of dissect_dlr() */
|
|
|
|
|
|
|
|
|
2006-05-21 04:49:01 +00:00
|
|
|
/* Register the protocol with Wireshark */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
/* this format is require because a script is used to build the C function
|
|
|
|
that calls all the protocol registration.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void
|
2004-09-23 17:34:35 +00:00
|
|
|
proto_register_enip(void)
|
2003-06-11 09:02:19 +00:00
|
|
|
{
|
2004-09-23 17:34:35 +00:00
|
|
|
/* Setup list of header fields */
|
2009-06-26 02:38:23 +00:00
|
|
|
static hf_register_info hf[] = {
|
|
|
|
{ &hf_enip_command,
|
|
|
|
{ "Command", "enip.command",
|
|
|
|
FT_UINT16, BASE_HEX, VALS(encap_cmd_vals), 0,
|
|
|
|
"Encapsulation command", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_session,
|
|
|
|
{ "Session Handle", "enip.session",
|
|
|
|
FT_UINT32, BASE_HEX, NULL, 0,
|
|
|
|
"Session identification", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_status,
|
|
|
|
{ "Status", "enip.status",
|
|
|
|
FT_UINT32, BASE_HEX, VALS(encap_status_vals), 0,
|
|
|
|
"Status code", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_sendercontex,
|
|
|
|
{ "Sender Context", "enip.context",
|
|
|
|
FT_BYTES, BASE_NONE, NULL, 0,
|
|
|
|
"Information pertient to the sender", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_options,
|
|
|
|
{ "Options", "enip.options",
|
|
|
|
FT_UINT32, BASE_HEX, NULL, 0,
|
|
|
|
"Options flags", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_lsr_tcp,
|
|
|
|
{ "Supports CIP Encapsulation via TCP", "enip.lsr.capaflags.tcp",
|
|
|
|
FT_UINT16, BASE_DEC, VALS(enip_true_false_vals), 0x0020,
|
|
|
|
"ListServices Reply: Supports CIP Encapsulation via TCP", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_lsr_udp,
|
|
|
|
{ "Supports CIP Class 0 or 1 via UDP", "enip.lsr.capaflags.udp",
|
|
|
|
FT_UINT16, BASE_DEC, VALS(enip_true_false_vals), 0x0100,
|
|
|
|
"ListServices Reply: Supports CIP Class 0 or 1 via UDP", HFILL }
|
|
|
|
},
|
|
|
|
/* Send Request/Reply Data */
|
|
|
|
{ &hf_enip_srrd_ifacehnd,
|
|
|
|
{ "Interface Handle", "enip.srrd.iface",
|
|
|
|
FT_UINT32, BASE_HEX, VALS(enip_interface_handle_vals), 0,
|
|
|
|
"SendRRData: Interface handle", HFILL }
|
|
|
|
},
|
|
|
|
/* Send Unit Data */
|
|
|
|
{ &hf_enip_sud_ifacehnd,
|
|
|
|
{ "Interface Handle", "enip.sud.iface",
|
|
|
|
FT_UINT32, BASE_HEX, VALS(enip_interface_handle_vals), 0,
|
|
|
|
"SendUnitData: Interface handle", HFILL }
|
|
|
|
},
|
|
|
|
/* List identity reply */
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_sinfamily,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "sin_family", "enip.lir.sa.sinfamily",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0,
|
|
|
|
"ListIdentity Reply: Socket Address.Sin Family", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_sinport,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "sin_port", "enip.lir.sa.sinport",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0,
|
|
|
|
"ListIdentity Reply: Socket Address.Sin Port", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_sinaddr,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "sin_addr", "enip.lir.sa.sinaddr",
|
|
|
|
FT_IPv4, BASE_NONE, NULL, 0,
|
|
|
|
"ListIdentity Reply: Socket Address.Sin Addr", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_sinzero,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "sin_zero", "enip.lir.sa.sinzero",
|
|
|
|
FT_BYTES, BASE_NONE, NULL, 0,
|
|
|
|
"ListIdentity Reply: Socket Address.Sin Zero", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_lir_vendor,
|
|
|
|
{ "Vendor ID", "enip.lir.vendor",
|
|
|
|
FT_UINT16, BASE_HEX, VALS(cip_vendor_vals), 0,
|
|
|
|
"ListIdentity Reply: Vendor ID", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_devtype,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "Device Type", "enip.lir.devtype",
|
|
|
|
FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0,
|
|
|
|
"ListIdentity Reply: Device Type", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_prodcode,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "Product Code", "enip.lir.prodcode",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0,
|
|
|
|
"ListIdentity Reply: Product Code", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_status,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "Status", "enip.lir.status",
|
|
|
|
FT_UINT16, BASE_HEX, NULL, 0,
|
|
|
|
"ListIdentity Reply: Status", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_serial,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "Serial Number", "enip.lir.serial",
|
|
|
|
FT_UINT32, BASE_HEX, NULL, 0,
|
|
|
|
"ListIdentity Reply: Serial Number", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_name,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "Product Name", "enip.lir.name",
|
|
|
|
FT_STRING, BASE_NONE, NULL, 0,
|
|
|
|
"ListIdentity Reply: Product Name", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_lir_state,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "State", "enip.lir.state",
|
|
|
|
FT_UINT8, BASE_HEX, NULL, 0,
|
|
|
|
"ListIdentity Reply: State", HFILL }
|
|
|
|
},
|
|
|
|
/* Common Packet Format */
|
|
|
|
{ &hf_enip_cpf_typeid,
|
|
|
|
{ "Type ID", "enip.cpf.typeid",
|
|
|
|
FT_UINT16, BASE_HEX, VALS(cdf_type_vals), 0,
|
|
|
|
"Common Packet Format: Type of encapsulated item", HFILL }
|
|
|
|
},
|
|
|
|
/* Sequenced Address Type */
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_cpf_sai_connid,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "Connection ID", "enip.cpf.sai.connid",
|
|
|
|
FT_UINT32, BASE_HEX, NULL, 0,
|
|
|
|
"Common Packet Format: Sequenced Address Item, Connection Identifier", HFILL }
|
|
|
|
},
|
2004-09-23 17:34:35 +00:00
|
|
|
{ &hf_enip_cpf_sai_seqnum,
|
2009-06-26 02:38:23 +00:00
|
|
|
{ "Sequence Number", "enip.cpf.sai.seq",
|
|
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
|
|
"Common Packet Format: Sequenced Address Item, Sequence Number", HFILL }
|
|
|
|
},
|
|
|
|
/* Request/Response Matching */
|
|
|
|
{ &hf_enip_response_in,
|
|
|
|
{ "Response In", "enip.response_in",
|
2009-07-07 09:02:59 +00:00
|
|
|
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
|
2009-06-26 02:38:23 +00:00
|
|
|
"The response to this ENIP request is in this frame", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_response_to,
|
|
|
|
{ "Request In", "enip.response_to",
|
2009-07-07 09:02:59 +00:00
|
|
|
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
|
2009-06-26 02:38:23 +00:00
|
|
|
"This is a response to the ENIP request in this frame", HFILL }
|
|
|
|
},
|
|
|
|
{ &hf_enip_time,
|
|
|
|
{ "Time", "enip.time",
|
|
|
|
FT_RELATIVE_TIME, BASE_NONE, NULL, 0x0,
|
|
|
|
"The time between the Call and the Reply", HFILL }
|
|
|
|
}
|
2003-06-11 09:02:19 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* Setup protocol subtree array */
|
2009-06-26 02:38:23 +00:00
|
|
|
static gint *ett[] = {
|
|
|
|
&ett_enip,
|
|
|
|
&ett_count_tree,
|
|
|
|
&ett_type_tree,
|
|
|
|
&ett_command_tree,
|
|
|
|
&ett_sockadd,
|
|
|
|
&ett_lsrcf,
|
|
|
|
};
|
2009-07-09 20:44:28 +00:00
|
|
|
|
|
|
|
/* Setup list of header fields for DLR See Section 1.6.1 for details*/
|
|
|
|
static hf_register_info hfdlr[] = {
|
|
|
|
/* Ring Sub-type */
|
|
|
|
{ &hf_dlr_ringsubtype,
|
|
|
|
{ "Subtype", "enip.dlr.ringsubtype",
|
|
|
|
FT_UINT8, BASE_HEX, NULL, 0,
|
|
|
|
"Ring Sub-Type", HFILL }
|
|
|
|
},
|
|
|
|
/* Ring Protocol Version */
|
|
|
|
{ &hf_dlr_ringprotoversion,
|
|
|
|
{ "Version", "enip.dlr.protversion",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0,
|
|
|
|
"Ring Protocol Version", HFILL }
|
|
|
|
},
|
|
|
|
/* Frame Type */
|
|
|
|
{ &hf_dlr_frametype,
|
|
|
|
{ "Frametype", "enip.dlr.frametype",
|
|
|
|
FT_UINT8, BASE_HEX, VALS(dlr_frame_type_vals), 0,
|
|
|
|
"Frame Type", HFILL }
|
|
|
|
},
|
|
|
|
/* Source Port */
|
|
|
|
{ &hf_dlr_sourceport,
|
|
|
|
{ "Sourceport", "enip.dlr.sourceport",
|
|
|
|
FT_UINT8, BASE_HEX, VALS(dlr_source_port_vals), 0,
|
|
|
|
"Source Port", HFILL }
|
|
|
|
},
|
|
|
|
/* Source IP Address */
|
|
|
|
{ &hf_dlr_sourceip,
|
|
|
|
{ "Source IP", "enip.dlr.sourceip",
|
|
|
|
FT_IPv4, BASE_HEX, NULL, 0,
|
|
|
|
"Source IP Address", HFILL }
|
|
|
|
},
|
|
|
|
/* Sequence ID*/
|
|
|
|
{ &hf_dlr_sequenceid,
|
|
|
|
{ "Sequence Id", "enip.dlr.seqid",
|
|
|
|
FT_UINT32, BASE_HEX, NULL, 0,
|
|
|
|
NULL, HFILL }
|
|
|
|
},
|
|
|
|
/* Ring State */
|
|
|
|
{ &hf_dlr_ringstate,
|
|
|
|
{ "Ring State", "enip.dlr.state",
|
|
|
|
FT_UINT8, BASE_HEX, VALS(dlr_ring_state_vals), 0,
|
|
|
|
NULL, HFILL }
|
|
|
|
},
|
|
|
|
/* Supervisor Precedence */
|
|
|
|
{ &hf_dlr_supervisorprecedence,
|
|
|
|
{ "Supervisor Precedence", "enip.dlr.supervisorprecedence",
|
|
|
|
FT_UINT8, BASE_DEC, NULL, 0,
|
|
|
|
NULL, HFILL }
|
|
|
|
},
|
|
|
|
/* Beacon Interval */
|
|
|
|
{ &hf_dlr_beaconinterval,
|
|
|
|
{ "Beacon Interval", "enip.dlr.beaconinterval",
|
|
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
|
|
NULL, HFILL }
|
|
|
|
},
|
|
|
|
/* Beacon Timeout */
|
|
|
|
{ &hf_dlr_beacontimeout,
|
|
|
|
{ "Beacon Timeout", "enip.dlr.beacontimeout",
|
|
|
|
FT_UINT32, BASE_DEC, NULL, 0,
|
|
|
|
NULL, HFILL }
|
|
|
|
},
|
|
|
|
/* Beacon Reserved */
|
|
|
|
{ &hf_dlr_beaconreserved,
|
|
|
|
{ "Reserved", "enip.dlr.beaconreserved",
|
|
|
|
FT_BYTES, BASE_HEX, NULL, 0,
|
|
|
|
"Beacon Reserved", HFILL }
|
|
|
|
},
|
|
|
|
/* Neighbor_Check_Request Reserved */
|
|
|
|
{ &hf_dlr_nreqreserved,
|
|
|
|
{ "Reserved", "enip.dlr.nreqreserved",
|
|
|
|
FT_BYTES, BASE_HEX, NULL, 0,
|
|
|
|
"Neighbor_Check_Request Reserved", HFILL }
|
|
|
|
},
|
|
|
|
/* Neighbor_Check_Response Source Port */
|
|
|
|
{ &hf_dlr_nressourceport,
|
|
|
|
{ "Sourceport", "enip.dlr.nressourceport",
|
|
|
|
FT_UINT8, BASE_HEX, VALS(dlr_source_port_vals), 0,
|
|
|
|
"Neighbor_Check_Response Source Port", HFILL }
|
|
|
|
},
|
|
|
|
/* Neighbor_Check_Response Reserved */
|
|
|
|
{ &hf_dlr_nresreserved,
|
|
|
|
{ "Reserved", "enip.dlr.nresreserved",
|
|
|
|
FT_BYTES, BASE_HEX, NULL, 0,
|
|
|
|
"Neighbor_Check_Response Reserved", HFILL }
|
|
|
|
},
|
|
|
|
/* Link_Status/Neighbor_Status Status */
|
|
|
|
{ &hf_dlr_lnknbrstatus,
|
|
|
|
{ "Status", "enip.dlr.lnknbrstatus",
|
|
|
|
FT_UINT8, BASE_HEX, VALS(dlr_lnk_nbr_status_vals), 0,
|
|
|
|
"Link_Status/Neighbor_Status Status", HFILL }
|
|
|
|
},
|
|
|
|
/* Link_Status/Neighbor_Status Reserved */
|
|
|
|
{ &hf_dlr_lnknbrreserved,
|
|
|
|
{ "Reserved", "enip.dlr.lnknbrreserved",
|
|
|
|
FT_BYTES, BASE_HEX, NULL, 0,
|
|
|
|
"Link_Status/Neighbor_Status Reserved", HFILL }
|
|
|
|
},
|
|
|
|
/* Locate_Fault Reserved */
|
|
|
|
{ &hf_dlr_lfreserved,
|
|
|
|
{ "Reserved", "enip.dlr.lfreserved",
|
|
|
|
FT_BYTES, BASE_HEX, NULL, 0,
|
|
|
|
"Locate_Fault Reserved", HFILL }
|
|
|
|
},
|
|
|
|
/* Announce Reserved */
|
|
|
|
{ &hf_dlr_anreserved,
|
|
|
|
{ "Reserved", "enip.dlr.anreserved",
|
|
|
|
FT_BYTES, BASE_HEX, NULL, 0,
|
|
|
|
"Announce Reserved", HFILL }
|
|
|
|
},
|
|
|
|
/* Number of Nodes in List */
|
|
|
|
{ &hf_dlr_sonumnodes,
|
|
|
|
{ "Num nodes", "enip.dlr.sonumnodes",
|
|
|
|
FT_UINT16, BASE_DEC, NULL, 0,
|
|
|
|
"Number of Nodes in List", HFILL }
|
|
|
|
},
|
|
|
|
/* Sign_On Node # MAC Address */
|
|
|
|
{ &hf_dlr_somac,
|
|
|
|
{ "MAC Address", "enip.dlr.somac",
|
|
|
|
FT_ETHER, BASE_HEX, NULL, 0,
|
|
|
|
"Sign_On Node MAC Address", HFILL }
|
|
|
|
},
|
|
|
|
/* Node # IP Address */
|
|
|
|
{ &hf_dlr_soip,
|
|
|
|
{ "IP Address", "enip.dlr.soip",
|
|
|
|
FT_IPv4, BASE_HEX, NULL, 0,
|
|
|
|
"Sign_On Node IP Address", HFILL }
|
|
|
|
},
|
|
|
|
/* Sign_On Reserved */
|
|
|
|
{ &hf_dlr_soreserved,
|
|
|
|
{ "Reserved", "enip.dlr.soreserved",
|
|
|
|
FT_BYTES, BASE_HEX, NULL, 0,
|
|
|
|
"Sign_On Reserved", HFILL }
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/* Setup protocol subtree array for DLR */
|
|
|
|
static gint *ettdlr[] = {
|
|
|
|
&ett_dlr
|
|
|
|
};
|
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
module_t *enip_module;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
/* Register the protocol name and description */
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_enip = proto_register_protocol("EtherNet/IP (Industrial Protocol)",
|
|
|
|
"ENIP", "enip");
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
/* Required function calls to register the header fields and subtrees used */
|
2009-06-26 02:38:23 +00:00
|
|
|
proto_register_field_array(proto_enip, hf, array_length(hf));
|
|
|
|
proto_register_subtree_array(ett, array_length(ett));
|
2003-08-16 01:53:41 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
enip_module = prefs_register_protocol(proto_enip, NULL);
|
|
|
|
prefs_register_bool_preference(enip_module, "desegment",
|
|
|
|
"Desegment all EtherNet/IP messages spanning multiple TCP segments",
|
|
|
|
"Whether the EtherNet/IP dissector should desegment all messages spanning multiple TCP segments",
|
|
|
|
&enip_desegment);
|
2004-09-23 17:34:35 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
subdissector_sud_table = register_dissector_table("enip.sud.iface",
|
|
|
|
"SendUnitData.Interface Handle", FT_UINT32, BASE_HEX);
|
2004-09-23 17:34:35 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
subdissector_srrd_table = register_dissector_table("enip.srrd.iface",
|
|
|
|
"SendRequestReplyData.Interface Handle", FT_UINT32, BASE_HEX);
|
2004-09-23 17:34:35 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
register_init_routine(&enip_init_protocol);
|
2009-07-09 20:44:28 +00:00
|
|
|
|
|
|
|
/* Register the protocol name and description */
|
|
|
|
proto_dlr = proto_register_protocol("Device Level Ring", "DLR", "dlr");
|
|
|
|
|
|
|
|
/* Required function calls to register the header fields and subtrees used */
|
|
|
|
proto_register_field_array(proto_dlr, hfdlr, array_length(hfdlr));
|
|
|
|
proto_register_subtree_array(ettdlr, array_length(ettdlr));
|
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
} /* end of proto_register_enip() */
|
2003-06-11 09:02:19 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* If this dissector uses sub-dissector registration add a registration routine.
|
|
|
|
This format is required because a script is used to find these routines and
|
|
|
|
create the code that calls these routines.
|
|
|
|
*/
|
|
|
|
void
|
2004-09-23 17:34:35 +00:00
|
|
|
proto_reg_handoff_enip(void)
|
2003-06-11 09:02:19 +00:00
|
|
|
{
|
2009-06-26 02:38:23 +00:00
|
|
|
dissector_handle_t enip_udp_handle, enip_tcp_handle;
|
|
|
|
dissector_handle_t enipio_handle;
|
2009-07-09 20:44:28 +00:00
|
|
|
dissector_handle_t dlr_handle;
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Register for EtherNet/IP, using TCP */
|
|
|
|
enip_tcp_handle = new_create_dissector_handle(dissect_enip_tcp, proto_enip);
|
|
|
|
dissector_add("tcp.port", ENIP_ENCAP_PORT, enip_tcp_handle);
|
2004-09-23 17:34:35 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Register for EtherNet/IP, using UDP */
|
|
|
|
enip_udp_handle = new_create_dissector_handle(dissect_enip_udp, proto_enip);
|
|
|
|
dissector_add("udp.port", ENIP_ENCAP_PORT, enip_udp_handle);
|
2003-06-11 09:02:19 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Register for EtherNet/IP IO data (UDP) */
|
|
|
|
enipio_handle = create_dissector_handle(dissect_enipio, proto_enip);
|
|
|
|
dissector_add("udp.port", ENIP_IO_PORT, enipio_handle);
|
2003-08-16 01:53:41 +00:00
|
|
|
|
2009-06-26 02:38:23 +00:00
|
|
|
/* Find dissector for data packet */
|
|
|
|
data_handle = find_dissector("data");
|
2004-09-23 17:34:35 +00:00
|
|
|
|
2009-07-09 20:44:28 +00:00
|
|
|
/* Register for EtherNet/IP Device Level Ring protocol */
|
|
|
|
dlr_handle = new_create_dissector_handle(dissect_dlr, proto_dlr);
|
|
|
|
dissector_add("ethertype", ETHERTYPE_DLR, dlr_handle);
|
|
|
|
|
2004-09-23 17:34:35 +00:00
|
|
|
} /* end of proto_reg_handoff_enip() */
|