diff options
author | stig <stig@f5534014-38df-0310-8fa8-9805f1628bb7> | 2009-06-19 06:15:52 +0000 |
---|---|---|
committer | stig <stig@f5534014-38df-0310-8fa8-9805f1628bb7> | 2009-06-19 06:15:52 +0000 |
commit | 3eea0f835744c5c9f05d664f6ece4174b4fdb852 (patch) | |
tree | 6ed7687b76e2eee8e076317a0f623ae51ce9195c /epan/dissectors/packet-enip.c | |
parent | 48adc45c5484564dde94d1745a954e81c831ac43 (diff) |
Revert changes committed by an accident.
Will be committed again later.
git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@28779 f5534014-38df-0310-8fa8-9805f1628bb7
Diffstat (limited to 'epan/dissectors/packet-enip.c')
-rw-r--r-- | epan/dissectors/packet-enip.c | 1503 |
1 files changed, 513 insertions, 990 deletions
diff --git a/epan/dissectors/packet-enip.c b/epan/dissectors/packet-enip.c index 02cd23dbf9..09e0beb31a 100644 --- a/epan/dissectors/packet-enip.c +++ b/epan/dissectors/packet-enip.c @@ -6,10 +6,6 @@ * Magnus Hansson <mah@hms.se> * Joakim Wiberg <jow@hms.se> * - * Conversation data support for CIP - * Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG - * Copyright 2007 - * * $Id$ * * Wireshark - Network traffic analyzer @@ -42,18 +38,19 @@ #include <glib.h> #include <epan/packet.h> -#include <epan/emem.h> -#include <epan/conversation.h> -#include <epan/prefs.h> +#include <prefs.h> #include "packet-tcp.h" -#include "packet-enip.h" #include "packet-cip.h" -#define se_new(type) ((type*)se_alloc(sizeof(type))) /* Communication Ports */ -#define ENIP_ENCAP_PORT 44818 /* EtherNet/IP located on port 44818 */ -#define ENIP_IO_PORT 2222 /* EtherNet/IP IO located on port 2222 */ +#define ENIP_ENCAP_PORT 44818 /* EtherNet/IP located on port 44818 */ +#define ENIP_IO_PORT 2222 /* EtherNet/IP IO located on port 2222 */ + +/* Return codes of function classifying packets as query/response */ +#define REQUEST_PACKET 0 +#define RESPONSE_PACKET 1 +#define CANNOT_CLASSIFY 2 /* EtherNet/IP function codes */ #define NOP 0x0000 @@ -121,10 +118,6 @@ static int hf_enip_cpf_typeid = -1; static int hf_enip_cpf_sai_connid = -1; static int hf_enip_cpf_sai_seqnum = -1; -static int hf_enip_response_in = -1; -static int hf_enip_response_to = -1; -static int hf_enip_time = -1; - /* Initialize the subtree pointers */ static gint ett_enip = -1; static gint ett_count_tree = -1; @@ -142,759 +135,322 @@ static gboolean enip_desegment = TRUE; /* Translate function to string - Encapsulation commands */ static const value_string encap_cmd_vals[] = { - { 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 } + { 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 } }; /* Translate function to string - Encapsulation status */ static const value_string encap_status_vals[] = { - { 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 } + { 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 } }; /* Translate function to Common data format values */ static const value_string cdf_type_vals[] = { - { 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 } + { 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 } }; /* Translate function to string - True/False */ static const value_string enip_true_false_vals[] = { - { 0, "False" }, - { 1, "True" }, + { 0, "False" }, + { 1, "True" }, - { 0, NULL } + { 0, NULL } }; /* Translate interface handle to string */ static const value_string enip_interface_handle_vals[] = { - { 0, "CIP" }, + { 0, "CIP" }, - { 0, NULL } + { 0, NULL } }; -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() */ /* Disssect Common Packet Format */ static void -dissect_cpf( enip_request_key_t *request_key, int command, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 ifacehndl ) +dissect_cpf( int command, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 ifacehndl ) { proto_item *temp_item, *count_item, *type_item, *sockaddr_item; - 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; + 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; + + /* 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 ); + + 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 ); + + /* 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 ) ); - /* 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 ); + item = tvb_get_letohs( tvb, offset+2 ); + item_length = tvb_get_letohs( tvb, offset+4 ); + + if( item_length ) + { + /* Add item data field */ + + switch( item ) + { + case CONNECTION_BASED: + + /* Add Connection identifier */ + proto_tree_add_text( item_tree, tvb, offset+6, 4, "Connection Identifier: 0x%08X", tvb_get_letohl( tvb, offset + 6 ) ); + + /* 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 ) ); + } + + break; - 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 ); + case UNCONNECTED_MSG: + + /* Call dissector for interface */ + next_tvb = tvb_new_subset( tvb, offset+6, item_length, item_length ); + + if( tvb_length(next_tvb) == 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 ); + } + + break; + + case CONNECTION_TRANSPORT: + + if( command == SEND_UNIT_DATA ) + { + /* + ** 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); + + if( tvb_length(next_tvb) == 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 ); + } - /* 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 ) ); + } + else + { + /* Display data */ + if (tvb_length_remaining(tvb, offset+6) > 0) + { + next_tvb = tvb_new_subset(tvb, offset+6, item_length, item_length); + call_dissector(data_handle, next_tvb, pinfo, item_tree); + } + } /* End of if send unit data */ - item = tvb_get_letohs( tvb, offset+2 ); - item_length = tvb_get_letohs( tvb, offset+4 ); + break; - if( item_length ) - { - /* Add item data field */ - switch( item ) - { - case CONNECTION_BASED: + case LIST_IDENTITY_RESP: - 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 ) ); + /* 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 ); - /* 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 ) ); - } + /* 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 ); - break; + /* Socket address struct - sin_family */ + proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinfamily, + tvb, offset+8, 2, FALSE ); - case UNCONNECTED_MSG: - request_info = NULL; - if ( request_key ) - { - request_key->type = EPDT_UNCONNECTED; - request_info = enip_match_request( pinfo, tree, request_key ); - } + /* Socket address struct - sin_port */ + proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinport, + tvb, offset+10, 2, FALSE ); - /* 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); + /* Socket address struct - sin_address */ + proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinaddr, + tvb, offset+12, 4, FALSE ); - break; + /* Socket address struct - sin_zero */ + proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinzero, + tvb, offset+16, 8, FALSE ); - case CONNECTION_TRANSPORT: + /* Vendor ID */ + proto_tree_add_item(item_tree, hf_enip_lir_vendor, + tvb, offset+24, 2, TRUE ); - 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: " ); + /* Device Type */ + proto_tree_add_item(item_tree, hf_enip_lir_devtype, + tvb, offset+26, 2, TRUE ); - } /* End of if send unit data */ + /* Product Code */ + proto_tree_add_item(item_tree, hf_enip_lir_prodcode, + tvb, offset+28, 2, TRUE ); - break; + /* 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 ); + /* Status */ + proto_tree_add_item(item_tree, hf_enip_lir_status, + tvb, offset+32, 2, TRUE ); - case LIST_IDENTITY_RESP: + /* Serial Number */ + proto_tree_add_item(item_tree, hf_enip_lir_serial, + tvb, offset+34, 4, TRUE ); - /* 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 ); + /* 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 ); - /* 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 ); + /* Product Name */ + proto_tree_add_item(item_tree, hf_enip_lir_name, + tvb, offset+39, name_length, TRUE ); - /* Socket address struct - sin_family */ - proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinfamily, - tvb, offset+8, 2, FALSE ); + /* 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)); + } - /* Socket address struct - sin_port */ - proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinport, - tvb, offset+10, 2, FALSE ); + /* State */ + proto_tree_add_item(item_tree, hf_enip_lir_state, + tvb, offset+name_length+39, 1, TRUE ); + break; - /* Socket address struct - sin_address */ - proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinaddr, - tvb, offset+12, 4, FALSE ); - /* Socket address struct - sin_zero */ - proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinzero, - tvb, offset+16, 8, FALSE ); + case SOCK_ADR_INFO_OT: + case SOCK_ADR_INFO_TO: - /* Vendor ID */ - proto_tree_add_item(item_tree, hf_enip_lir_vendor, - tvb, offset+24, 2, TRUE ); + /* Socket address struct - sin_family */ + proto_tree_add_item(item_tree, hf_enip_lir_sinfamily, + tvb, offset+6, 2, FALSE ); - /* Device Type */ - proto_tree_add_item(item_tree, hf_enip_lir_devtype, - tvb, offset+26, 2, TRUE ); + /* Socket address struct - sin_port */ + proto_tree_add_item(item_tree, hf_enip_lir_sinport, + tvb, offset+8, 2, FALSE ); - /* Product Code */ - proto_tree_add_item(item_tree, hf_enip_lir_prodcode, - tvb, offset+28, 2, TRUE ); + /* Socket address struct - sin_address */ + proto_tree_add_item(item_tree, hf_enip_lir_sinaddr, + tvb, offset+10, 4, FALSE ); - /* 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 ); + /* Socket address struct - sin_zero */ + proto_tree_add_item( item_tree, hf_enip_lir_sinzero, + tvb, offset+14, 8, FALSE ); + break; - /* Status */ - proto_tree_add_item(item_tree, hf_enip_lir_status, - tvb, offset+32, 2, TRUE ); - /* Serial Number */ - proto_tree_add_item(item_tree, hf_enip_lir_serial, - tvb, offset+34, 4, TRUE ); + case SEQ_ADDRESS: + proto_tree_add_item(item_tree, hf_enip_cpf_sai_connid, + tvb, offset+6, 4, TRUE ); - /* 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 ); + proto_tree_add_item(item_tree, hf_enip_cpf_sai_seqnum, + tvb, offset+10, 4, TRUE ); - /* Product Name */ - proto_tree_add_item(item_tree, hf_enip_lir_name, - tvb, offset+39, name_length, TRUE ); + /* Add info to column */ - /* 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)); - } + if(check_col(pinfo->cinfo, COL_INFO)) + { + col_clear(pinfo->cinfo, COL_INFO); - /* State */ - proto_tree_add_item(item_tree, hf_enip_lir_state, - tvb, offset+name_length+39, 1, TRUE ); - break; + 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; - case SOCK_ADR_INFO_OT: - case SOCK_ADR_INFO_TO: + case LIST_SERVICES_RESP: - /* Socket address struct - sin_family */ - proto_tree_add_item(item_tree, hf_enip_lir_sinfamily, - tvb, offset+6, 2, FALSE ); + /* 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 ); - /* Socket address struct - sin_port */ - proto_tree_add_item(item_tree, hf_enip_lir_sinport, - tvb, offset+8, 2, FALSE ); + /* 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); - /* Socket address struct - sin_address */ - proto_tree_add_item(item_tree, hf_enip_lir_sinaddr, - tvb, offset+10, 4, FALSE ); + 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 ); - /* 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); + /* 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) ); - 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; + /* 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) ); + } - case LIST_SERVICES_RESP: + break; - /* 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 ); - /* 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); + default: + if (tvb_length_remaining(tvb, offset+6) > 0) + { + next_tvb = tvb_new_subset(tvb, offset+6, item_length, item_length); + call_dissector(data_handle, next_tvb, pinfo, item_tree); + } + break; - 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 ); + } /* end of switch( item type ) */ - /* 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) ); + } /* end of if( item length ) */ - /* 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; + offset = offset + item_length + 4; - - 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 ) */ + } /* end of while( item count ) */ } /* end of dissect_cpf() */ @@ -903,17 +459,17 @@ dissect_cpf( enip_request_key_t *request_key, int command, tvbuff_t *tvb, packet static int classify_packet(packet_info *pinfo) { - /* 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; + /* 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 RESPONSE_PACKET; + else if ( ENIP_ENCAP_PORT == pinfo->destport ) + return REQUEST_PACKET; + } + /* else, cannot classify */ + return CANNOT_CLASSIFY; } static guint @@ -937,16 +493,17 @@ get_enip_pdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset) static void dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { - int packet_type; + int packet_type; guint16 encap_cmd, encap_data_length; const char *pkt_type_str = ""; guint32 ifacehndl; - enip_request_key_t request_key; - conversation_t *conversation; + tvbuff_t *next_tvb; /* Set up structures needed to add the protocol subtree and manage it */ proto_item *ti, *encaph, *csf; - proto_tree *enip_tree, *header_tree = NULL, *csftree; + proto_tree *enip_tree = NULL; + proto_tree *header_tree = NULL; + proto_tree *csftree; /* Make entries in Protocol column and Info column on summary display */ if (check_col(pinfo->cinfo, COL_PROTOCOL)) @@ -955,18 +512,19 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) col_clear(pinfo->cinfo, COL_INFO); encap_cmd = tvb_get_letohs( tvb, 0 ); - - packet_type = classify_packet(pinfo); + encap_data_length = tvb_get_letohs( tvb, 2 ); if( check_col(pinfo->cinfo, COL_INFO) ) { + packet_type = classify_packet(pinfo); + switch ( packet_type ) { - case ENIP_REQUEST_PACKET: + case REQUEST_PACKET: pkt_type_str="Req"; break; - case ENIP_RESPONSE_PACKET: + case RESPONSE_PACKET: pkt_type_str="Rsp"; break; @@ -976,48 +534,16 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) /* Add service and request/response to info column */ col_add_fstr(pinfo->cinfo, COL_INFO, - "%s (%s)", + "%s (%s)", val_to_str(encap_cmd, encap_cmd_vals, "Unknown (0x%04x)"), pkt_type_str ); - } /* end of if( col exists ) */ - - /* - * 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; /* In the interest of speed, if "tree" is NULL, don't do any work not necessary to generate protocol tree items. */ if (tree) { + /* create display subtree for the protocol */ ti = proto_tree_add_item(tree, proto_enip, tvb, 0, -1, FALSE); @@ -1030,7 +556,6 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) /* Add EtherNet/IP encapsulation header */ proto_tree_add_item( header_tree, hf_enip_command, tvb, 0, 2, TRUE ); - encap_data_length = tvb_get_letohs( tvb, 2 ); proto_tree_add_text( header_tree, tvb, 2, 2, "Length: %u", encap_data_length ); proto_tree_add_item( header_tree, hf_enip_session, tvb, 4, 4, TRUE ); @@ -1042,95 +567,100 @@ dissect_enip_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *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)" ) ); - /* - ** For some commands we want to add some info to the info column - */ + } /* end of if (tree) */ - if( check_col( pinfo->cinfo, COL_INFO ) ) - { + /* + ** For some commands we want to add some info to the info column + */ - switch( encap_cmd ) - { - case REGISTER_SESSION: - case UNREGISTER_SESSION: - col_append_fstr( pinfo->cinfo, COL_INFO, ", Session: 0x%08X", - tvb_get_letohl( tvb, 4 ) ); + if( check_col( pinfo->cinfo, COL_INFO ) ) + { - } /* end of switch() */ + switch( encap_cmd ) + { + case REGISTER_SESSION: + case UNREGISTER_SESSION: + col_append_fstr( pinfo->cinfo, COL_INFO, ", Session: 0x%08X", + tvb_get_letohl( tvb, 4 ) ); - } /* end of id info column */ - } /* end of tree */ + } /* end of switch() */ - /* Command specific data - create tree */ - if( encap_data_length ) - { - /* The packet have some command specific data, buid a sub tree for it */ + } /* end of id info column */ - csf = proto_tree_add_text( enip_tree, tvb, 24, encap_data_length, - "Command Specific Data"); + /* Command specific data - create tree */ + if( encap_data_length ) + { + /* The packet have some command specific data, buid a sub tree for it */ - csftree = proto_item_add_subtree(csf, ett_command_tree); + csf = proto_tree_add_text( enip_tree, tvb, 24, encap_data_length, + "Command Specific Data"); - switch( encap_cmd ) - { - case NOP: - break; + csftree = proto_item_add_subtree(csf, ett_command_tree); - case LIST_SERVICES: - dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 ); - break; + switch( encap_cmd ) + { + case NOP: + break; - case LIST_IDENTITY: - dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 ); - break; + case LIST_SERVICES: + dissect_cpf( encap_cmd, tvb, pinfo, csftree, 24, 0 ); + break; - case LIST_INTERFACES: - dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 ); - break; + case LIST_IDENTITY: + dissect_cpf( encap_cmd, tvb, pinfo, csftree, 24, 0 ); + break; - case REGISTER_SESSION: - proto_tree_add_text( csftree, tvb, 24, 2, "Protocol Version: 0x%04X", - tvb_get_letohs( tvb, 24 ) ); + case LIST_INTERFACES: + dissect_cpf( encap_cmd, tvb, pinfo, csftree, 24, 0 ); + break; - proto_tree_add_text( csftree, tvb, 26, 2, "Option Flags: 0x%04X", - tvb_get_letohs( tvb, 26 ) ); + case REGISTER_SESSION: + proto_tree_add_text( csftree, tvb, 24, 2, "Protocol Version: 0x%04X", + tvb_get_letohs( tvb, 24 ) ); - break; + proto_tree_add_text( csftree, tvb, 26, 2, "Option Flags: 0x%04X", + tvb_get_letohs( tvb, 26 ) ); - case UNREGISTER_SESSION: - break; + break; - case SEND_RR_DATA: - proto_tree_add_item(csftree, hf_enip_srrd_ifacehnd, tvb, 24, 4, TRUE); + case UNREGISTER_SESSION: + break; - proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u", - tvb_get_letohs( tvb, 28 ) ); + case SEND_RR_DATA: + proto_tree_add_item(csftree, hf_enip_srrd_ifacehnd, tvb, 24, 4, TRUE); - ifacehndl = tvb_get_letohl( tvb, 24 ); - dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 30, ifacehndl ); - break; + proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u", + tvb_get_letohs( tvb, 28 ) ); - case SEND_UNIT_DATA: - proto_tree_add_item(csftree, hf_enip_sud_ifacehnd, tvb, 24, 4, TRUE); + ifacehndl = tvb_get_letohl( tvb, 24 ); + dissect_cpf( encap_cmd, tvb, pinfo, csftree, 30, ifacehndl ); + break; - proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u", - tvb_get_letohs( tvb, 28 ) ); + case SEND_UNIT_DATA: + proto_tree_add_item(csftree, hf_enip_sud_ifacehnd, tvb, 24, 4, TRUE); - ifacehndl = tvb_get_letohl( tvb, 24 ); - dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 30, ifacehndl ); - break; + proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u", + tvb_get_letohs( tvb, 28 ) ); - case INDICATE_STATUS: - case CANCEL: - default: + ifacehndl = tvb_get_letohl( tvb, 24 ); + dissect_cpf( encap_cmd, tvb, pinfo, csftree, 30, ifacehndl ); + break; - /* Can not decode - Just show the data */ - add_byte_array_text_to_proto_tree( header_tree, tvb, 24, encap_data_length, "Encap Data: " ); - break; + case INDICATE_STATUS: + case CANCEL: + default: + /* Can not decode - Just show the data */ + if (tvb_length_remaining(tvb, 24) > 0) + { + next_tvb = tvb_new_subset(tvb, 24, encap_data_length, encap_data_length); + call_dissector(data_handle, next_tvb, pinfo, header_tree); + } + break; - } /* end of switch() */ + } /* end of switch() */ + + } /* end of if( encapsulated data ) */ - } /* end of if( encapsulated data ) */ } /* end of dissect_enip_pdu() */ static int @@ -1141,13 +671,13 @@ dissect_enip_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) g_tree = tree; /* An ENIP packet is at least 4 bytes long - we need the command type. */ - if (!tvb_bytes_exist(tvb, 0, 4)) + if (tvb_length(tvb) < 4) 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) - return 0; /* not a known command */ + return 0; /* not a known command */ dissect_enip_pdu(tvb, pinfo, tree); return tvb_length(tvb); @@ -1161,45 +691,55 @@ dissect_enip_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) g_tree = tree; /* An ENIP packet is at least 4 bytes long - we need the command type. */ - if (!tvb_bytes_exist(tvb, 0, 4)) + if (tvb_length(tvb) < 4) 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) - return 0; /* not a known command */ + return 0; /* not a known command */ tcp_dissect_pdus(tvb, pinfo, tree, enip_desegment, 4, - get_enip_pdu_len, dissect_enip_pdu); + get_enip_pdu_len, dissect_enip_pdu); return tvb_length(tvb); } /* Code to actually dissect the io packets*/ -static void +static int dissect_enipio(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { /* Set up structures needed to add the protocol subtree and manage it */ - proto_item *ti; - proto_tree *enip_tree; + proto_item *ti; + proto_tree *enip_tree = NULL; + guint16 type_id; - g_tree = tree; + g_tree = tree; + + /* Verify that the packet belongs to this dissector */ + if (tvb_length(tvb) < 4) + return 0; + + type_id = tvb_get_letohs( tvb, 2 ); + if (match_strval(type_id, cdf_type_vals) == NULL) + return 0; /* not a known type id */ /* 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"); + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "ENIP"); /* In the interest of speed, if "tree" is NULL, don't do any work not necessary to generate protocol tree items. */ - if (tree) - { - /* create display subtree for the protocol */ - ti = proto_tree_add_item(tree, proto_enip, tvb, 0, -1, FALSE); + if (tree) + { + /* create display subtree for the protocol */ + ti = proto_tree_add_item(tree, proto_enip, tvb, 0, -1, FALSE); - enip_tree = proto_item_add_subtree(ti, ett_enip); + enip_tree = proto_item_add_subtree(ti, ett_enip); + } - dissect_cpf( NULL, 0xFFFF, tvb, pinfo, enip_tree, 0, 0 ); - } + dissect_cpf( 0xFFFF, tvb, pinfo, enip_tree, 0, 0 ); + return tvb_length(tvb); } /* end of dissect_enipio() */ @@ -1213,178 +753,161 @@ void proto_register_enip(void) { /* Setup list of header fields */ - 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 */ + 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_HEX, NULL, 0, + "Information pertinent 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 */ { &hf_enip_lir_sinfamily, - { "sin_family", "enip.lir.sa.sinfamily", - FT_UINT16, BASE_DEC, NULL, 0, - "ListIdentity Reply: Socket Address.Sin Family", HFILL } - }, + { "sin_family", "enip.lir.sa.sinfamily", + FT_UINT16, BASE_DEC, NULL, 0, + "ListIdentity Reply: Socket Address.Sin Family", HFILL } + }, { &hf_enip_lir_sinport, - { "sin_port", "enip.lir.sa.sinport", - FT_UINT16, BASE_DEC, NULL, 0, - "ListIdentity Reply: Socket Address.Sin Port", HFILL } - }, + { "sin_port", "enip.lir.sa.sinport", + FT_UINT16, BASE_DEC, NULL, 0, + "ListIdentity Reply: Socket Address.Sin Port", HFILL } + }, { &hf_enip_lir_sinaddr, - { "sin_addr", "enip.lir.sa.sinaddr", - FT_IPv4, BASE_HEX, NULL, 0, - "ListIdentity Reply: Socket Address.Sin Addr", HFILL } - }, + { "sin_addr", "enip.lir.sa.sinaddr", + FT_IPv4, BASE_HEX, NULL, 0, + "ListIdentity Reply: Socket Address.Sin Addr", HFILL } + }, { &hf_enip_lir_sinzero, - { "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 } - }, + { "sin_zero", "enip.lir.sa.sinzero", + FT_BYTES, BASE_HEX, 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 } + }, { &hf_enip_lir_devtype, - { "Device Type", "enip.lir.devtype", - FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0, - "ListIdentity Reply: Device Type", HFILL } - }, + { "Device Type", "enip.lir.devtype", + FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0, + "ListIdentity Reply: Device Type", HFILL } + }, { &hf_enip_lir_prodcode, - { "Product Code", "enip.lir.prodcode", - FT_UINT16, BASE_DEC, NULL, 0, - "ListIdentity Reply: Product Code", HFILL } - }, + { "Product Code", "enip.lir.prodcode", + FT_UINT16, BASE_DEC, NULL, 0, + "ListIdentity Reply: Product Code", HFILL } + }, { &hf_enip_lir_status, - { "Status", "enip.lir.status", - FT_UINT16, BASE_HEX, NULL, 0, - "ListIdentity Reply: Status", HFILL } - }, + { "Status", "enip.lir.status", + FT_UINT16, BASE_HEX, NULL, 0, + "ListIdentity Reply: Status", HFILL } + }, { &hf_enip_lir_serial, - { "Serial Number", "enip.lir.serial", - FT_UINT32, BASE_HEX, NULL, 0, - "ListIdentity Reply: Serial Number", HFILL } - }, + { "Serial Number", "enip.lir.serial", + FT_UINT32, BASE_HEX, NULL, 0, + "ListIdentity Reply: Serial Number", HFILL } + }, { &hf_enip_lir_name, - { "Product Name", "enip.lir.name", - FT_STRING, BASE_NONE, NULL, 0, - "ListIdentity Reply: Product Name", HFILL } - }, + { "Product Name", "enip.lir.name", + FT_STRING, BASE_NONE, NULL, 0, + "ListIdentity Reply: Product Name", HFILL } + }, { &hf_enip_lir_state, - { "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 */ + { "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 */ { &hf_enip_cpf_sai_connid, - { "Connection ID", "enip.cpf.sai.connid", - FT_UINT32, BASE_HEX, NULL, 0, - "Common Packet Format: Sequenced Address Item, Connection Identifier", HFILL } - }, + { "Connection ID", "enip.cpf.sai.connid", + FT_UINT32, BASE_HEX, NULL, 0, + "Common Packet Format: Sequenced Address Item, Connection Identifier", HFILL } + }, { &hf_enip_cpf_sai_seqnum, - { "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", - FT_FRAMENUM, BASE_DEC, NULL, 0x0, - "The response to this ENIP request is in this frame", HFILL } - }, - { &hf_enip_response_to, - { "Request In", "enip.response_to", - FT_FRAMENUM, BASE_DEC, NULL, 0x0, - "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 } - } + { "Sequence Number", "enip.cpf.sai.seq", + FT_UINT32, BASE_DEC, NULL, 0, + "Common Packet Format: Sequenced Address Item, Sequence Number", HFILL } + } }; /* Setup protocol subtree array */ - static gint *ett[] = { - &ett_enip, - &ett_count_tree, - &ett_type_tree, - &ett_command_tree, - &ett_sockadd, - &ett_lsrcf, - }; - module_t *enip_module; + static gint *ett[] = { + &ett_enip, + &ett_count_tree, + &ett_type_tree, + &ett_command_tree, + &ett_sockadd, + &ett_lsrcf, + }; + module_t *enip_module; /* Register the protocol name and description */ - proto_enip = proto_register_protocol("EtherNet/IP (Industrial Protocol)", - "ENIP", "enip"); + proto_enip = proto_register_protocol("EtherNet/IP (Industrial Protocol)", + "ENIP", "enip"); /* Required function calls to register the header fields and subtrees used */ - proto_register_field_array(proto_enip, hf, array_length(hf)); - proto_register_subtree_array(ett, array_length(ett)); + proto_register_field_array(proto_enip, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); - 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); + 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); - subdissector_sud_table = register_dissector_table("enip.sud.iface", - "SendUnitData.Interface Handle", FT_UINT32, BASE_HEX); + subdissector_sud_table = register_dissector_table("enip.sud.iface", + "SendUnitData.Interface Handle", FT_UINT32, BASE_HEX); - subdissector_srrd_table = register_dissector_table("enip.srrd.iface", - "SendRequestReplyData.Interface Handle", FT_UINT32, BASE_HEX); + subdissector_srrd_table = register_dissector_table("enip.srrd.iface", + "SendRequestReplyData.Interface Handle", FT_UINT32, BASE_HEX); - register_init_routine(&enip_init_protocol); } /* end of proto_register_enip() */ @@ -1395,22 +918,22 @@ proto_register_enip(void) void proto_reg_handoff_enip(void) { - dissector_handle_t enip_udp_handle, enip_tcp_handle; - dissector_handle_t enipio_handle; + dissector_handle_t enip_udp_handle, enip_tcp_handle; + dissector_handle_t enipio_handle; - /* 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); + /* 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); - /* 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); + /* 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); - /* 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); + /* Register for EtherNet/IP IO data (UDP) */ + enipio_handle = new_create_dissector_handle(dissect_enipio, proto_enip); + dissector_add("udp.port", ENIP_IO_PORT, enipio_handle); - /* Find dissector for data packet */ - data_handle = find_dissector("data"); + /* Find dissector for data packet */ + data_handle = find_dissector("data"); } /* end of proto_reg_handoff_enip() */ |