From Joakim Wiberg via bug 3103:
authorstig <stig@f5534014-38df-0310-8fa8-9805f1628bb7>
Fri, 26 Jun 2009 02:38:23 +0000 (02:38 +0000)
committerstig <stig@f5534014-38df-0310-8fa8-9805f1628bb7>
Fri, 26 Jun 2009 02:38:23 +0000 (02:38 +0000)
Various improvements to the CIP and ENIP dissectors.

From me:
Updated to latest trunk changes.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@28844 f5534014-38df-0310-8fa8-9805f1628bb7

epan/dissectors/Makefile.common
epan/dissectors/packet-cip.c
epan/dissectors/packet-cip.h
epan/dissectors/packet-enip.c
epan/dissectors/packet-enip.h [new file with mode: 0644]

index 7fca9cc88df0a30b01670bb9a58052e7f386b0c0..59a9cbdb06ca552bdef30aea3eb793df8f714c30 100644 (file)
@@ -1012,6 +1012,7 @@ DISSECTOR_INCLUDES =      \
        packet-dns.h    \
        packet-dop.h    \
        packet-dsp.h    \
+       packet-enip.h   \
        packet-erf.h    \
        packet-dvmrp.h  \
        packet-e164.h   \
index c33a9017dbc5c1969622d5abf4d1116b8118cb50..de8931693aada7adabda9c8909486e5c30dd9aee 100644 (file)
@@ -8,8 +8,10 @@
  *
  * Added support for Connection Configuration Object
  *   ryan wamsley * Copyright 2007
- * Added Additional Status text in Forward Open Response
- *   ryan wamsley * Copyright 2008
+ *
+ * Object dependend services based on IOI
+ *   Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG
+ *   Copyright 2007
  *
  * $Id$
  *
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  */
 
+/*
+      &ett_cip,         dissect_cip
+      &ett_path,        dissect_epath
+      &ett_ekey_path,   dissect_epath
+      &ett_mcsc,        dissect_epath,
+      &ett_cia_path,    dissect_epath
+      &ett_data_seg,    dissect_epath
+      &ett_port_path,   dissect_epath
+      &ett_rrsc,        dissect_cip_generic_data, dissect_cip_mr_data, dissect_cip_cm_data, dissect_cip_cco_data, dissect_cip_pccc_data
+      &ett_status_item  dissect_cip_generic_data, dissect_cip_mr_data, dissect_cip_cm_data, dissect_cip_cco_data, dissect_cip_pccc_data
+
+      &ett_cmd_data,    dissect_cip_generic_data, dissect_cip_mr_data, dissect_cip_cm_data, dissect_cip_cco_data, dissect_cip_pccc_data
+      &ett_ncp,         dissect_cip_cm_data
+      &ett_mes_req,     dissect_cip_cm_data
+      &ett_mult_ser,    dissect_cip_mr_data
+*/
+
 #ifdef HAVE_CONFIG_H
 # include "config.h"
 #endif
 #include <glib.h>
 
 #include <epan/packet.h>
-#include "packet-tcp.h"
+#include "packet-enip.h"
 #include "packet-cip.h"
 
 #define  ENIP_CIP_INTERFACE   0
 
+typedef struct cip_req_info {
+   dissector_handle_t dissector;
+   guint8 bService;
+   guint IOILen;
+   void *pIOI;
+   void *pData;
+} cip_req_info_t;
+
+dissector_handle_t cip_handle;
+dissector_handle_t cip_class_generic_handle,cip_class_cm_handle,cip_class_mr_handle;
+dissector_handle_t cip_class_cco_handle;
+
 /* Initialize the protocol and registered fields */
-static int proto_cip              = -1;
-
-static int hf_cip_sc              = -1;
-static int hf_cip_rr              = -1;
-static int hf_cip_epath           = -1;
-static int hf_cip_genstat         = -1;
-
-static int hf_cip_fwo_comp        = -1;
-static int hf_cip_fwo_mrev        = -1;
-static int hf_cip_fwo_con_size    = -1;
-static int hf_cip_fwo_fixed_var   = -1;
-static int hf_cip_fwo_prio        = -1;
-static int hf_cip_fwo_typ         = -1;
-static int hf_cip_fwo_own         = -1;
-static int hf_cip_fwo_dir         = -1;
-static int hf_cip_fwo_trigg       = -1;
-static int hf_cip_fwo_class       = -1;
+static int proto_cip               = -1;
+static int proto_cip_class_generic = -1;
+static int proto_cip_class_cm      = -1;
+static int proto_cip_class_mr      = -1;
+static int proto_cip_class_cco     = -1;
+static int proto_enip              = -1;
+
+static int hf_cip_sc      = -1;
+static int hf_cip_rr      = -1;
+static int hf_cip_epath   = -1;
+static int hf_cip_genstat = -1;
+
+static int hf_cip_fwo_comp = -1;
+static int hf_cip_fwo_mrev = -1;
+
+static int hf_cip_cm_sc            = -1;
+static int hf_cip_cm_rr            = -1;
+static int hf_cip_cm_fwo_comp      = -1;
+static int hf_cip_cm_fwo_mrev      = -1;
+static int hf_cip_cm_fwo_con_size  = -1;
+static int hf_cip_cm_fwo_fixed_var = -1;
+static int hf_cip_cm_fwo_prio      = -1;
+static int hf_cip_cm_fwo_typ       = -1;
+static int hf_cip_cm_fwo_own       = -1;
+static int hf_cip_cm_fwo_dir       = -1;
+static int hf_cip_cm_fwo_trigg     = -1;
+static int hf_cip_cm_fwo_class     = -1;
 
 static int hf_cip_vendor              = -1;
 static int hf_cip_devtype             = -1;
@@ -78,6 +119,9 @@ static int hf_cip_class32             = -1;
 static int hf_cip_instance8           = -1;
 static int hf_cip_instance16          = -1;
 static int hf_cip_instance32          = -1;
+static int hf_cip_member8             = -1;
+static int hf_cip_member16            = -1;
+static int hf_cip_member32            = -1;
 static int hf_cip_attribute8          = -1;
 static int hf_cip_attribute16         = -1;
 static int hf_cip_attribute32         = -1;
@@ -86,168 +130,205 @@ static int hf_cip_conpoint16          = -1;
 static int hf_cip_conpoint32          = -1;
 static int hf_cip_symbol              = -1;
 
-static int hf_cip_data                = -1;
+static int hf_cip_mr_sc = -1;
+static int hf_cip_mr_rr = -1;
 
-/* Initialize the subtree pointers */
-static gint ett_cip           = -1;
-static gint ett_ekey_path     = -1;
-static gint ett_cia_path      = -1;
-static gint ett_data_seg      = -1;
-static gint ett_rrsc          = -1;
-static gint ett_mcsc          = -1;
-static gint ett_ncp           = -1;
-static gint ett_lsrcf         = -1;
-static gint ett_mes_req       = -1;
-static gint ett_cmd_data      = -1;
-static gint ett_port_path     = -1;
-static gint ett_mult_ser      = -1;
-static gint ett_path          = -1;
-static gint ett_status_item   = -1;
+static int hf_cip_cco_sc = -1;
+static int hf_cip_cco_rr = -1;
 
+/* Initialize the subtree pointers */
+static gint ett_cip               = -1;
+static gint ett_cip_class_generic = -1;
+static gint ett_cip_class_mr      = -1;
+static gint ett_cip_class_cm      = -1;
+static gint ett_cip_class_cco     = -1;
+
+static gint ett_path      = -1;
+static gint ett_ekey_path = -1;
+static gint ett_mcsc      = -1;
+static gint ett_cia_path  = -1;
+static gint ett_data_seg  = -1;
+static gint ett_port_path = -1;
+
+static gint ett_rrsc        = -1;
+static gint ett_status_item = -1;
+static gint ett_cmd_data    = -1;
+
+static gint ett_cm_rrsc     = -1;
+static gint ett_cm_ncp      = -1;
+static gint ett_cm_mes_req  = -1;
+static gint ett_cm_cmd_data = -1;
+
+static gint ett_mr_rrsc     = -1;
+static gint ett_mr_mult_ser = -1;
+static gint ett_mr_cmd_data = -1;
+
+static gint ett_cco_rrsc     = -1;
+static gint ett_cco_cmd_data = -1;
+
+static dissector_table_t   subdissector_class_table;
+static dissector_table_t   subdissector_symbol_table;
 
 /* Translate function to string - CIP Service codes */
 static const value_string cip_sc_vals[] = {
-       { SC_GET_ATT_ALL,                "Get Attribute All" },
-       { SC_SET_ATT_ALL,                "Set Attribute All" },
-       { SC_GET_ATT_LIST,            "Get Attribute List" },
-       { SC_SET_ATT_LIST,            "Set Attribute List" },
-       { SC_RESET,                    "Reset" },
-   { SC_START,                "Start" },
-   { SC_STOP,                 "Stop" },
-   { SC_CREATE,                    "Create" },
-   { SC_DELETE,                    "Delete" },
-   { SC_APPLY_ATTRIBUTES,         "Apply Attributes" },
-       { SC_GET_ATT_SINGLE,          "Get Attribute Single" },
-       { SC_SET_ATT_SINGLE,          "Set Attribute Single" },
-   { SC_FIND_NEXT_OBJ_INST,    "Find Next Object Instance" },
-   { SC_RESTOR,                    "Restore" },
-       { SC_SAVE,                     "Save" },
-       { SC_NO_OP,                    "Nop" },
-       { SC_GET_MEMBER,                 "Get Member" },
-       { SC_SET_MEMBER,                 "Set Member" },
-       { SC_MULT_SERV_PACK,       "Multiple Service Packet" },
-
-       /* Some class specific services */
-       { SC_FWD_CLOSE,          "Forward Close" },
-       { SC_FWD_OPEN,              "Forward Open" },
-       { SC_UNCON_SEND,           "Unconnected Send" },
-
-       /* Connection Configuration Object services */
-       { SC_KICK_TIMER,                 "Kick Timer" },
-       { SC_OPEN_CONN,                  "Open Connection" },
-       { SC_CLOSE_CONN,                 "Close Connection" },
-       { SC_CHANGE_START,               "Change Start" },
-       { SC_GET_STATUS,                 "Get Status" },
-       { SC_CHANGE_COMPLETE,            "Change Complete" },
-
-       { 0,                                        NULL }
+   { SC_GET_ATT_ALL,          "Get Attribute All" },
+   { SC_SET_ATT_ALL,          "Set Attribute All" },
+   { SC_GET_ATT_LIST,         "Get Attribute List" },
+   { SC_SET_ATT_LIST,         "Set Attribute List" },
+   { SC_RESET,                "Reset" },
+   { SC_START,                "Start" },
+   { SC_STOP,                 "Stop" },
+   { SC_CREATE,               "Create" },
+   { SC_DELETE,               "Delete" },
+   { SC_APPLY_ATTRIBUTES,     "Apply Attributes" },
+   { SC_GET_ATT_SINGLE,       "Get Attribute Single" },
+   { SC_SET_ATT_SINGLE,       "Set Attribute Single" },
+   { SC_FIND_NEXT_OBJ_INST,   "Find Next Object Instance" },
+   { SC_RESTOR,               "Restore" },
+   { SC_SAVE,                 "Save" },
+   { SC_NO_OP,                "Nop" },
+   { SC_GET_MEMBER,           "Get Member" },
+   { SC_SET_MEMBER,           "Set Member" },
+
+   { 0,                       NULL }
 };
 
-/* Translate function to string - CIP Service codes that collide with cip_sc_vals */
-static const value_string cip_sc_vals_cco[] = {
-       /* Connection Configuration Object services */
-       { SC_STOP_CONN,                  "Stop Connection" }, /* collision with SC_UNCON_SEND */
-       { SC_AUDIT_CHANGE,               "Audit Changes" },   /* collision with SC_UNCON_SEND */
+/* Translate function to string - CIP Service codes for MR */
+static const value_string cip_sc_vals_mr[] = {
+   { SC_GET_ATT_ALL,          "Get Attribute All" },
+   { SC_SET_ATT_ALL,          "Set Attribute All" },
+   { SC_GET_ATT_LIST,         "Get Attribute List" },
+   { SC_SET_ATT_LIST,         "Set Attribute List" },
+   /* Some class specific services */
+   { SC_MULT_SERV_PACK,       "Multiple Service Packet" },
+
+   { 0,                       NULL }
+};
+
+/* Translate function to string - CIP Service codes for CM */
+static const value_string cip_sc_vals_cm[] = {
+   { SC_GET_ATT_ALL,          "Get Attribute All" },
+   { SC_SET_ATT_ALL,          "Set Attribute All" },
+   { SC_GET_ATT_LIST,         "Get Attribute List" },
+   { SC_SET_ATT_LIST,         "Set Attribute List" },
+   /* Some class specific services */
+   { SC_CM_FWD_CLOSE,            "Forward Close" },
+   { SC_CM_FWD_OPEN,             "Forward Open" },
+   { SC_CM_UNCON_SEND,           "Unconnected Send" },
+
+   { 0,                       NULL }
+};
 
-       { 0,                                        NULL }
+/* Translate function to string - CIP Service codes for CCO */
+static const value_string cip_sc_vals_cco[] = {
+   { SC_CCO_KICK_TIMER,           "Kick Timer" },
+   { SC_CCO_OPEN_CONN,            "Open Connection" },
+   { SC_CCO_CLOSE_CONN,           "Close Connection" },
+   { SC_CCO_STOP_CONN,            "Stop Connection" },
+   { SC_CCO_CHANGE_START,         "Change Start" },
+   { SC_CCO_GET_STATUS,           "Get Status" },
+   { SC_CCO_CHANGE_COMPLETE,      "Change Complete" },
+   { SC_CCO_AUDIT_CHANGE,         "Audit Changes" },
+
+   { 0,                       NULL }
 };
 
 /* Translate function to string - CIP Request/Response */
 static const value_string cip_sc_rr[] = {
-       { 0,          "Request"  },
-       { 1,          "Response" },
+   { 0,        "Request"  },
+   { 1,        "Response" },
 
-       { 0,                    NULL }
+   { 0,        NULL }
 };
 
 /* Translate function to string - Compatibility */
 static const value_string cip_com_bit_vals[] = {
-       { 0,          "Bit Cleared" },
-       { 1,          "Bit Set"     },
+   { 0,        "Bit Cleared" },
+   { 1,        "Bit Set"     },
 
-       { 0,        NULL          }
+   { 0,        NULL          }
 };
 
 /* Translate function to string - Connection priority */
 static const value_string cip_con_prio_vals[] = {
-       { 0,          "Low Priority"  },
-       { 1,          "High Priority" },
-       { 2,          "Scheduled"     },
-       { 3,          "Urgent"        },
+   { 0,        "Low Priority"  },
+   { 1,        "High Priority" },
+   { 2,        "Scheduled"     },
+   { 3,        "Urgent"        },
 
-       { 0,        NULL            }
+   { 0,        NULL            }
 };
 
 /* Translate function to string - Connection size fixed or variable */
 static const value_string cip_con_fw_vals[] = {
-       { 0,          "Fixed"    },
-       { 1,          "Variable" },
+   { 0,        "Fixed"    },
+   { 1,        "Variable" },
 
-       { 0,        NULL       }
+   { 0,        NULL       }
 };
 
 /* Translate function to string - Connection owner */
 static const value_string cip_con_owner_vals[] = {
-       { 0,          "Exclusive" },
-       { 1,          "Redundant" },
+   { 0,        "Exclusive" },
+   { 1,        "Redundant" },
 
-       { 0,        NULL        }
+   { 0,        NULL        }
 };
 
 /* Translate function to string - Connection direction */
 static const value_string cip_con_dir_vals[] = {
-       { 0,          "Client" },
-       { 1,          "Server" },
+   { 0,        "Client" },
+   { 1,        "Server" },
 
-       { 0,        NULL        }
+   { 0,        NULL        }
 };
 
 /* Translate function to string - Production trigger */
 static const value_string cip_con_trigg_vals[] = {
-       { 0,          "Cyclic" },
-       { 1,          "Change-Of-State" },
-       { 2,          "Application Object" },
+   { 0,        "Cyclic" },
+   { 1,        "Change-Of-State" },
+   { 2,        "Application Object" },
 
-       { 0,        NULL        }
+   { 0,        NULL        }
 };
 
 /* Translate function to string - Transport class */
 static const value_string cip_con_class_vals[] = {
-       { 0,          "0" },
-       { 1,          "1" },
-       { 2,          "2" },
-       { 3,          "3" },
+   { 0,        "0" },
+   { 1,        "1" },
+   { 2,        "2" },
+   { 3,        "3" },
 
-       { 0,        NULL        }
+   { 0,        NULL        }
 };
 
 /* Translate function to string - Connection type */
 static const value_string cip_con_type_vals[] = {
-       { 0,          "Null"           },
-       { 1,          "Multicast"      },
-       { 2,          "Point to Point" },
-       { 3,          "Reserved"       },
+   { 0,        "Null"           },
+   { 1,        "Multicast"      },
+   { 2,        "Point to Point" },
+   { 3,        "Reserved"       },
 
-       { 0,        NULL             }
+   { 0,        NULL             }
 };
 
 /* Translate function to string - Timeout Multiplier */
 static const value_string cip_con_time_mult_vals[] = {
-       { 0,        "*4"   },
-       { 1,        "*8"   },
-       { 2,        "*16"  },
-       { 3,        "*32"  },
-       { 4,        "*64"  },
-       { 5,        "*128" },
-       { 6,        "*256" },
-       { 7,        "*512" },
+   { 0,        "*4"   },
+   { 1,        "*8"   },
+   { 2,        "*16"  },
+   { 3,        "*32"  },
+   { 4,        "*64"  },
+   { 5,        "*128" },
+   { 6,        "*256" },
+   { 7,        "*512" },
 
    { 0,        NULL    }
 };
 
 /* Translate function to string - CIP General Status codes */
 static const value_string cip_gs_vals[] = {
-       { CI_GRC_SUCCESS,             "Success" },
+   { CI_GRC_SUCCESS,             "Success" },
    { CI_GRC_FAILURE,             "Connection failure" },
    { CI_GRC_NO_RESOURCE,         "Resource unavailable" },
    { CI_GRC_BAD_DATA,            "Invalid parameter value" },
@@ -288,60 +369,14 @@ static const value_string cip_gs_vals[] = {
    { CI_GRC_INVALID_MEMBER,      "Invalid Member ID" },
    { CI_GRC_MEMBER_NOT_SETTABLE, "Member not settable" },
 
-       { 0,                                           NULL }
+   { 0,                          NULL }
 };
 
-/* Translate function to string - CIP Extended Status codes */
-static const value_string cip_es_vals[] = {
-       { CI_GRC_SUCCESS,           "Success" },
-   { CI_SREC_CONNECTION_IN_USE,     "Connection in Use or Duplicate Forward Open" },
-   { CI_SREC_TCLASS_TRIGGER_ERR,    "Transport Class and Trigger combination not supported" },
-   { CI_SREC_OWNERSHIP_CONFLICT,    "Ownership Conflict"  },
-   { CI_SREC_CONN_NOT_FOUND,        "Connection not found at target application" },
-   { CI_SREC_INVALID_CONN_TYPE,     "Invalid Connection Type" },
-   { CI_SREC_INVALID_CONN_SIZE,     "Invalid Connection Size" },
-   { CI_SREC_DEV_NOT_CONFIGURED,    "Device not configured" },
-   { CI_SREC_UNSUPPORTED_RPI,       "RPI not supported" },
-   { CI_SREC_NO_MORE_CONNS,         "Connection Manager cannot support any more connections" },
-   { CI_SREC_VEN_OR_PCODE_MISMATCH, "The Vendor Id or Product Code did not match the device" },
-   { CI_SREC_PRODTYPE_MISMATCH,     "The Product Type did not match the device" },
-   { CI_SREC_REVISION_MISMATCH,     "The Major or Minor Revision did not match the device" },
-   { CI_SREC_BAD_CONN_POINT,        "Invalid Connection Point" },
-   { CI_SREC_INVAL_CONFIG_FRMT,     "Invalid Configuration Format" },
-   { CI_SREC_NO_CONTROL_CONN,       "There is no controlling connection currently open" },
-   { CI_SREC_NO_MORE_CONN_SUPPORT,  "Target Application cannot support any more connections" },
-   { CI_SREC_RPI_SMALLERTHAN_PIT,   "RPI is smaller than the Production Inhibit Time" },
-   { CI_SREC_CONN_ALREADY_CLOSED,   "Connection cannot be closed: connection has timed out" },
-   { CI_SREC_UNCONN_SND_TIMEOUT,    "Unconnected Send timed out" },
-   { CI_SREC_UNCONN_PARM_ERR,       "Parameter Error in Unconnected Send Service" },
-   { CI_SREC_UCONN_TOO_LARGE,       "Message too large for Unconnected message service" },
-   { CI_SREC_UCONN_ACK_NO_REP,      "Unconnected acknowledge without reply" },
-   { CI_SREC_NO_MEMORY,             "No buffer memory available" },
-   { CI_SREC_NO_NET_BANDWIDTH,      "Network Bandwidth not available for data" },
-   { CI_SREC_NO_SCREENERS,          "No screeners available" },
-   { CI_SREC_NO_REALTIME_CONFIG,    "Not Configured to send real-time data" },
-   { CI_SREC_INVALID_PORT,          "Port specified in Port Segment Not Available" },
-   { CI_SREC_LINKADDR_NOT_AVAIL,    "Link Address specified in Port Segment Not Available" },
-   { CI_SREC_INVALID_SEGMENT_TYP,   "Invalid Segment Type or Segment Value in Path" },
-   { CI_SREC_CLOSE_PATH_ERR,        "Error in close path" },
-   { CI_SREC_NO_SCHED,              "Scheduling not specified" },
-   { CI_SREC_INVALID_LINK_ADDR,     "Link Address to Self Invalid" },
-   { CI_SREC_UNAVAIL_RESOURCE,      "Resources on Secondary Unavailable" },
-   { CI_SREC_CONN_ALREADY_ESTAB,    "Connection already established" },
-   { CI_SREC_DCONN_ALREADY_ESTAB,   "Direct connection already established" },
-   { CI_SREC_MISC,                  "Miscellaneous" },
-   { CI_SREC_REDUNDANT_MISMATCH,    "Redundant connection mismatch" },
-   { CI_SREC_NO_CONSUME_RESRC,      "No more consumer resources available in the producing module" },
-   { CI_SREC_NO_CONN_RESRC,         "No connection resources exist for target path" },
-
-       { 0,                                           NULL }
-} ;
-
 /* Translate Vendor ID:s */
 const value_string cip_vendor_vals[] = {
    VENDOR_ID_LIST
 
-       { 0, NULL }
+   { 0, NULL }
 };
 
 /* Translate Device Profile:s */
@@ -368,64 +403,130 @@ const value_string cip_devtype_vals[] = {
    { 0, NULL }
 };
 
+#define CI_CLS_MR   0x02    /* Message Router */
+#define CI_CLS_CM   0x06    /* Connection Manager */
+#define CI_CLS_CCO  0xF3    /* Connection Configuration Object */
+
 /* Translate class names */
 static const value_string cip_class_names_vals[] = {
-       { 0x01,     "Identity Object"                       },
-       { 0x02,     "Message Router"                        },
-       { 0x03,     "DeviceNet Object"                      },
-       { 0x04,     "Assembly Object"                       },
-       { 0x05,     "Connection Object"                     },
-       { 0x06,     "Connection Manager"                    },
-       { 0x07,     "Register Object"                       },
-       { 0x08,     "Discrete Input Point Object"           },
-       { 0x09,     "Discrete Output Point Object"          },
-       { 0x0A,     "Analog Input Point Object"             },
-       { 0x0B,     "Analog Output Point Object"            },
-       { 0x0E,     "Presence Sensing Object"               },
-       { 0x0F,     "Parameter Object"                      },
-       { 0x10,     "Parameter Group Object"                },
-       { 0x12,     "Group Object"                          },
-       { 0x1D,     "Discrete Input Group Object"           },
-       { 0x1E,     "Discrete Output Group Object"          },
-       { 0x1F,     "Discrete Group Object"                 },
-       { 0x20,     "Analog Input Group Object"             },
-       { 0x21,     "Analog Output Group Object"            },
-       { 0x22,     "Analog Group Object"                   },
-       { 0x23,     "Position Sensor Object"                },
-       { 0x24,     "Position Controller Supervisor Object" },
-       { 0x25,     "Position Controller Object"            },
-       { 0x26,     "Block Sequencer Object"                },
-       { 0x27,     "Command Block Object"                  },
-       { 0x28,     "Motor Data Object"                     },
-       { 0x29,     "Control Supervisor Object"             },
-       { 0x2A,     "AC/DC Drive Object"                    },
-       { 0x2B,     "Acknowledge Handler Object"            },
-       { 0x2C,     "Overload Object"                       },
-       { 0x2D,     "Softstart Object"                      },
-       { 0x2E,     "Selection Object"                      },
-       { 0x30,     "S-Device Supervisor Object"            },
-       { 0x31,     "S-Analog Sensor Object"                },
-       { 0x32,     "S-Analog Actuator Object"              },
-       { 0x33,     "S-Single Stage Controller Object"      },
-       { 0x34,     "S-Gas Calibration Object"              },
-       { 0x35,     "Trip Point Object"                     },
-       { 0x37,     "File Object"                           },
-       { 0x38,     "S-Partial Pressure Object"             },
-       { 0xF0,     "ControlNet Object"                     },
-       { 0xF1,     "ControlNet Keeper Object"              },
-       { 0xF2,     "ControlNet Scheduling Object"          },
-       { 0xF3,     "Connection Configuration Object"       },
-       { 0xF4,     "Port Object"                           },
-       { 0xF5,     "TCP/IP Interface Object"               },
-       { 0xF6,     "EtherNet Link Object"                  },
-
-       { 0,                    NULL                                    }
+   { 0x01,     "Identity Object"                       },
+   { 0x02,     "Message Router"                        },
+   { 0x03,     "DeviceNet Object"                      },
+   { 0x04,     "Assembly Object"                       },
+   { 0x05,     "Connection Object"                     },
+   { 0x06,     "Connection Manager"                    },
+   { 0x07,     "Register Object"                       },
+   { 0x08,     "Discrete Input Point Object"           },
+   { 0x09,     "Discrete Output Point Object"          },
+   { 0x0A,     "Analog Input Point Object"             },
+   { 0x0B,     "Analog Output Point Object"            },
+   { 0x0E,     "Presence Sensing Object"               },
+   { 0x0F,     "Parameter Object"                      },
+   { 0x10,     "Parameter Group Object"                },
+   { 0x12,     "Group Object"                          },
+   { 0x1D,     "Discrete Input Group Object"           },
+   { 0x1E,     "Discrete Output Group Object"          },
+   { 0x1F,     "Discrete Group Object"                 },
+   { 0x20,     "Analog Input Group Object"             },
+   { 0x21,     "Analog Output Group Object"            },
+   { 0x22,     "Analog Group Object"                   },
+   { 0x23,     "Position Sensor Object"                },
+   { 0x24,     "Position Controller Supervisor Object" },
+   { 0x25,     "Position Controller Object"            },
+   { 0x26,     "Block Sequencer Object"                },
+   { 0x27,     "Command Block Object"                  },
+   { 0x28,     "Motor Data Object"                     },
+   { 0x29,     "Control Supervisor Object"             },
+   { 0x2A,     "AC/DC Drive Object"                    },
+   { 0x2B,     "Acknowledge Handler Object"            },
+   { 0x2C,     "Overload Object"                       },
+   { 0x2D,     "Softstart Object"                      },
+   { 0x2E,     "Selection Object"                      },
+   { 0x30,     "S-Device Supervisor Object"            },
+   { 0x31,     "S-Analog Sensor Object"                },
+   { 0x32,     "S-Analog Actuator Object"              },
+   { 0x33,     "S-Single Stage Controller Object"      },
+   { 0x34,     "S-Gas Calibration Object"              },
+   { 0x35,     "Trip Point Object"                     },
+   { 0x37,     "File Object"                           },
+   { 0x38,     "S-Partial Pressure Object"             },
+   { 0xF0,     "ControlNet Object"                     },
+   { 0xF1,     "ControlNet Keeper Object"              },
+   { 0xF2,     "ControlNet Scheduling Object"          },
+   { 0xF3,     "Connection Configuration Object"       },
+   { 0xF4,     "Port Object"                           },
+   { 0xF5,     "TCP/IP Interface Object"               },
+   { 0xF6,     "EtherNet Link Object"                  },
+
+   { 0,        NULL                                    }
 };
 
+static void
+dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, packet_info *pinfo, cip_req_info_t *preq_info );
+
+static proto_item*
+add_byte_array_text_to_proto_tree( proto_tree *tree, tvbuff_t *tvb, gint start, gint length, const char* str )
+{
+  const guint8 *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 = 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() */
+
 
 /* Dissect EPATH */
 static void
-dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_length )
+dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_length, gboolean generate )
 {
    int pathpos, temp_data, temp_data2, seg_size, i, temp_word;
    unsigned char segment_type, opt_link_size;
@@ -434,14 +535,18 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
    proto_tree *e_key_tree, *cia_tree, *ds_tree;
    proto_item *mcpi, *port_item, *net_item;
    proto_tree *mc_tree;
+   proto_item *it;
    proto_item *hidden_item;
 
    /* Create a sub tree for the epath */
    path_tree = proto_item_add_subtree( epath_item, ett_path );
 
-   hidden_item = proto_tree_add_item(path_tree, hf_cip_epath,
-                                  tvb, offset, path_length, TRUE );
-   PROTO_ITEM_SET_HIDDEN(hidden_item);
+   if ( !generate )
+   {
+      hidden_item = proto_tree_add_item(path_tree, hf_cip_epath,
+                         tvb, offset, path_length, TRUE );
+      PROTO_ITEM_SET_HIDDEN(hidden_item);
+   }
 
    pathpos = 0;
 
@@ -457,24 +562,55 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
       case CI_PORT_SEGMENT:
 
          port_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 0, "Port Segment" );
+         if ( generate )
+         {
+            port_item = proto_tree_add_text( path_tree, NULL, 0, 0, "Port Segment" );
+            PROTO_ITEM_SET_GENERATED(port_item);
+         }
+         else
+            port_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 0, "Port Segment" );
          port_tree = proto_item_add_subtree( port_item, ett_port_path );
 
          /* Add port number */
-         proto_tree_add_item( port_tree, hf_cip_port, tvb, offset + pathpos, 1, TRUE );
+         if ( generate )
+         {
+            it = proto_tree_add_uint(port_tree, hf_cip_port, NULL, 0, 0, ( segment_type & 0x0F ) );
+            PROTO_ITEM_SET_GENERATED(it);
+         }
+         else
+            proto_tree_add_item( port_tree, hf_cip_port, tvb, offset + pathpos, 1, TRUE );
          proto_item_append_text( epath_item, "Port: %d", ( segment_type & 0x0F ) );
          proto_item_append_text( port_item, ": Port: %d", ( segment_type & 0x0F ) );
 
          if( segment_type & 0x10 )
          {
             /* Add Extended Link Address flag */
-            proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: TRUE" );
+            if ( generate )
+            {
+               it = proto_tree_add_text( port_tree, NULL, 0, 0, "Extended Link Address: TRUE" );
+               PROTO_ITEM_SET_GENERATED(it);
+            }
+            else
+               it = proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: TRUE" );
 
             /* Add size of extended link address */
             opt_link_size = tvb_get_guint8( tvb, offset + pathpos + 1 );
-            proto_tree_add_text( port_tree, tvb, offset+pathpos+1, 1, "Link Address Size: %d", opt_link_size  );
+            if ( generate )
+            {
+               it = proto_tree_add_text( port_tree, NULL, 0, 0, "Link Address Size: %d", opt_link_size  );
+               PROTO_ITEM_SET_GENERATED(it);
+            }
+            else
+               it = proto_tree_add_text( port_tree, tvb, offset+pathpos+1, 1, "Link Address Size: %d", opt_link_size  );
 
             /* Add extended link address */
-            proto_tree_add_item( port_tree, hf_cip_link_address_string, tvb, offset+pathpos+2, opt_link_size, FALSE );
+            if ( generate )
+            {
+               it = proto_tree_add_string(port_tree, hf_cip_link_address_string, NULL, 0, 0, tvb_format_text(tvb, offset+pathpos+2, opt_link_size) );
+               PROTO_ITEM_SET_GENERATED(it);
+            }
+            else
+               proto_tree_add_item( port_tree, hf_cip_link_address_string, tvb, offset+pathpos+2, opt_link_size, FALSE );
             proto_item_append_text( epath_item, ", Address: %s", tvb_format_text(tvb, offset+pathpos+2, opt_link_size) );
             proto_item_append_text( port_item,  ", Address: %s", tvb_format_text(tvb, offset+pathpos+2, opt_link_size) );
 
@@ -493,10 +629,22 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
          else
          {
             /* Add Extended Link Address flag */
-            proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: FALSE" );
+            if ( generate )
+            {
+               it = proto_tree_add_text( port_tree, NULL, 0, 0, "Extended Link Address: FALSE" );
+               PROTO_ITEM_SET_GENERATED(it);
+            }
+            else
+               it = proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: FALSE" );
 
             /* Add Link Address */
-            proto_tree_add_item( port_tree, hf_cip_link_address_byte, tvb, offset+pathpos+1, 1, FALSE );
+            if ( generate )
+            {
+               it = proto_tree_add_uint(port_tree, hf_cip_link_address_byte, NULL, 0, 0, tvb_get_guint8( tvb, offset + pathpos + 1 ) );
+               PROTO_ITEM_SET_GENERATED(it);
+            }
+            else
+               proto_tree_add_item( port_tree, hf_cip_link_address_byte, tvb, offset+pathpos+1, 1, FALSE );
             proto_item_append_text( epath_item, ", Address: %d",tvb_get_guint8( tvb, offset + pathpos + 1 ) );
             proto_item_append_text( port_item,  ", Address: %d",tvb_get_guint8( tvb, offset + pathpos + 1 ) );
 
@@ -516,16 +664,28 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
 
             /* Logical Class ID, do a format check */
 
-                  if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
-                  {
-                     temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Class Segment (0x%02X)", segment_type );
+            if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+            {
+               temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "8-Bit Logical Class Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Class Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the class */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 8-bit class number */
-               proto_tree_add_item( cia_tree, hf_cip_class8, tvb, offset + pathpos + 1, 1, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_uint(cia_tree, hf_cip_class8, NULL, 0, 0, temp_data );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_class8, tvb, offset + pathpos + 1, 1, TRUE );
                proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%02X" ) );
 
                /* 2 bytes of path used */
@@ -534,13 +694,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT )
             {
                temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Class Segment (0x%02X)", segment_type );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "16-Bit Logical Class Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Class Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the class */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 16-bit class number */
-               proto_tree_add_item( cia_tree, hf_cip_class16, tvb, offset + pathpos + 2, 2, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_uint(cia_tree, hf_cip_class16, NULL, 0, 0, temp_data );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_class16, tvb, offset + pathpos + 2, 2, TRUE );
                proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%04X" ) );
 
                /* 4 bytes of path used */
@@ -549,13 +721,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT )
             {
                temp_data = tvb_get_letohl( tvb, offset + pathpos + 2 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the class */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 32-bit class number */
-               proto_tree_add_item( cia_tree, hf_cip_class32, tvb, offset + pathpos + 2, 4, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_uint(cia_tree, hf_cip_class32, NULL, 0, 0, temp_data );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_class32, tvb, offset + pathpos + 2, 4, TRUE );
                proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%08X" ) );
 
                /* 6 bytes of path used */
@@ -574,16 +758,28 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
 
             /* Logical Instance ID, do a format check */
 
-                  if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
-                  {
-                     temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Instance Segment (0x%02X)", segment_type );
+            if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+            {
+               temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "8-Bit Logical Instance Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Instance Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the instance */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 8-bit instance number */
-               proto_tree_add_item( cia_tree, hf_cip_instance8, tvb, offset + pathpos + 1, 1, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_uint(cia_tree, hf_cip_instance8, NULL, 0, 0, temp_data );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_instance8, tvb, offset + pathpos + 1, 1, TRUE );
                proto_item_append_text( epath_item, "Instance: 0x%02X", temp_data );
 
                /* 2 bytes of path used */
@@ -592,13 +788,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT )
             {
                temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Instance Segment (0x%02X)", segment_type );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "16-Bit Logical Instance Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Instance Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the instance */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 16-bit instance number */
-               proto_tree_add_item( cia_tree, hf_cip_instance16, tvb, offset + pathpos + 2, 2, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_uint(cia_tree, hf_cip_instance16, NULL, 0, 0, temp_data );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_instance16, tvb, offset + pathpos + 2, 2, TRUE );
                proto_item_append_text( epath_item, "Instance: 0x%04X", temp_data );
 
                /* 4 bytes of path used */
@@ -607,13 +815,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT )
             {
                temp_data = tvb_get_letohl( tvb, offset + pathpos + 2 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Instance Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the instance */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 32-bit instance number */
-               proto_tree_add_item( cia_tree, hf_cip_instance32, tvb, offset + pathpos + 2, 4, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_uint(cia_tree, hf_cip_instance32, NULL, 0, 0, temp_data );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_instance32, tvb, offset + pathpos + 2, 4, TRUE );
                proto_item_append_text( epath_item, "Instance: 0x%08X", temp_data );
 
 
@@ -629,20 +849,125 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             break;
 
 
+         case CI_LOGICAL_SEG_MBR_ID:
+
+            /* Logical Member ID, do a format check */
+
+            if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+            {
+               temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "8-Bit Logical Member Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Member Segment (0x%02X)", segment_type );
+
+               /* Create a sub tree for the attribute */
+               cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
+
+               /* Display the 8-bit attribute number */
+               if ( generate )
+               {
+                  it = proto_tree_add_item( cia_tree, hf_cip_member8, NULL, 0, 0, TRUE );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_member8, tvb, offset + pathpos + 1, 1, TRUE );
+               proto_item_append_text( epath_item, "Member: 0x%02X", temp_data );
+
+               /* 2 bytes of path used */
+               pathpos += 2;
+            }
+            else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT )
+            {
+               temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "16-Bit Logical Member Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Member Segment (0x%02X)", segment_type );
+
+               /* Create a sub tree for the attribute */
+               cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
+
+               /* Display the 16-bit attribute number */
+               if ( generate )
+               {
+                  it = proto_tree_add_item( cia_tree, hf_cip_member16, NULL, 0, 0, TRUE );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_member16, tvb, offset + pathpos + 2, 2, TRUE );
+               proto_item_append_text( epath_item, "Member: 0x%04X", temp_data );
+
+               /* 4 bytes of path used */
+               pathpos += 4;
+            }
+            else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT )
+            {
+               temp_data = tvb_get_letohl( tvb, offset + pathpos + 2 );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "32-Bit Logical Member Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Member Segment (0x%02X)", segment_type );
+
+               /* Create a sub tree for the attribute */
+               cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
+
+               /* Display the 32-bit attribute number */
+               if ( generate )
+               {
+                  it = proto_tree_add_item( cia_tree, hf_cip_member32, NULL, 0, 0, TRUE );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_member32, tvb, offset + pathpos + 2, 4, TRUE );
+               proto_item_append_text( epath_item, "Member: 0x%08X", temp_data );
+
+               /* 6 bytes of path used */
+               pathpos += 6;
+            }
+            else
+            {
+               /* Unsupported logical segment format */
+               proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Logical Segment Format" );
+               return;
+            }
+            break;
+
          case CI_LOGICAL_SEG_ATTR_ID:
 
             /* Logical Attribute ID, do a format check */
 
-                  if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
-                  {
-                     temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Attribute Segment (0x%02X)", segment_type );
+            if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+            {
+               temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "8-Bit Logical Attribute Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Attribute Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the attribute */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 8-bit attribute number */
-               proto_tree_add_item( cia_tree, hf_cip_attribute8, tvb, offset + pathpos + 1, 1, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_item( cia_tree, hf_cip_attribute8, NULL, 0, 0, TRUE );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_attribute8, tvb, offset + pathpos + 1, 1, TRUE );
                proto_item_append_text( epath_item, "Attribute: 0x%02X", temp_data );
 
                /* 2 bytes of path used */
@@ -651,13 +976,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_16_BIT )
             {
                temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Attribute Segment (0x%02X)", segment_type );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "16-Bit Logical Attribute Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Attribute Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the attribute */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 16-bit attribute number */
-               proto_tree_add_item( cia_tree, hf_cip_attribute16, tvb, offset + pathpos + 2, 2, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_item( cia_tree, hf_cip_attribute16, NULL, 0, 0, TRUE );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_attribute16, tvb, offset + pathpos + 2, 2, TRUE );
                proto_item_append_text( epath_item, "Attribute: 0x%04X", temp_data );
 
                /* 4 bytes of path used */
@@ -666,13 +1003,25 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             else if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_32_BIT )
             {
                temp_data = tvb_get_letohl( tvb, offset + pathpos + 2 );
-               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Attribute Segment (0x%02X)", segment_type );
+               if ( generate )
+               {
+                  cia_item = proto_tree_add_text( path_tree, NULL, 0, 0, "32-Bit Logical Attribute Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(cia_item);
+               }
+               else
+                  cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Attribute Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the attribute */
                cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );
 
                /* Display the 32-bit attribute number */
-               proto_tree_add_item( cia_tree, hf_cip_attribute32, tvb, offset + pathpos + 2, 4, TRUE );
+               if ( generate )
+               {
+                  it = proto_tree_add_item( cia_tree, hf_cip_attribute32, NULL, 0, 0, TRUE );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_item( cia_tree, hf_cip_attribute32, tvb, offset + pathpos + 2, 4, TRUE );
                proto_item_append_text( epath_item, "Attribute: 0x%08X", temp_data );
 
                /* 6 bytes of path used */
@@ -691,9 +1040,9 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
 
             /* Logical Connection point , do a format check */
 
-                  if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
-                  {
-                     temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
+            if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_8_BIT )
+            {
+               temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );
                cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "8-Bit Logical Connection Point Segment (0x%02X)", segment_type );
 
                /* Create a sub tree for the connection point */
@@ -765,37 +1114,37 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
                   proto_tree_add_text( e_key_tree, tvb, offset + pathpos + 1, 1, "Key Format: 0x%02X", temp_data );
 
                   /* Get the Vendor ID */
-                     temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
+                  temp_data = tvb_get_letohs( tvb, offset + pathpos + 2 );
                   proto_tree_add_item( e_key_tree, hf_cip_vendor, tvb, offset + pathpos + 2, 2, TRUE);
                   proto_item_append_text( qi, "VendorID: 0x%04X", temp_data );
 
                   /* Get Device Type */
-                        temp_data = tvb_get_letohs( tvb, offset + pathpos + 4 );
-                        proto_tree_add_item( e_key_tree, hf_cip_devtype, tvb, offset + pathpos + 4, 2, TRUE);
+                  temp_data = tvb_get_letohs( tvb, offset + pathpos + 4 );
+                  proto_tree_add_item( e_key_tree, hf_cip_devtype, tvb, offset + pathpos + 4, 2, TRUE);
                   proto_item_append_text( qi, ", DevTyp: 0x%04X", temp_data );
 
                   /* Product Code */
-                        temp_data = tvb_get_letohs( tvb, offset + pathpos + 6 );
+                  temp_data = tvb_get_letohs( tvb, offset + pathpos + 6 );
                   proto_tree_add_text( e_key_tree, tvb, offset + pathpos + 6, 2, "Product Code: 0x%04X", temp_data );
 
                   /* Major revision/Compatibility */
-                        temp_data = tvb_get_guint8( tvb, offset + pathpos + 8 );
+                  temp_data = tvb_get_guint8( tvb, offset + pathpos + 8 );
 
-                                       /* Add Major revision/Compatibility tree */
-                                       mcpi = proto_tree_add_text(e_key_tree, tvb, offset + pathpos + 8, 1, "Compatibility ");
-                                       mc_tree = proto_item_add_subtree(mcpi, ett_mcsc);
+                  /* Add Major revision/Compatibility tree */
+                  mcpi = proto_tree_add_text(e_key_tree, tvb, offset + pathpos + 8, 1, "Compatibility ");
+                  mc_tree = proto_item_add_subtree(mcpi, ett_mcsc);
 
-                                       /* Add Compatibility bit info */
+                  /* Add Compatibility bit info */
                   proto_tree_add_item(mc_tree, hf_cip_fwo_comp,
-                                                       tvb, offset + pathpos + 8, 1, TRUE );
+                        tvb, offset + pathpos + 8, 1, TRUE );
 
                   proto_item_append_text( mcpi, "%s, Major Revision: %d",
                               val_to_str( ( temp_data & 0x80 )>>7, cip_com_bit_vals , "" ),
                               temp_data & 0x7F );
 
-                                       /* Major revision */
-                                       proto_tree_add_item(mc_tree, hf_cip_fwo_mrev,
-                                                       tvb, offset + pathpos + 8, 1, TRUE );
+                  /* Major revision */
+                  proto_tree_add_item(mc_tree, hf_cip_fwo_mrev,
+                        tvb, offset + pathpos + 8, 1, TRUE );
 
                   /* Minor revision */
                   temp_data2 = tvb_get_guint8( tvb, offset + pathpos + 9 );
@@ -842,7 +1191,13 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             case CI_DATA_SEG_SIMPLE:
 
                /* Simple data segment */
-               ds_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 1, "Simple Data Segment (0x%02X)", segment_type );
+               if ( generate )
+               {
+                  ds_item = proto_tree_add_text( path_tree, NULL, 0, 0, "Simple Data Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(ds_item);
+               }
+               else
+                  ds_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 1, "Simple Data Segment (0x%02X)", segment_type );
 
                /* Create a sub tree */
                ds_tree = proto_item_add_subtree( ds_item, ett_data_seg );
@@ -875,36 +1230,57 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
             case CI_DATA_SEG_SYMBOL:
 
                /* ANSI extended symbol segment */
-               ds_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 1, "Extended Symbol Segment (0x%02X)", segment_type );
+               if ( generate )
+               {
+                  ds_item = proto_tree_add_text( path_tree, NULL, 0, 0, "Extended Symbol Segment (0x%02X)", segment_type );
+                  PROTO_ITEM_SET_GENERATED(ds_item);
+               }
+               else
+                  ds_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 1, "Extended Symbol Segment (0x%02X)", segment_type );
 
                /* Create a sub tree */
                ds_tree = proto_item_add_subtree( ds_item, ett_data_seg );
 
                /* Segment size */
                seg_size = tvb_get_guint8( tvb, offset + pathpos+1 );
-               proto_tree_add_text( ds_tree, tvb, offset + pathpos+1, 1, "Data Size: %d", seg_size );
+               if ( generate )
+               {
+                  it = proto_tree_add_text( ds_tree, NULL, 0, 0, "Data Size: %d", seg_size );
+                  PROTO_ITEM_SET_GENERATED(it);
+               }
+               else
+                  proto_tree_add_text( ds_tree, tvb, offset + pathpos+1, 1, "Data Size: %d", seg_size );
 
                /* Segment data  */
                if( seg_size != 0 )
                {
-                  qi = proto_tree_add_text( ds_tree, tvb, offset + pathpos + 2, seg_size, "Data: %s",
-                        tvb_format_text(tvb, offset + pathpos + 2, seg_size ) );
+                  if ( generate )
+                  {
+                     qi = proto_tree_add_text( ds_tree, NULL, 0, 0, "Data: %s",
+                           tvb_format_text(tvb, offset + pathpos + 2, seg_size ) );
+                     PROTO_ITEM_SET_GENERATED(qi);
+                  }
+                  else
+                     qi = proto_tree_add_text( ds_tree, tvb, offset + pathpos + 2, seg_size, "Data: %s",
+                           tvb_format_text(tvb, offset + pathpos + 2, seg_size ) );
 
                   proto_item_append_text(epath_item, "%s", tvb_format_text(tvb, offset + pathpos + 2, seg_size ) );
+
                   hidden_item = proto_tree_add_item( ds_tree, hf_cip_symbol, tvb, offset + pathpos + 2, seg_size, FALSE );
                   PROTO_ITEM_SET_HIDDEN(hidden_item);
 
                   if( seg_size %2 )
                   {
                      /* We have a PAD BYTE */
-                     proto_tree_add_text( ds_tree, tvb, offset + pathpos + 2 + seg_size, 1, "Pad Byte (0x%02X)",
-                         tvb_get_guint8( tvb, offset + pathpos + 2 + seg_size ) );
-                     pathpos++;
+                     if ( !generate )
+                        proto_tree_add_text( ds_tree, tvb, offset + pathpos + 2 + seg_size, 1, "Pad Byte (0x%02X)",
+                            tvb_get_guint8( tvb, offset + pathpos + 2 + seg_size ) );
                      seg_size++;
                   }
                }
 
-               proto_item_set_len( ds_item, 2 + seg_size );
+               if ( !generate )
+                  proto_item_set_len( ds_item, 2 + seg_size );
                pathpos = pathpos + 2 + seg_size;
 
                break;
@@ -977,209 +1353,187 @@ dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_lengt
 
 } /* end of dissect_epath() */
 
+/************************************************
+ *
+ * Dissector for generic CIP object
+ *
+ ************************************************/
 
 static void
-dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+dissect_cip_generic_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo, proto_item *ti )
 {
-   proto_item *pi, *rrsc_item, *ncppi, *ar_item, *temp_item, *temp_item2, *status_item;
-   proto_tree *temp_tree, *rrsc_tree, *ncp_tree, *cmd_data_tree, *status_tree;
-   int req_path_size, conn_path_size, temp_data;
-   unsigned char gen_status;
+   proto_item *pi, *temp_item;
+   proto_tree *cmd_data_tree;
+   int req_path_size;
    unsigned char add_stat_size;
-   unsigned char temp_byte, route_path_size;
-   unsigned char app_rep_size, i, collision;
-   int msg_req_siz, num_services, serv_offset;
-
-   /* Add Service code & Request/Response tree */
-   rrsc_item = proto_tree_add_text( item_tree, tvb, offset, 1, "Service: " );
-   rrsc_tree = proto_item_add_subtree( rrsc_item, ett_rrsc );
+   unsigned char i;
 
-   /* Add Request/Response */
-   proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
 
-   /* watch for service collisions with CCO */
-   temp_byte = tvb_get_guint8( tvb, offset );
-   collision = 0;
-   if ( SC_STOP_CONN == temp_byte || SC_AUDIT_CHANGE == temp_byte )
+   if( tvb_get_guint8( tvb, offset ) & 0x80 )
    {
-      /* check for CCO object in path... */
-      temp_data = tvb_get_guint8( tvb, offset+3 );
-      /* F3 is the CCO */
-      if ( temp_data == 0xF3 )
-      {
-         collision = 1;
-         proto_item_append_text( rrsc_item, "%s (%s)",
-                     val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
-                        cip_sc_vals_cco , "Unknown Service (%x)"),
-                     val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
-                        cip_sc_rr, "") );
-      }
-   }
+      /* Response message */
+      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
 
-   if (!collision)
-   {
-      proto_item_append_text( rrsc_item, "%s (%s)",
-                  val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
-                     cip_sc_vals , "Unknown Service (%x)"),
-                  val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
-                     cip_sc_rr, "") );
-   }
+      /* If there is any command specific data create a sub-tree for it */
+      if( ( item_length-4-add_stat_size ) != 0 )
+      {
+         pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific Data" );
+         cmd_data_tree = proto_item_add_subtree( pi, ett_cmd_data );
 
-   /* Add Service code */
-   proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
+         /* Add data */
+         add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+      }
+      else
+      {
+         PROTO_ITEM_SET_HIDDEN( ti );
+      }
 
-   if( tvb_get_guint8( tvb, offset ) & 0x80 )
+   } /* End of if reply */
+   else
    {
-      /* Response message */
-      status_item = proto_tree_add_text( item_tree, tvb, offset+2, 1, "Status: " );
-      status_tree = proto_item_add_subtree( status_item, ett_status_item );
-
-      /* Add general status */
-      gen_status = tvb_get_guint8( tvb, offset+2 );
-      proto_tree_add_item(status_tree, hf_cip_genstat, tvb, offset+2, 1, TRUE );
-      proto_item_append_text( status_item, "%s", val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
-                     cip_gs_vals , "Unknown Response (%x)")   );
+      /* Request message */
 
-      /* Add reply status to info column */
+      /* Add service to info column */
       if(check_col(pinfo->cinfo, COL_INFO))
       {
          col_append_str( pinfo->cinfo, COL_INFO,
-                  val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
-                     cip_gs_vals , "Unknown Response (%x)") );
+                  val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+                     cip_sc_vals , "Unknown Service (%x)") );
       }
 
-      /* Add additional status size */
-      proto_tree_add_text( status_tree, tvb, offset+3, 1, "Additional Status Size: %d (word)",
-         tvb_get_guint8( tvb, offset+3 ) );
-
-      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+      req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
 
-      if( add_stat_size )
+      /* If there is any command specific data creat a sub-tree for it */
+      if( (item_length-req_path_size-2) != 0 )
       {
-         proto_item_append_text( status_item, ", Extended:" );
 
-         /* Add additional status */
-         pi = proto_tree_add_text( status_tree, tvb, offset+4, add_stat_size, "Additional Status:" );
+         pi = proto_tree_add_text( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Command Specific Data" );
+         cmd_data_tree = proto_item_add_subtree( pi, ett_cmd_data );
 
-         for( i=0; i < add_stat_size/2; i ++ )
+         /* Check what service code that recived */
+
+         if( tvb_get_guint8( tvb, offset ) == SC_GET_ATT_LIST )
          {
-            proto_item_append_text( pi, " %s", val_to_str( ( tvb_get_letohs( tvb, offset+4+(i*2) ) ),
-                                   cip_es_vals , "Unknown Status (%x)")   );
-            proto_item_append_text( pi, " (0x%04X)", tvb_get_letohs( tvb, offset+4+(i*2) ) );
-            proto_item_append_text( status_item, " 0x%04X", tvb_get_letohs( tvb, offset+4+(i*2) ) );
-         }
-      }
+            /* Get attribute list request */
 
-      proto_item_set_len( status_item, 2 + add_stat_size );
+            int att_count;
 
-      /* If there is any command specific data create a sub-tree for it */
-      if( ( item_length-4-add_stat_size ) != 0 )
-      {
-         pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific data" );
-         cmd_data_tree = proto_item_add_subtree( pi, ett_cmd_data );
+            /* Add number of services */
+            att_count = tvb_get_letohs( tvb, offset+2+req_path_size );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Attribute Count: %d", att_count );
 
-         if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
-         {
-            /* Success responses */
+            /* Add Attribute List */
+            temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, att_count*2, "Attribute List: " );
 
-            if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_OPEN )
+            for( i=0; i < att_count; i++ )
             {
-               /* Forward open Response (Success) */
-
-               /* Display originator to target connection ID */
-               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 4, "O->T Network Connection ID: 0x%08X", temp_data );
+               if( i == (att_count-1) )
+                  proto_item_append_text(temp_item, "%d",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+               else
+                  proto_item_append_text(temp_item, "%d, ",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+            }
 
-               /* Display target to originator connection ID */
-               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "T->O Network Connection ID: 0x%08X", temp_data );
+         } /* End of Get attribute list request */
+         else
+         {
+            /* Add data */
+            add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " );
+         } /* End of check service code */
 
-               /* Display connection serial number */
-               temp_data = tvb_get_letohs( tvb, offset+4+add_stat_size+8 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 2, "Connection Serial Number: 0x%04X", temp_data );
+      }
+      else
+      {
+         PROTO_ITEM_SET_HIDDEN( ti );
+      } /* End of if command-specific data present */
 
-               /* Display the originator vendor id */
-               proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+10, 2, TRUE);
+   } /* End of if-else( request ) */
 
-               /* Display the originator serial number */
-               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+12 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+12, 4, "Originator Serial Number: 0x%08X", temp_data );
+} /* End of dissect_cip_generic_data() */
 
-               /* Display originator to target actual packet interval */
-               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+16 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+16, 4, "O->T API: %dms (0x%08X)", temp_data / 1000, temp_data );
+static int
+dissect_cip_class_generic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+   proto_item *ti;
+   proto_tree *class_tree;
 
-               /* Display originator to target actual packet interval */
-               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+20 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+20, 4, "T->O API: %dms (0x%08X)", temp_data / 1000, temp_data );
+   if( tree )
+   {
+      /* Create display subtree for the protocol */
+      ti = proto_tree_add_item(tree, proto_cip_class_generic, tvb, 0, -1, FALSE);
+      class_tree = proto_item_add_subtree( ti, ett_cip_class_generic );
 
-               /* Display the application reply size */
-               app_rep_size = tvb_get_guint8( tvb, offset+4+add_stat_size+24 ) * 2;
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+24, 1, "Application Reply Size: %d (words)", app_rep_size / 2 );
+      dissect_cip_generic_data( class_tree, tvb, 0, tvb_length(tvb), pinfo, ti );
+   }
 
-               /* Display the Reserved byte */
-               temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+25 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+25, 1, "Reserved: 0x%02X", temp_byte );
+   return tvb_length(tvb);
+}
 
-               if( app_rep_size != 0 )
-               {
-                  /* Display application Reply data */
-                  ar_item = proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+26, app_rep_size, "Application Reply:" );
+/************************************************
+ *
+ * Dissector for CIP Message Router
+ *
+ ************************************************/
 
-                  for( i=0; i < app_rep_size; i++ )
-                  {
-                    temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+26+i );
-                    proto_item_append_text(ar_item, " 0x%02X", temp_byte );
-                  }
+static void
+dissect_cip_mr_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+{
 
-               } /* End of if reply data */
+typedef struct mr_mult_req_info {
+   guint8 service;
+   int num_services;
+   cip_req_info_t *requests;
+} mr_mult_req_info_t;
 
-            } /* End of if forward open response */
-            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_CLOSE )
-            {
-               /* Forward close response (Success) */
+   proto_item *pi, *rrsc_item, *temp_item, *temp_item2;
+   proto_tree *temp_tree, *rrsc_tree, *cmd_data_tree;
+   int req_path_size;
+   int i;
+   unsigned char gen_status;
+   unsigned char add_stat_size;
+   int num_services, serv_offset;
+   unsigned char service;
+   mr_mult_req_info_t *mr_mult_req_info;
+   cip_req_info_t *mr_single_req_info;
+   cip_req_info_t *cip_req_info;
 
-               /* Display connection serial number */
-               temp_data = tvb_get_letohs( tvb, offset+4+add_stat_size );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Connection Serial Number: 0x%04X", temp_data );
+   if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
+      col_set_str( pinfo->cinfo, COL_PROTOCOL, "CIP MR" );
 
-               /* Display the originator vendor id */
-               proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+2, 2, TRUE);
+   /* Add Service code & Request/Response tree */
+   rrsc_item = proto_tree_add_text( item_tree, tvb, offset, 1, "Service: " );
+   rrsc_tree = proto_item_add_subtree( rrsc_item, ett_mr_rrsc );
 
-               /* Display the originator serial number */
-               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "Originator Serial Number: 0x%08X", temp_data );
+   /* Add Request/Response */
+   proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
 
-               /* Display the application reply size */
-               app_rep_size = tvb_get_guint8( tvb, offset+4+add_stat_size+8 ) * 2;
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 1, "Application Reply Size: %d (words)", app_rep_size / 2 );
+   /* watch for service collisions */
+   service = tvb_get_guint8( tvb, offset );
 
-               /* Display the Reserved byte */
-               temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+9 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+9, 1, "Reserved: 0x%02X", temp_byte );
+   proto_item_append_text( rrsc_item, "%s (%s)",
+               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+                  cip_sc_vals_mr , "Unknown Service (%x)"),
+               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
+                  cip_sc_rr, "") );
 
-               if( app_rep_size != 0 )
-               {
-                  /* Display application Reply data */
-                  ar_item = proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+10, app_rep_size, "Application Reply:" );
+   /* Add Service code */
+   proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
 
-                  for( i=0; i < app_rep_size; i ++ )
-                  {
-                     temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+10+i );
-                     proto_item_append_text(ar_item, " 0x%02X", temp_byte );
-                  }
+   if( tvb_get_guint8( tvb, offset ) & 0x80 )
+   {
+      gen_status = tvb_get_guint8( tvb, offset+2 );
+      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
 
-               } /* End of if reply data */
+      /* If there is any command specific data create a sub-tree for it */
+      if( ( item_length-4-add_stat_size ) != 0 )
+      {
+         pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific Data" );
+         cmd_data_tree = proto_item_add_subtree( pi, ett_mr_cmd_data );
 
-            } /* End of if forward close response */
-            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_UNCON_SEND )
-            {
-               /* Unconnected send response (Success) */
+         if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
+         {
+           /* Success responses */
 
-               /* Display service response data */
-               proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, FALSE);
-            }
-            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_MULT_SERV_PACK )
+            if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_MULT_SERV_PACK )
             {
                /* Multiple Service Reply (Success)*/
 
@@ -1190,9 +1544,24 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
                /* Add replies */
                temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+add_stat_size+4, num_services*2, "Offsets: " );
 
+               cip_req_info = (cip_req_info_t*)p_get_proto_data( pinfo->fd, proto_cip );
+               mr_mult_req_info = NULL;
+               if ( cip_req_info )
+               {
+                  mr_mult_req_info = (mr_mult_req_info_t*)cip_req_info->pData;
+
+                  if (  mr_mult_req_info
+                     && (  mr_mult_req_info->service != SC_MULT_SERV_PACK
+                        || mr_mult_req_info->num_services != num_services
+                        )
+                     )
+                     mr_mult_req_info = NULL;
+               }
+
                for( i=0; i < num_services; i++ )
                {
                   int serv_length;
+                  tvbuff_t *next_tvb;
 
                   serv_offset = tvb_get_letohs( tvb, offset+6+add_stat_size+(i*2) );
 
@@ -1209,7 +1578,7 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
                   }
 
                   temp_item2 = proto_tree_add_text( cmd_data_tree, tvb, offset+serv_offset+4, serv_length, "Service Reply #%d", i+1 );
-                  temp_tree = proto_item_add_subtree( temp_item2, ett_mult_ser );
+                  temp_tree = proto_item_add_subtree( temp_item2, ett_mr_mult_ser );
 
                   /*
                   ** We call our selves again to disect embedded packet
@@ -1218,7 +1587,16 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
                   if(check_col(pinfo->cinfo, COL_INFO))
                      col_append_str( pinfo->cinfo, COL_INFO, ", ");
 
-                  dissect_cip_data( temp_tree, tvb, offset+serv_offset+4, serv_length, pinfo );
+                  next_tvb = tvb_new_subset(tvb, offset+serv_offset+4, serv_length, serv_length);
+                  if ( mr_mult_req_info )
+                  {
+                     mr_single_req_info = mr_mult_req_info->requests + i;
+                     dissect_cip_data( temp_tree, next_tvb, 0, pinfo, mr_single_req_info );
+                  }
+                  else
+                  {
+                     dissect_cip_data( temp_tree, next_tvb, 0, pinfo, NULL );
+                  }
                }
             } /* End if Multiple service Packet */
             else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_GET_ATT_LIST )
@@ -1232,12 +1610,13 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
                proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Attribute Count: %d", att_count );
 
                /* Add the data */
-               proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+6+add_stat_size, item_length-6-add_stat_size, FALSE);
-            } /* End if Multiple service Packet */
+               add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+6+add_stat_size, item_length-6-add_stat_size, "Data: " );
+
+            } /* End if GetAttrList */
             else
             {
                /* Add data */
-               proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, FALSE);
+               add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
             } /* end of check service code */
 
          }
@@ -1245,43 +1624,8 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
          {
             /* Error responses */
 
-            if( ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_OPEN ) ||
-                ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_CLOSE ) )
-            {
-               /* Forward open and forward close error response look the same */
-
-               /* Display connection serial number */
-               temp_data = tvb_get_letohs( tvb, offset+4+add_stat_size );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Connection Serial Number: 0x%04X", temp_data );
-
-               /* Display the originator vendor id */
-               proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+2, 2, TRUE);
-
-               /* Display the originator serial number */
-               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "Originator Serial Number: 0x%08X", temp_data );
-
-               /* Display remaining path size */
-               temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size+8 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 1, "Remaining Path Size: %d", temp_data );
-
-               /* Display reserved data */
-               temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size+9 );
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+9, 1, "Reserved: 0x%02X", temp_data );
-            }
-            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_UNCON_SEND )
-            {
-               /* Unconnected send response (Unsuccess) */
-
-               /* Display remaining path size */
-               temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size);
-               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 1, "Remaining Path Size: %d", temp_data );
-            }
-            else
-            {
-               /* Add data */
-               proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, FALSE);
-            }
+            /* Add data */
+            add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
 
          } /* end of if-else( CI_CRC_SUCCESS ) */
 
@@ -1297,88 +1641,520 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
       {
          col_append_str( pinfo->cinfo, COL_INFO,
                   val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
-                     cip_sc_vals , "Unknown Service (%x)") );
+                     cip_sc_vals_mr, "Unknown Service (%x)") );
       }
 
       /* Add path size to tree */
       req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
-      proto_tree_add_text( item_tree, tvb, offset+1, 1, "Request Path Size: %d (words)", req_path_size/2 );
-
-      /* Add the epath */
-      pi = proto_tree_add_text(item_tree, tvb, offset+2, req_path_size, "Request Path: ");
-      dissect_epath( tvb, pi, offset+2, req_path_size );
 
       /* If there is any command specific data creat a sub-tree for it */
       if( (item_length-req_path_size-2) != 0 )
       {
 
          pi = proto_tree_add_text( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Command Specific Data" );
-         cmd_data_tree = proto_item_add_subtree( pi, ett_cmd_data );
+         cmd_data_tree = proto_item_add_subtree( pi, ett_mr_cmd_data );
 
          /* Check what service code that recived */
 
-         if( tvb_get_guint8( tvb, offset ) == SC_FWD_OPEN )
+         if( tvb_get_guint8( tvb, offset ) == SC_MULT_SERV_PACK )
          {
-            /* Forward open Request*/
+            /* Multiple service packet */
 
-            /* Display the priority/tick timer */
-            temp_byte = tvb_get_guint8( tvb, offset+2+req_path_size );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 1, "Priority/Time_tick: 0x%02X", temp_byte );
+            /* Add number of services */
+            num_services = tvb_get_letohs( tvb, offset+2+req_path_size );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Number of Services: %d", num_services );
 
-            /* Display the time-out ticks */
-            temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+1 );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+1, 1, "Time-out_ticks: %d", temp_data );
+            /* Add services */
+            temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, num_services*2, "Offsets: " );
 
-            /* Display the actual time out */
-            temp_data = ( 1 << ( temp_byte & 0x0F ) ) * temp_data;
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Actual Time Out: %dms", temp_data );
+            cip_req_info = (cip_req_info_t*)p_get_proto_data( pinfo->fd, proto_cip );
 
-            /* Display originator to taget connection ID */
-            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+2 );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, 4, "O->T Network Connection ID: 0x%08X", temp_data );
+            mr_mult_req_info = NULL;
+            if ( cip_req_info )
+            {
+               if ( cip_req_info->pData == NULL )
+               {
+                  mr_mult_req_info = se_alloc(sizeof(mr_mult_req_info_t));
+                  mr_mult_req_info->service = SC_MULT_SERV_PACK;
+                  mr_mult_req_info->num_services = num_services;
+                  mr_mult_req_info->requests = se_alloc(sizeof(cip_req_info_t)*num_services);
+                  cip_req_info->pData = mr_mult_req_info;
+               }
+               else
+               {
+                  mr_mult_req_info = (mr_mult_req_info_t*)cip_req_info->pData;
+                  if ( mr_mult_req_info && mr_mult_req_info->num_services != num_services )
+                     mr_mult_req_info = NULL;
+               }
+            }
+            for( i=0; i < num_services; i++ )
+            {
+               int serv_length;
+               tvbuff_t *next_tvb;
 
-            /* Display target to originator connection ID */
-            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+6 );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+6, 4, "T->O Network Connection ID: 0x%08X", temp_data );
+               serv_offset = tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) );
 
-            /* Display connection serial number */
-            temp_data = tvb_get_letohs( tvb, offset+2+req_path_size+10 );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+10, 2, "Connection Serial Number: 0x%04X", temp_data );
+               if( i == (num_services-1) )
+               {
+                  /* Last service to add */
+                  serv_length = item_length-2-req_path_size-serv_offset;
+                  proto_item_append_text(temp_item, "%d", serv_offset );
+               }
+               else
+               {
+                  serv_length = tvb_get_letohs( tvb, offset+4+req_path_size+((i+1)*2) ) - serv_offset;
+                  proto_item_append_text(temp_item, "%d, ", serv_offset );
+               }
 
-            /* Display the originator vendor id */
-            proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+2+req_path_size+12, 2, TRUE);
+               temp_item2 = proto_tree_add_text( cmd_data_tree, tvb, offset+serv_offset+6, serv_length, "Service Packet #%d", i+1 );
+               temp_tree = proto_item_add_subtree( temp_item2, ett_mr_mult_ser );
 
-            /* Display the originator serial number */
-            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+14 );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+14, 4, "Originator Serial Number: 0x%08X", temp_data );
+               /*
+               ** We call our selves again to disect embedded packet
+               */
 
-            /* Display the timeout multiplier */
-            temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+18 );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+18, 1, "Connection Timeout Multiplier: %s (%d)", val_to_str( temp_data, cip_con_time_mult_vals , "Reserved" ), temp_data );
+               if(check_col(pinfo->cinfo, COL_INFO))
+                  col_append_str( pinfo->cinfo, COL_INFO, ", ");
 
-            /* Put out an indicator for the reserved bytes */
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+19, 3, "Reserved Data" );
+               next_tvb = tvb_new_subset(tvb, offset+serv_offset+6, serv_length, serv_length);
 
-            /* Display originator to target requested packet interval */
-            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+22 );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+22, 4, "O->T RPI: %dms (0x%08X)", temp_data / 1000, temp_data );
+               if ( mr_mult_req_info )
+               {
+                  mr_single_req_info = mr_mult_req_info->requests + i;
+                  mr_single_req_info->bService = 0;
+                  mr_single_req_info->dissector = NULL;
+                  mr_single_req_info->IOILen = 0;
+                  mr_single_req_info->pIOI = NULL;
+                  mr_single_req_info->pData = NULL;
+
+                  dissect_cip_data( temp_tree, next_tvb, 0, pinfo, mr_single_req_info );
+               }
+               else
+               {
+                  dissect_cip_data( temp_tree, next_tvb, 0, pinfo, NULL );
+               }
+            }
+         } /* End if Multiple service Packet */
+         else if( tvb_get_guint8( tvb, offset ) == SC_GET_ATT_LIST )
+         {
+            /* Get attribute list request */
 
-            /* Display originator to target network connection patameterts, in a tree */
-            temp_data = tvb_get_letohs( tvb, offset+2+req_path_size+26 );
-            ncppi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+26, 2, "O->T Network Connection Parameters: 0x%04X", temp_data );
-            ncp_tree = proto_item_add_subtree(ncppi, ett_ncp);
+            int att_count;
 
-            /* Add the data to the tree */
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_own,
-                                tvb, offset+2+req_path_size+26, 2, TRUE );
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_typ,
-                                tvb, offset+2+req_path_size+26, 2, TRUE );
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_prio,
-                                tvb, offset+2+req_path_size+26, 2, TRUE );
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_fixed_var,
-                                tvb, offset+2+req_path_size+26, 2, TRUE );
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_con_size,
-                                tvb, offset+2+req_path_size+26, 2, TRUE );
+            /* Add number of services */
+            att_count = tvb_get_letohs( tvb, offset+2+req_path_size );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Attribute Count: %d", att_count );
+
+            /* Add Attribute List */
+            temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, att_count*2, "Attribute List: " );
+
+            for( i=0; i < att_count; i++ )
+            {
+               if( i == (att_count-1) )
+                  proto_item_append_text(temp_item, "%d",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+               else
+                  proto_item_append_text(temp_item, "%d, ",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+            }
+
+         } /* End of Get attribute list request */
+         else
+         {
+            /* Add data */
+            add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " );
+         } /* End of check service code */
+
+      } /* End of if command-specific data present */
+
+   } /* End of if-else( request ) */
+
+} /* End of dissect_cip_mr() */
+
+static int
+dissect_cip_class_mr(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+   proto_item *ti;
+   proto_tree *class_tree;
+
+   if( tree )
+   {
+      /* Create display subtree for the protocol */
+      ti = proto_tree_add_item(tree, proto_cip_class_mr, tvb, 0, -1, FALSE);
+      class_tree = proto_item_add_subtree( ti, ett_cip_class_mr );
+
+      dissect_cip_mr_data( class_tree, tvb, 0, tvb_length(tvb), pinfo );
+   }
+
+   return tvb_length(tvb);
+}
+
+/************************************************
+ *
+ * Dissector for CIP Connection Manager
+ *
+ ************************************************/
+
+static void
+dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+{
+   proto_item *pi, *rrsc_item, *ncppi, *ar_item, *temp_item;
+   proto_tree *temp_tree, *rrsc_tree, *ncp_tree, *cmd_data_tree;
+   int req_path_size, conn_path_size, temp_data;
+   unsigned char gen_status;
+   unsigned char add_stat_size;
+   unsigned short add_status;
+   unsigned char temp_byte, route_path_size;
+   unsigned char app_rep_size, i;
+   int msg_req_siz;
+   unsigned char service;
+   cip_req_info_t *preq_info;
+   cip_req_info_t *pembedded_req_info;
+
+   /* Special handling for Unconnected send response. If successful, embedded service code is sent.
+    * If failed, it can be either an Unconnected send response or the embedded service code response. */
+   preq_info = (cip_req_info_t*)p_get_proto_data( pinfo->fd, proto_cip );
+   if (  preq_info && ( tvb_get_guint8( tvb, offset ) & 0x80 )
+      && preq_info->bService == SC_CM_UNCON_SEND
+      )
+   {
+      gen_status = tvb_get_guint8( tvb, offset+2 );
+      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+      if ( add_stat_size == 2 )
+         add_status = tvb_get_letohs( tvb, offset + 4 );
+      else
+         add_status = 0;
+      if(   gen_status == 0   /* success response ) */
+         || ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) != SC_CM_UNCON_SEND )
+         || !(  ( gen_status == 0x01 && ( add_status == 0x0204 || add_status == 0x0311 || add_status == 0x0312 || add_status == 0x0315 ) )
+             || gen_status == 0x02
+             || gen_status == 0x04
+             )
+         )
+      {
+         pembedded_req_info = (cip_req_info_t*)preq_info->pData;
+
+         if ( pembedded_req_info )
+         {
+            tvbuff_t *next_tvb;
+            void *p_save_proto_data;
+
+            p_save_proto_data = p_get_proto_data( pinfo->fd, proto_cip );
+            p_remove_proto_data(pinfo->fd, proto_cip);
+            p_add_proto_data(pinfo->fd, proto_cip, pembedded_req_info );
+
+            rrsc_item = proto_tree_add_text( item_tree, NULL, 0, 0, "(Service: Unconnected Send (Response))" );
+            next_tvb = tvb_new_subset(tvb, offset, item_length, item_length);
+            if ( pembedded_req_info && pembedded_req_info->dissector )
+               call_dissector(pembedded_req_info->dissector, next_tvb, pinfo, item_tree );
+            else
+               call_dissector( cip_class_generic_handle, next_tvb, pinfo, item_tree );
+
+            p_remove_proto_data(pinfo->fd, proto_cip);
+            p_add_proto_data(pinfo->fd, proto_cip, p_save_proto_data);
+            return;
+         }
+      }
+   }
+
+   if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
+      col_set_str( pinfo->cinfo, COL_PROTOCOL, "CIP CM" );
+
+   /* Add Service code & Request/Response tree */
+   rrsc_item = proto_tree_add_text( item_tree, tvb, offset, 1, "Service: " );
+   rrsc_tree = proto_item_add_subtree( rrsc_item, ett_cm_rrsc );
+
+   /* Add Request/Response */
+   proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
+
+   /* watch for service collisions */
+   service = tvb_get_guint8( tvb, offset );
+   proto_item_append_text( rrsc_item, "%s (%s)",
+               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+                  cip_sc_vals_cm , "Unknown Service (%x)"),
+               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
+                  cip_sc_rr, "") );
+
+   /* Add Service code */
+   proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
+
+   if( tvb_get_guint8( tvb, offset ) & 0x80 )
+   {
+      /* Response message */
+      gen_status = tvb_get_guint8( tvb, offset+2 );
+      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+
+      /* If there is any command specific data create a sub-tree for it */
+      if( ( item_length-4-add_stat_size ) != 0 )
+      {
+         pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific Data" );
+         cmd_data_tree = proto_item_add_subtree( pi, ett_cm_cmd_data );
+
+         if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
+         {
+           /* Success responses */
+
+           if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_FWD_OPEN )
+           {
+               /* Forward open Response (Success) */
+               guint32 O2TConnID;
+               guint32 T2OConnID;
+               guint16 ConnSerialNumber;
+               guint32 DeviceSerialNumber;
+               guint16 VendorID;
+
+               /* Display originator to target connection ID */
+               O2TConnID = tvb_get_letohl( tvb, offset+4+add_stat_size );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 4, "O->T Network Connection ID: 0x%08X", O2TConnID );
+
+               /* Display target to originator connection ID */
+               T2OConnID = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "T->O Network Connection ID: 0x%08X", T2OConnID );
+
+               /* Display connection serial number */
+               ConnSerialNumber = tvb_get_letohs( tvb, offset+4+add_stat_size+8 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 2, "Connection Serial Number: 0x%04X", ConnSerialNumber );
+
+               /* Display the originator vendor id */
+               VendorID = tvb_get_letohs( tvb, offset+4+add_stat_size+10 );
+               proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+10, 2, TRUE);
+
+               /* Display the originator serial number */
+               DeviceSerialNumber = tvb_get_letohl( tvb, offset+4+add_stat_size+12 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+12, 4, "Originator Serial Number: 0x%08X", DeviceSerialNumber );
+
+               /* Display originator to target actual packet interval */
+               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+16 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+16, 4, "O->T API: %dms (0x%08X)", temp_data / 1000, temp_data );
+
+               /* Display originator to target actual packet interval */
+               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+20 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+20, 4, "T->O API: %dms (0x%08X)", temp_data / 1000, temp_data );
+
+               /* Display the application reply size */
+               app_rep_size = tvb_get_guint8( tvb, offset+4+add_stat_size+24 ) * 2;
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+24, 1, "Application Reply Size: %d (words)", app_rep_size / 2 );
+
+               /* Display the Reserved byte */
+               temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+25 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+25, 1, "Reserved: 0x%02X", temp_byte );
+
+               if( app_rep_size != 0 )
+               {
+                  /* Display application Reply data */
+                  ar_item = proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+26, app_rep_size, "Application Reply:" );
+
+                  for( i=0; i < app_rep_size; i++ )
+                  {
+                    temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+26+i );
+                    proto_item_append_text(ar_item, " 0x%02X", temp_byte );
+                  }
+
+                } /* End of if reply data */
+
+                enip_open_cip_connection( pinfo, ConnSerialNumber, VendorID, DeviceSerialNumber, O2TConnID, T2OConnID );
+
+            } /* End of if forward open response */
+       else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_FWD_CLOSE )
+            {
+               /* Forward close response (Success) */
+               guint16 ConnSerialNumber;
+               guint32 DeviceSerialNumber;
+               guint16 VendorID;
+
+               /* Display connection serial number */
+               ConnSerialNumber = tvb_get_letohs( tvb, offset+4+add_stat_size );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Connection Serial Number: 0x%04X", ConnSerialNumber );
+
+               /* Display the originator vendor id */
+               VendorID = tvb_get_letohs( tvb, offset+4+add_stat_size+2 );
+               proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+2, 2, TRUE);
+
+               /* Display the originator serial number */
+               DeviceSerialNumber = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "Originator Serial Number: 0x%08X", DeviceSerialNumber );
+
+               /* Display the application reply size */
+               app_rep_size = tvb_get_guint8( tvb, offset+4+add_stat_size+8 ) * 2;
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 1, "Application Reply Size: %d (words)", app_rep_size / 2 );
+
+               /* Display the Reserved byte */
+               temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+9 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+9, 1, "Reserved: 0x%02X", temp_byte );
+
+               if( app_rep_size != 0 )
+               {
+                  /* Display application Reply data */
+                  ar_item = proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+10, app_rep_size, "Application Reply:" );
+
+                  for( i=0; i < app_rep_size; i ++ )
+                  {
+                    temp_byte = tvb_get_guint8( tvb, offset+4+add_stat_size+10+i );
+                    proto_item_append_text(ar_item, " 0x%02X", temp_byte );
+                  }
+
+                } /* End of if reply data */
+
+                enip_close_cip_connection( pinfo, ConnSerialNumber, VendorID, DeviceSerialNumber );
+
+            } /* End of if forward close response */
+            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_UNCON_SEND )
+            {
+               /* Unconnected send response (Success) */
+
+               /* Display service response data */
+               add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+            }
+            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_GET_ATT_LIST )
+            {
+               /* Get Attribute List Reply (Success)*/
+
+               int att_count;
+
+               /* Add Attribute Count */
+               att_count = tvb_get_letohs( tvb, offset+4+add_stat_size );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Attribute Count: %d", att_count );
+
+               /* Add the data */
+               add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+6+add_stat_size, item_length-6-add_stat_size, "Data: " );
+
+            } /* Get Attribute List Reply */
+            else
+            {
+               /* Add data */
+               add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+            } /* end of check service code */
+
+         }
+         else
+         {
+            /* Error responses */
+
+            if( ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_FWD_OPEN ) ||
+                ( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_FWD_CLOSE ) )
+            {
+               /* Forward open and forward close error response look the same */
+
+               /* Display connection serial number */
+               temp_data = tvb_get_letohs( tvb, offset+4+add_stat_size );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Connection Serial Number: 0x%04X", temp_data );
+
+               /* Display the originator vendor id */
+               proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+2, 2, TRUE);
+
+               /* Display the originator serial number */
+               temp_data = tvb_get_letohl( tvb, offset+4+add_stat_size+4 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+4, 4, "Originator Serial Number: 0x%08X", temp_data );
+
+               /* Display remaining path size */
+               temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size+8 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+8, 1, "Remaining Path Size: %d", temp_data );
+
+               /* Display reserved data */
+               temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size+9 );
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size+9, 1, "Reserved: 0x%02X", temp_data );
+            }
+            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_CM_UNCON_SEND )
+            {
+               /* Unconnected send response (Unsuccess) */
+
+               /* Display remaining path size */
+               temp_data = tvb_get_guint8( tvb, offset+4+add_stat_size);
+               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 1, "Remaining Path Size: %d", temp_data );
+            }
+            else
+            {
+               /* Add data */
+               add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+            }
+
+         } /* end of if-else( CI_CRC_SUCCESS ) */
+
+      } /* End of if command-specific data present */
+
+   } /* End of if reply */
+   else
+   {
+      /* Request message */
+
+      /* Add service to info column */
+      if(check_col(pinfo->cinfo, COL_INFO))
+      {
+         col_append_str( pinfo->cinfo, COL_INFO,
+                  val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+                     cip_sc_vals_cm , "Unknown Service (%x)") );
+      }
+      req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
+
+      /* If there is any command specific data creat a sub-tree for it */
+      if( (item_length-req_path_size-2) != 0 )
+      {
+
+         pi = proto_tree_add_text( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Command Specific Data" );
+         cmd_data_tree = proto_item_add_subtree( pi, ett_cm_cmd_data );
+
+         /* Check what service code that recived */
+
+         if( tvb_get_guint8( tvb, offset ) == SC_CM_FWD_OPEN )
+         {
+            /* Forward open Request*/
+
+            /* Display the priority/tick timer */
+            temp_byte = tvb_get_guint8( tvb, offset+2+req_path_size );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 1, "Priority/Time_tick: 0x%02X", temp_byte );
+
+            /* Display the time-out ticks */
+            temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+1 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+1, 1, "Time-out_ticks: %d", temp_data );
+
+            /* Display the actual time out */
+            temp_data = ( 1 << ( temp_byte & 0x0F ) ) * temp_data;
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Actual Time Out: %dms", temp_data );
+
+            /* Display originator to taget connection ID */
+            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+2 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, 4, "O->T Network Connection ID: 0x%08X", temp_data );
+
+            /* Display target to originator connection ID */
+            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+6 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+6, 4, "T->O Network Connection ID: 0x%08X", temp_data );
+
+            /* Display connection serial number */
+            temp_data = tvb_get_letohs( tvb, offset+2+req_path_size+10 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+10, 2, "Connection Serial Number: 0x%04X", temp_data );
+
+            /* Display the originator vendor id */
+            proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+2+req_path_size+12, 2, TRUE);
+
+            /* Display the originator serial number */
+            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+14 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+14, 4, "Originator Serial Number: 0x%08X", temp_data );
+
+            /* Display the timeout multiplier */
+            temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+18 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+18, 1, "Connection Timeout Multiplier: %s (%d)", val_to_str( temp_data, cip_con_time_mult_vals , "Reserved" ), temp_data );
+
+            /* Put out an indicator for the reserved bytes */
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+19, 3, "Reserved Data" );
+
+            /* Display originator to target requested packet interval */
+            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+22 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+22, 4, "O->T RPI: %dms (0x%08X)", temp_data / 1000, temp_data );
+
+            /* Display originator to target network connection patameterts, in a tree */
+            temp_data = tvb_get_letohs( tvb, offset+2+req_path_size+26 );
+            ncppi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+26, 2, "O->T Network Connection Parameters: 0x%04X", temp_data );
+            ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp);
+
+            /* Add the data to the tree */
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_own,
+                  tvb, offset+2+req_path_size+26, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_typ,
+                  tvb, offset+2+req_path_size+26, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_prio,
+                  tvb, offset+2+req_path_size+26, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_fixed_var,
+                  tvb, offset+2+req_path_size+26, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_con_size,
+                  tvb, offset+2+req_path_size+26, 2, TRUE );
 
             /* Display target to originator requested packet interval */
             temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+28 );
@@ -1387,35 +2163,35 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
             /* Display target to originator network connection patameterts, in a tree */
             temp_data = tvb_get_letohs( tvb, offset+2+req_path_size+32 );
             ncppi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+32, 2, "T->O Network Connection Parameters: 0x%04X", temp_data );
-            ncp_tree = proto_item_add_subtree(ncppi, ett_ncp);
+            ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp);
 
             /* Add the data to the tree */
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_own,
-                                tvb, offset+2+req_path_size+32, 2, TRUE );
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_typ,
-                                tvb, offset+2+req_path_size+32, 2, TRUE );
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_prio,
-                                tvb, offset+2+req_path_size+32, 2, TRUE );
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_fixed_var,
-                                tvb, offset+2+req_path_size+32, 2, TRUE );
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_con_size,
-                                tvb, offset+2+req_path_size+32, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_own,
+                  tvb, offset+2+req_path_size+32, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_typ,
+                  tvb, offset+2+req_path_size+32, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_prio,
+                  tvb, offset+2+req_path_size+32, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_fixed_var,
+                  tvb, offset+2+req_path_size+32, 2, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_con_size,
+                  tvb, offset+2+req_path_size+32, 2, TRUE );
 
             /* Transport type/trigger in tree*/
             temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+34 );
 
             ncppi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+34, 1, "Transport Type/Trigger: 0x%02X", temp_data );
-            ncp_tree = proto_item_add_subtree(ncppi, ett_ncp);
+            ncp_tree = proto_item_add_subtree(ncppi, ett_cm_ncp);
 
             /* Add the data to the tree */
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_dir,
-                                tvb, offset+2+req_path_size+34, 1, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_dir,
+                  tvb, offset+2+req_path_size+34, 1, TRUE );
 
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_trigg,
-                                tvb, offset+2+req_path_size+34, 1, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_trigg,
+                  tvb, offset+2+req_path_size+34, 1, TRUE );
 
-            proto_tree_add_item(ncp_tree, hf_cip_fwo_class,
-                                tvb, offset+2+req_path_size+34, 1, TRUE );
+            proto_tree_add_item(ncp_tree, hf_cip_cm_fwo_class,
+                  tvb, offset+2+req_path_size+34, 1, TRUE );
 
             /* Add path size */
             conn_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+35 )*2;
@@ -1423,9 +2199,9 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
 
             /* Add the epath */
             pi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+36, conn_path_size, "Connection Path: ");
-            dissect_epath( tvb, pi, offset+2+req_path_size+36, conn_path_size );
+            dissect_epath( tvb, pi, offset+2+req_path_size+36, conn_path_size, FALSE );
          }
-         else if( tvb_get_guint8( tvb, offset ) == SC_FWD_CLOSE && ! collision)
+         else if( tvb_get_guint8( tvb, offset ) == SC_CM_FWD_CLOSE )
          {
             /* Forward Close Request */
 
@@ -1452,133 +2228,248 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
             temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+6 );
             proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+6, 4, "Originator Serial Number: 0x%08X", temp_data );
 
-            /* Add the path size */
-            conn_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+10 )*2;
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+10, 1, "Connection Path Size: %d (words)", conn_path_size / 2 );
+            /* Add the path size */
+            conn_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+10 )*2;
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+10, 1, "Connection Path Size: %d (words)", conn_path_size / 2 );
+
+            /* Add the reserved byte */
+            temp_byte = tvb_get_guint8( tvb, offset+2+req_path_size+11 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+11, 1, "Reserved: 0x%02X", temp_byte );
+
+            /* Add the EPATH */
+            pi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+12, conn_path_size, "Connection Path: ");
+            dissect_epath( tvb, pi, offset+2+req_path_size+12, conn_path_size, FALSE );
+
+         } /* End of forward close */
+         else if( tvb_get_guint8( tvb, offset ) == SC_CM_UNCON_SEND )
+         {
+            /* Unconnected send */
+            tvbuff_t *next_tvb;
+
+            /* Display the priority/tick timer */
+            temp_byte = tvb_get_guint8( tvb, offset+2+req_path_size );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 1, "Priority/Time_tick: 0x%02X", temp_byte );
+
+            /* Display the time-out ticks */
+            temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+1 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+1, 1, "Time-out_ticks: %d", temp_data );
+
+            /* Display the actual time out */
+            temp_data = ( 1 << ( temp_byte & 0x0F ) ) * temp_data;
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Actual Time Out: %dms", temp_data );
+
+            /* Message request size */
+            msg_req_siz = tvb_get_letohs( tvb, offset+2+req_path_size+2 );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, 2, "Message Request Size: 0x%04X", msg_req_siz );
+
+            /* Message Request */
+            temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4, msg_req_siz, "Message Request" );
+            temp_tree = proto_item_add_subtree(temp_item, ett_cm_mes_req );
+
+            /*
+            ** We call our selves again to disect embedded packet
+            */
+
+            if(check_col(pinfo->cinfo, COL_INFO))
+              col_append_str( pinfo->cinfo, COL_INFO, ": ");
+
+            next_tvb = tvb_new_subset(tvb, offset+2+req_path_size+4, msg_req_siz, msg_req_siz);
+            preq_info = p_get_proto_data( pinfo->fd, proto_cip );
+            pembedded_req_info = NULL;
+            if ( preq_info )
+            {
+               if ( preq_info->pData == NULL )
+               {
+                  pembedded_req_info = (cip_req_info_t*)se_alloc(sizeof(cip_req_info_t));
+                  pembedded_req_info->bService = 0;
+                  pembedded_req_info->dissector = NULL;
+                  pembedded_req_info->IOILen = 0;
+                  pembedded_req_info->pIOI = NULL;
+                  pembedded_req_info->pData = NULL;
+                  preq_info->pData = pembedded_req_info;
+               }
+               else
+               {
+                  pembedded_req_info = (cip_req_info_t*)preq_info->pData;
+               }
+            }
+            dissect_cip_data( temp_tree, next_tvb, 0, pinfo, pembedded_req_info );
+
+            if( msg_req_siz % 2 )
+            {
+              /* Pad byte */
+              proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4+msg_req_siz, 1, "Pad Byte (0x%02X)",
+                         tvb_get_guint8( tvb, offset+2+req_path_size+4+msg_req_siz ) );
+              msg_req_siz++;  /* include the padding */
+            }
+
+            /* Route Path Size */
+            route_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+4+msg_req_siz )*2;
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4+msg_req_siz, 1, "Route Path Size: %d (words)", route_path_size/2 );
+
+            /* Reserved */
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+5+msg_req_siz, 1, "Reserved (0x%02X)",
+                tvb_get_guint8( tvb, offset+2+req_path_size+5+msg_req_siz ) );
+
+            /* Route Path */
+            temp_item = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+6+msg_req_siz, route_path_size, "Route Path: ");
+            dissect_epath( tvb, temp_item, offset+2+req_path_size+6+msg_req_siz, route_path_size, FALSE );
+         } /* End if unconnected send */
+         else if( tvb_get_guint8( tvb, offset ) == SC_GET_ATT_LIST )
+         {
+            /* Get attribute list request */
+
+            int att_count;
+
+            /* Add number of services */
+            att_count = tvb_get_letohs( tvb, offset+2+req_path_size );
+            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Attribute Count: %d", att_count );
+
+            /* Add Attribute List */
+            temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, att_count*2, "Attribute List: " );
+
+            for( i=0; i < att_count; i++ )
+            {
+               if( i == (att_count-1) )
+                  proto_item_append_text(temp_item, "%d",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+               else
+                  proto_item_append_text(temp_item, "%d, ",tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) ) );
+            }
+
+         } /* End of Get attribute list request */
+         else
+         {
+            /* Add data */
+            add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " );
+         } /* End of check service code */
+
+      } /* End of if command-specific data present */
+
+   } /* End of if-else( request ) */
+
+} /* End of dissect_cip_cm_data() */
+
+static int
+dissect_cip_class_cm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+   proto_item *ti;
+   proto_tree *class_tree;
+
+   if( tree )
+   {
+      /* Create display subtree for the protocol */
+      ti = proto_tree_add_item(tree, proto_cip_class_cm, tvb, 0, -1, FALSE);
+      class_tree = proto_item_add_subtree( ti, ett_cip_class_cm );
+
+      dissect_cip_cm_data( class_tree, tvb, 0, tvb_length(tvb), pinfo );
+   }
+
+   return tvb_length(tvb);
+}
+
+/************************************************
+ *
+ * Dissector for CIP Connection Configuration Object
+ *
+ ************************************************/
+
+static void
+dissect_cip_cco_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
+{
+   proto_item *pi, *rrsc_item, *temp_item;
+   proto_tree *rrsc_tree, *cmd_data_tree;
+   int req_path_size, temp_data;
+   unsigned char gen_status;
+   unsigned char add_stat_size;
+   unsigned char i;
+
+   if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
+      col_set_str( pinfo->cinfo, COL_PROTOCOL, "CIP CCO" );
+
+   /* Add Service code & Request/Response tree */
+   rrsc_item = proto_tree_add_text( item_tree, tvb, offset, 1, "Service: " );
+   rrsc_tree = proto_item_add_subtree( rrsc_item, ett_cco_rrsc );
+
+   /* Add Request/Response */
+   proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
+
+   proto_item_append_text( rrsc_item, "%s (%s)",
+               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+                  cip_sc_vals_cco , "Unknown Service (%x)"),
+               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
+                  cip_sc_rr, "") );
+
+   /* Add Service code */
+   proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
+
+   if( tvb_get_guint8( tvb, offset ) & 0x80 )
+   {
+      /* Response message */
 
-            /* Add the reserved byte */
-            temp_byte = tvb_get_guint8( tvb, offset+2+req_path_size+11 );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+11, 1, "Reserved: 0x%02X", temp_byte );
+      /* Add additional status size */
+      gen_status = tvb_get_guint8( tvb, offset+2 );
+      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
 
-            /* Add the EPATH */
-            pi = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+12, conn_path_size, "Connection Path: ");
-            dissect_epath( tvb, pi, offset+2+req_path_size+12, conn_path_size );
+      /* If there is any command specific data create a sub-tree for it */
+      if( ( item_length-4-add_stat_size ) != 0 )
+      {
+         pi = proto_tree_add_text( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Command Specific Data" );
+         cmd_data_tree = proto_item_add_subtree( pi, ett_cco_cmd_data );
 
-         } /* End of forward close */
-         else if( tvb_get_guint8( tvb, offset ) == SC_UNCON_SEND )
+         if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
          {
-           /* check for collision */
-           if ( collision )
-           {
-              /* Audit Change */
-
-              temp_data = tvb_get_letohs( tvb, offset+2+req_path_size );
-              temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: ");
-
-               if (temp_data == 0)
-                  proto_item_append_text(temp_item, "Full" );
-               else if (temp_data == 1)
-                  proto_item_append_text(temp_item, "Incremental" );
-               else
-                  proto_item_append_text(temp_item, "Reserved" );
-           }
-           else
-           {
-              /* Unconnected send */
-
-               /* Display the priority/tick timer */
-              temp_byte = tvb_get_guint8( tvb, offset+2+req_path_size );
-              proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 1, "Priority/Time_tick: 0x%02X", temp_byte );
-
-              /* Display the time-out ticks */
-              temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+1 );
-              proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+1, 1, "Time-out_ticks: %d", temp_data );
-
-              /* Display the actual time out */
-              temp_data = ( 1 << ( temp_byte & 0x0F ) ) * temp_data;
-              proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Actual Time Out: %dms", temp_data );
-
-              /* Message request size */
-              msg_req_siz = tvb_get_letohs( tvb, offset+2+req_path_size+2 );
-              proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, 2, "Message Request Size: 0x%04X", msg_req_siz );
-
-              /* Message Request */
-              temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4, msg_req_siz, "Message Request" );
-              temp_tree = proto_item_add_subtree(temp_item, ett_mes_req );
-
-              /*
-              ** We call our selves again to disect embedded packet
-              */
-
-              if(check_col(pinfo->cinfo, COL_INFO))
-                  col_append_str( pinfo->cinfo, COL_INFO, ": ");
-
-              dissect_cip_data( temp_tree, tvb, offset+2+req_path_size+4, msg_req_siz, pinfo );
-
-              if( msg_req_siz % 2 )
-              {
-                  /* Pad byte */
-                 proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4+msg_req_siz, 1, "Pad Byte (0x%02X)",
-                                      tvb_get_guint8( tvb, offset+2+req_path_size+4+msg_req_siz ) );
-                 msg_req_siz++;        /* include the padding */
-              }
-
-              /* Route Path Size */
-              route_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+4+msg_req_siz )*2;
-              proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+4+msg_req_siz, 1, "Route Path Size: %d (words)", route_path_size/2 );
-
-              /* Reserved */
-              proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+5+msg_req_siz, 1, "Reserved (0x%02X)",
-                                    tvb_get_guint8( tvb, offset+2+req_path_size+5+msg_req_siz ) );
-
-              /* Route Path */
-              temp_item = proto_tree_add_text(cmd_data_tree, tvb, offset+2+req_path_size+6+msg_req_siz, route_path_size, "Route Path: ");
-              dissect_epath( tvb, temp_item, offset+2+req_path_size+6+msg_req_siz, route_path_size );
-           }
+           /* Success responses */
 
-         } /* End if unconnected send */
-         else if( tvb_get_guint8( tvb, offset ) == SC_MULT_SERV_PACK )
+            /* Add data */
+            add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+         }
+         else
          {
-            /* Multiple service packet */
+            /* Error responses */
 
-            /* Add number of services */
-            num_services = tvb_get_letohs( tvb, offset+2+req_path_size );
-            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Number of Services: %d", num_services );
+            /* Add data */
+            add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, "Data: " );
+         } /* end of if-else( CI_CRC_SUCCESS ) */
 
-            /* Add services */
-            temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, num_services*2, "Offsets: " );
+      } /* End of if command-specific data present */
 
-            for( i=0; i < num_services; i++ )
-            {
-               int serv_length;
+   } /* End of if reply */
+   else
+   {
+      /* Request message */
 
-               serv_offset = tvb_get_letohs( tvb, offset+4+req_path_size+(i*2) );
+      /* Add service to info column */
+      if(check_col(pinfo->cinfo, COL_INFO))
+      {
+         col_append_str( pinfo->cinfo, COL_INFO,
+                  val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+                     cip_sc_vals_cco , "Unknown Service (%x)") );
+      }
+      req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
 
-               if( i == (num_services-1) )
-               {
-                  /* Last service to add */
-                  serv_length = item_length-2-req_path_size-serv_offset;
-                  proto_item_append_text(temp_item, "%d", serv_offset );
-               }
-               else
-               {
-                  serv_length = tvb_get_letohs( tvb, offset+4+req_path_size+((i+1)*2) ) - serv_offset;
-                  proto_item_append_text(temp_item, "%d, ", serv_offset );
-               }
+      /* If there is any command specific data creat a sub-tree for it */
+      if( (item_length-req_path_size-2) != 0 )
+      {
 
-               temp_item2 = proto_tree_add_text( cmd_data_tree, tvb, offset+serv_offset+6, serv_length, "Service Packet #%d", i+1 );
-               temp_tree = proto_item_add_subtree( temp_item2, ett_mult_ser );
+         pi = proto_tree_add_text( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Command Specific Data" );
+         cmd_data_tree = proto_item_add_subtree( pi, ett_cco_cmd_data );
 
-               /*
-               ** We call our selves again to disect embedded packet
-               */
+         /* Check what service code that recived */
 
-               if(check_col(pinfo->cinfo, COL_INFO))
-                  col_append_str( pinfo->cinfo, COL_INFO, ", ");
+         if( tvb_get_guint8( tvb, offset ) == SC_CCO_AUDIT_CHANGE )
+         {
+             /* Audit Change */
 
-               dissect_cip_data( temp_tree, tvb, offset+serv_offset+6, serv_length, pinfo );
-            }
-         } /* End if Multiple service Packet */
+             temp_data = tvb_get_letohs( tvb, offset+2+req_path_size );
+             temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: ");
+
+             if (temp_data == 0)
+               proto_item_append_text(temp_item, "Full" );
+             else if (temp_data == 1)
+               proto_item_append_text(temp_item, "Incremental" );
+             else
+               proto_item_append_text(temp_item, "Reserved" );
+         }
          else if( tvb_get_guint8( tvb, offset ) == SC_GET_ATT_LIST )
          {
             /* Get attribute list request */
@@ -1601,39 +2492,276 @@ dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_len
             }
 
          } /* End of Get attribute list request */
-        else if ( tvb_get_guint8( tvb, offset ) == SC_CHANGE_COMPLETE )
-        {
-           /* Change complete request */
+         else if ( tvb_get_guint8( tvb, offset ) == SC_CCO_CHANGE_COMPLETE )
+         {
+           /* Change complete request */
 
-            temp_data = tvb_get_letohs( tvb, offset+2+req_path_size );
-           temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: ");
+           temp_data = tvb_get_letohs( tvb, offset+2+req_path_size );
+           temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size, 2, "Change Type: ");
 
-           if (temp_data == 0)
-               proto_item_append_text(temp_item, "Full" );
-           else if (temp_data == 1)
-               proto_item_append_text(temp_item, "Incremental" );
-           else
-               proto_item_append_text(temp_item, "Reserved" );
-        }
+           if (temp_data == 0)
+             proto_item_append_text(temp_item, "Full" );
+           else if (temp_data == 1)
+             proto_item_append_text(temp_item, "Incremental" );
+           else
+             proto_item_append_text(temp_item, "Reserved" );
+         }
          else
          {
             /* Add data */
-            proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+2+req_path_size, item_length-req_path_size-2, FALSE);
-
+            add_byte_array_text_to_proto_tree( cmd_data_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2, "Data: " );
          } /* End of check service code */
 
       } /* End of if command-specific data present */
 
    } /* End of if-else( request ) */
 
+} /* End of dissect_cip_cco_data() */
+
+static int
+dissect_cip_class_cco(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+   proto_item *ti;
+   proto_tree *class_tree;
+
+   if( tree )
+   {
+      /* Create display subtree for the protocol */
+      ti = proto_tree_add_item(tree, proto_cip_class_cco, tvb, 0, -1, FALSE);
+      class_tree = proto_item_add_subtree( ti, ett_cip_class_cco );
+
+      dissect_cip_cco_data( class_tree, tvb, 0, tvb_length(tvb), pinfo );
+   }
+
+   return tvb_length(tvb);
+}
+
+/************************************************
+ *
+ * Dissector for CIP Request/Response
+ * - matches requests/responses
+ * - calls class specific dissector
+ *
+ ************************************************/
+
+static void
+dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, packet_info *pinfo, cip_req_info_t* preq_info )
+{
+   proto_item *ti;
+   proto_tree *cip_tree;
+   proto_item *pi, *rrsc_item, *status_item;
+   proto_tree *rrsc_tree, *status_tree;
+   int req_path_size;
+   unsigned char gen_status;
+   unsigned char add_stat_size;
+   unsigned char i;
+   guint32 classid;
+   unsigned char service,ioilen,segment;
+   void *p_save_proto_data;
+   dissector_handle_t dissector;
+
+   p_save_proto_data = p_get_proto_data(pinfo->fd, proto_cip);
+   p_remove_proto_data(pinfo->fd, proto_cip);
+   p_add_proto_data(pinfo->fd, proto_cip, preq_info);
+
+   /* Create display subtree for the protocol */
+   ti = proto_tree_add_item(item_tree, proto_cip, tvb, 0, -1, FALSE);
+   cip_tree = proto_item_add_subtree( ti, ett_cip );
+
+   /* Add Service code & Request/Response tree */
+   rrsc_item = proto_tree_add_text( cip_tree, tvb, offset, 1, "Service: " );
+   rrsc_tree = proto_item_add_subtree( rrsc_item, ett_rrsc );
+
+   /* Add Request/Response */
+   proto_tree_add_item( rrsc_tree, hf_cip_rr, tvb, offset, 1, TRUE );
+
+   /* watch for service collisions */
+   service = tvb_get_guint8( tvb, offset );
+   proto_item_append_text( rrsc_item, "%s (%s)",
+               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
+                  cip_sc_vals , "Unknown Service (%x)"),
+               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
+                  cip_sc_rr, "") );
+
+   /* Add Service code */
+   proto_tree_add_item(rrsc_tree, hf_cip_sc, tvb, offset, 1, TRUE );
+
+   if( service & 0x80 )
+   {
+      /* Response message */
+      status_item = proto_tree_add_text( cip_tree, tvb, offset+2, 1, "Status: " );
+      status_tree = proto_item_add_subtree( status_item, ett_status_item );
+
+      /* Add general status */
+      gen_status = tvb_get_guint8( tvb, offset+2 );
+      proto_tree_add_item(status_tree, hf_cip_genstat, tvb, offset+2, 1, TRUE );
+      proto_item_append_text( status_item, "%s", val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
+                     cip_gs_vals , "Unknown Response (%x)")   );
+
+      /* Add reply status to info column */
+      if(check_col(pinfo->cinfo, COL_INFO))
+      {
+         col_append_str( pinfo->cinfo, COL_INFO,
+                  val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
+                     cip_gs_vals , "Unknown Response (%x)") );
+      }
+
+      /* Add additional status size */
+      proto_tree_add_text( status_tree, tvb, offset+3, 1, "Additional Status Size: %d (word)",
+         tvb_get_guint8( tvb, offset+3 ) );
+
+      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
+
+      if( add_stat_size )
+      {
+         proto_item_append_text( status_item, ", Extended:" );
+
+         /* Add additional status */
+         pi = proto_tree_add_text( status_tree, tvb, offset+4, add_stat_size, "Additional Status:" );
+
+         for( i=0; i < add_stat_size/2; i ++ )
+         {
+           proto_item_append_text( pi, " 0x%04X", tvb_get_letohs( tvb, offset+4+(i*2) ) );
+           proto_item_append_text( status_item, " 0x%04X", tvb_get_letohs( tvb, offset+4+(i*2) ) );
+         }
+      }
+
+      proto_item_set_len( status_item, 2 + add_stat_size );
+
+
+      if(  preq_info
+        && !(  preq_info->bService == ( service & 0x7F )
+            || ( preq_info->bService == SC_CM_UNCON_SEND && preq_info->dissector == cip_class_cm_handle )
+            )
+        )
+         preq_info = NULL;
+
+      if ( preq_info )
+      {
+         if ( preq_info->IOILen && preq_info->pIOI )
+         {
+            tvbuff_t* tvbIOI;
+
+            tvbIOI = tvb_new_real_data( preq_info->pIOI, preq_info->IOILen * 2, preq_info->IOILen * 2);
+            if ( tvbIOI )
+            {
+               /* pi = add_byte_array_text_to_proto_tree( cip_tree, tvbIOI, 0, req_path_size+1, "IOI: " );
+               PROTO_ITEM_SET_GENERATED(pi); */
+
+               pi = proto_tree_add_text( cip_tree, NULL, 0, 0, "Request Path Size: %d (words)", preq_info->IOILen );
+               PROTO_ITEM_SET_GENERATED(pi);
+
+               /* Add the epath */
+               pi = proto_tree_add_text(cip_tree, NULL, 0, 0, "Request Path: ");
+               PROTO_ITEM_SET_GENERATED(pi);
+               dissect_epath( tvbIOI, pi, 0, preq_info->IOILen, TRUE );
+               tvb_free(tvbIOI);
+            }
+         }
+      }
+
+      if ( preq_info && preq_info->dissector )
+      {
+         call_dissector( preq_info->dissector, tvb, pinfo, item_tree );
+      }
+      else
+      {
+         call_dissector( cip_class_generic_handle, tvb, pinfo, item_tree );
+      }
+   } /* End of if reply */
+   else
+   {
+      /* Request message */
+
+      /* Add path size to tree */
+      req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
+      proto_tree_add_text( cip_tree, tvb, offset+1, 1, "Request Path Size: %d (words)", req_path_size/2 );
+
+      /* Add the epath */
+      pi = proto_tree_add_text(cip_tree, tvb, offset+2, req_path_size, "Request Path: ");
+      dissect_epath( tvb, pi, offset+2, req_path_size, FALSE );
+
+      /* parse IOI; extract class ID */
+      ioilen = tvb_get_guint8( tvb, offset + 1 );
+      if ( preq_info )
+         preq_info->dissector = NULL;
+      dissector = NULL;
+      if ( ioilen >= 1 )
+      {
+         segment = tvb_get_guint8( tvb, offset + 2 );
+         switch ( segment & CI_SEGMENT_TYPE_MASK )
+         {
+         case CI_LOGICAL_SEGMENT:
+            /* Logical segment, determin the logical type */
+            switch( segment & CI_LOGICAL_SEG_TYPE_MASK )
+            {
+            case CI_LOGICAL_SEG_CLASS_ID:
+
+               /* Logical Class ID, do a format check */
+               classid = 0;
+               switch ( segment & CI_LOGICAL_SEG_FORMAT_MASK )
+               {
+               case CI_LOGICAL_SEG_8_BIT:
+                  classid = tvb_get_guint8( tvb, offset + 3 );
+                  break;
+               case CI_LOGICAL_SEG_16_BIT:
+                  if ( ioilen >= 2 )
+                     classid = tvb_get_letohs( tvb, offset + 4 );
+                  break;
+               case CI_LOGICAL_SEG_32_BIT:
+                  if ( ioilen >= 3 )
+                     classid = tvb_get_letohl( tvb, offset + 4 );
+                  break;
+               }
+               dissector = dissector_get_port_handle( subdissector_class_table, classid );
+               if ( preq_info )
+                  preq_info->dissector = dissector;
+               break;
+            }
+            break;
+
+         case CI_DATA_SEGMENT:
+            dissector = dissector_get_port_handle( subdissector_symbol_table, segment );
+            if ( preq_info )
+               preq_info->dissector = dissector;
+            break;
+         }
+         if ( preq_info )
+         {
+            /* copy IOI for access by response packet */
+            preq_info->pIOI = se_alloc( ioilen*2);
+            if ( preq_info->pIOI )
+            {
+               preq_info->IOILen = ioilen;
+               tvb_memcpy(tvb, preq_info->pIOI, offset+2, ioilen*2);
+            }
+         }
+      }
+
+      if( preq_info )
+         preq_info->bService = service;
+
+      if ( dissector )
+      {
+         call_dissector( dissector, tvb, pinfo, item_tree );
+      }
+      else
+      {
+         call_dissector( cip_class_generic_handle, tvb, pinfo, item_tree );
+      }
+   } /* End of if-else( request ) */
+
+   p_remove_proto_data(pinfo->fd, proto_cip);
+   p_add_proto_data(pinfo->fd, proto_cip, p_save_proto_data);
+
 } /* End of dissect_cip_data() */
 
 
 static int
 dissect_cip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
-   proto_item *ti;
-   proto_tree *cip_tree = NULL;
+   enip_request_info_t *enip_info;
+   cip_req_info_t *preq_info;
 
    /* Make entries in Protocol column and Info column on summary display */
    if( check_col( pinfo->cinfo, COL_PROTOCOL ) )
@@ -1642,18 +2770,44 @@ dissect_cip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
    if (check_col( pinfo->cinfo, COL_INFO ) )
       col_clear( pinfo->cinfo, COL_INFO );
 
-   if( tree )
+   /* Each CIP request received by ENIP gets a unique ID */
+   enip_info = (enip_request_info_t*)p_get_proto_data(pinfo->fd, proto_enip);
+
+   if ( enip_info )
    {
-      /* Create display subtree for the protocol */
-      ti = proto_tree_add_item(tree, proto_cip, tvb, 0, -1, FALSE);
-      cip_tree = proto_item_add_subtree( ti, ett_cip );
+      preq_info = (cip_req_info_t*)enip_info->cip_info;
+      if ( preq_info == NULL )
+      {
+         preq_info = se_alloc( sizeof( cip_req_info_t ) );
+         if ( preq_info )
+         {
+            preq_info->bService = 0;
+            preq_info->dissector = NULL;
+            preq_info->IOILen = 0;
+            preq_info->pIOI = NULL;
+            preq_info->pData = NULL;
+            enip_info->cip_info = preq_info;
+         }
+      }
+      dissect_cip_data( tree, tvb, 0, pinfo, enip_info->cip_info );
+   }
+   else
+   {
+      dissect_cip_data( tree, tvb, 0, pinfo, NULL );
    }
-
-   dissect_cip_data( cip_tree, tvb, 0, tvb_length(tvb), pinfo );
 
    return tvb_length(tvb);
 }
 
+/*
+ * Protocol initialization
+ */
+
+static void
+cip_init_protocol(void)
+{
+   proto_enip = proto_get_id_by_filter_name( "enip" );
+}
 
 void
 proto_register_cip(void)
@@ -1661,215 +2815,328 @@ proto_register_cip(void)
    /* Setup list of header fields */
    static hf_register_info hf[] = {
 
-               { &hf_cip_rr,
-                       { "Request/Response", "cip.rr",
-                       FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
-                       "Request or Response message", HFILL }
-               },
-               { &hf_cip_sc,
-                       { "Service", "cip.sc",
-                       FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
-                       "Service Code", HFILL }
-               },
-               { &hf_cip_epath,
-                       { "EPath", "cip.epath",
-                       FT_BYTES, BASE_NONE, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_genstat,
-                       { "General Status", "cip.genstat",
-                       FT_UINT8, BASE_HEX, VALS(cip_gs_vals), 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_port,
-                       { "Port", "cip.port",
-                       FT_UINT8, BASE_DEC, NULL, 0,
-                       "Port Identifier", HFILL }
-               },
-               { &hf_cip_link_address_byte,
-                       { "Link Address", "cip.linkaddress",
-                       FT_UINT8, BASE_DEC, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_link_address_string,
-                       { "Link Address", "cip.linkaddress",
-                       FT_STRING, BASE_NONE, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_class8,
-                       { "Class", "cip.class",
-                       FT_UINT8, BASE_HEX, VALS(cip_class_names_vals), 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_class16,
-                       { "Class", "cip.class",
-                       FT_UINT16, BASE_HEX, VALS(cip_class_names_vals), 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_class32,
-                       { "Class", "cip.class",
-                       FT_UINT32, BASE_HEX, VALS(cip_class_names_vals), 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_instance8,
-                       { "Instance", "cip.instance",
-                       FT_UINT8, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_instance16,
-                       { "Instance", "cip.instance",
-                       FT_UINT16, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_instance32,
-                       { "Instance", "cip.instance",
-                       FT_UINT32, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_attribute8,
-                       { "Attribute", "cip.attribute",
-                       FT_UINT8, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_attribute16,
-                       { "Attribute", "cip.attribute",
-                       FT_UINT16, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_attribute32,
-                       { "Attribute", "cip.attribute",
-                       FT_UINT32, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_conpoint8,
-                       { "Connection Point", "cip.connpoint",
-                       FT_UINT8, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_conpoint16,
-                       { "Connection Point", "cip.connpoint",
-                       FT_UINT16, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_conpoint32,
-                       { "Connection Point", "cip.connpoint",
-                       FT_UINT16, BASE_HEX, NULL, 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_symbol,
-                       { "Symbol", "cip.symbol",
-                       FT_STRING, BASE_NONE, NULL, 0,
-                       "ANSI Extended Symbol Segment", HFILL }
-               },
-               { &hf_cip_vendor,
-                       { "Vendor ID", "cip.vendor",
-                       FT_UINT16, BASE_HEX, VALS(cip_vendor_vals), 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_devtype,
-                       { "Device Type", "cip.devtype",
-                       FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0,
-                       NULL, HFILL }
-               },
-               { &hf_cip_fwo_comp,
-                       { "Compatibility", "cip.fwo.cmp",
-                       FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80,
-                       "Fwd Open: Compatibility bit", HFILL }
-               },
-               { &hf_cip_fwo_mrev,
-                       { "Major Revision", "cip.fwo.major",
-                       FT_UINT8, BASE_DEC, NULL, 0x7F,
-                       "Fwd Open: Major Revision", HFILL }
-               },
-               { &hf_cip_fwo_con_size,
-                       { "Connection Size", "cip.fwo.consize",
-                       FT_UINT16, BASE_DEC, NULL, 0x01FF,
-                       "Fwd Open: Connection size", HFILL }
-               },
-               { &hf_cip_fwo_fixed_var,
-                       { "Connection Size Type", "cip.fwo.f_v",
-                       FT_UINT16, BASE_DEC, VALS(cip_con_fw_vals), 0x0200,
-                       "Fwd Open: Fixed or variable connection size", HFILL }
-               },
-               { &hf_cip_fwo_prio,
-                       { "Priority", "cip.fwo.prio",
-                       FT_UINT16, BASE_DEC, VALS(cip_con_prio_vals), 0x0C00,
-                       "Fwd Open: Connection priority", HFILL }
-               },
-               { &hf_cip_fwo_typ,
-                       { "Connection Type", "cip.fwo.type",
-                       FT_UINT16, BASE_DEC, VALS(cip_con_type_vals), 0x6000,
-                       "Fwd Open: Connection type", HFILL }
-               },
-               { &hf_cip_fwo_own,
-                       { "Owner", "cip.fwo.owner",
-                       FT_UINT16, BASE_DEC, VALS(cip_con_owner_vals), 0x8000,
-                       "Fwd Open: Redundant owner bit", HFILL }
-               },
-               { &hf_cip_fwo_dir,
-                       { "Direction", "cip.fwo.dir",
-                       FT_UINT8, BASE_DEC, VALS(cip_con_dir_vals), 0x80,
-                       "Fwd Open: Direction", HFILL }
-               },
-               { &hf_cip_fwo_trigg,
-                       { "Trigger", "cip.fwo.trigger",
-                       FT_UINT8, BASE_DEC, VALS(cip_con_trigg_vals), 0x70,
-                       "Fwd Open: Production trigger", HFILL }
-               },
-               { &hf_cip_fwo_class,
-                       { "Class", "cip.fwo.transport",
-                       FT_UINT8, BASE_DEC, VALS(cip_con_class_vals), 0x0F,
-                       "Fwd Open: Transport Class", HFILL }
-               },
-               { &hf_cip_data,
-                       { "Data", "cip.data",
-                       FT_BYTES, BASE_NONE, NULL, 0,
-                       NULL, HFILL }
-               }
+      { &hf_cip_rr,
+         { "Request/Response", "cip.rr",
+         FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
+         "Request or Response message", HFILL }
+      },
+      { &hf_cip_sc,
+         { "Service", "cip.sc",
+         FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
+         "Service Code", HFILL }
+      },
+      { &hf_cip_epath,
+         { "EPath", "cip.epath",
+         FT_BYTES, BASE_NONE, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_genstat,
+         { "General Status", "cip.genstat",
+         FT_UINT8, BASE_HEX, VALS(cip_gs_vals), 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_port,
+         { "Port", "cip.port",
+         FT_UINT8, BASE_DEC, NULL, 0,
+         "Port Identifier", HFILL }
+      },
+      { &hf_cip_link_address_byte,
+         { "Link Address", "cip.linkaddress",
+         FT_UINT8, BASE_DEC, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_link_address_string,
+         { "Link Address", "cip.linkaddress",
+         FT_STRING, BASE_NONE, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_class8,
+         { "Class", "cip.class",
+         FT_UINT8, BASE_HEX, VALS(cip_class_names_vals), 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_class16,
+         { "Class", "cip.class",
+         FT_UINT16, BASE_HEX, VALS(cip_class_names_vals), 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_class32,
+         { "Class", "cip.class",
+         FT_UINT32, BASE_HEX, VALS(cip_class_names_vals), 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_instance8,
+         { "Instance", "cip.instance",
+         FT_UINT8, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_instance16,
+         { "Instance", "cip.instance",
+         FT_UINT16, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_instance32,
+         { "Instance", "cip.instance",
+         FT_UINT32, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_member8,
+         { "Member", "cip.member",
+         FT_UINT8, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_member16,
+         { "Member", "cip.member",
+         FT_UINT16, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_member32,
+         { "Member", "cip.member",
+         FT_UINT32, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_attribute8,
+         { "Attribute", "cip.attribute",
+         FT_UINT8, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_attribute16,
+         { "Attribute", "cip.attribute",
+         FT_UINT16, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_attribute32,
+         { "Attribute", "cip.attribute",
+         FT_UINT32, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_conpoint8,
+         { "Connection Point", "cip.connpoint",
+         FT_UINT8, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_conpoint16,
+         { "Connection Point", "cip.connpoint",
+         FT_UINT16, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_conpoint32,
+         { "Connection Point", "cip.connpoint",
+         FT_UINT16, BASE_HEX, NULL, 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_symbol,
+         { "Symbol", "cip.symbol",
+         FT_STRING, BASE_NONE, NULL, 0,
+         "ANSI Extended Symbol Segment", HFILL }
+      },
+      { &hf_cip_vendor,
+         { "Vendor ID", "cip.vendor",
+         FT_UINT16, BASE_HEX, VALS(cip_vendor_vals), 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_devtype,
+         { "Device Type", "cip.devtype",
+         FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0,
+         NULL, HFILL }
+      },
+      { &hf_cip_fwo_comp,
+         { "Compatibility", "cip.fwo.cmp",
+         FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80,
+         "EKey: Compatibility bit", HFILL }
+      },
+      { &hf_cip_fwo_mrev,
+         { "Major Revision", "cip.fwo.major",
+         FT_UINT8, BASE_DEC, NULL, 0x7F,
+         "EKey: Major Revision", HFILL }
+      }
+   };
+
+   static hf_register_info hf_cm[] = {
+      { &hf_cip_cm_rr,
+         { "Request/Response", "cip.rr",
+         FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
+         "Request or Response message", HFILL }
+      },
+      { &hf_cip_cm_sc,
+         { "Service", "cip.sc",
+         FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
+         "Service Code", HFILL }
+      },
+      { &hf_cip_cm_fwo_comp,
+         { "Compatibility", "cip.cm.fwo.cmp",
+         FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80,
+         "Fwd Open: Compatibility bit", HFILL }
+      },
+      { &hf_cip_cm_fwo_mrev,
+         { "Major Revision", "cip.cm.fwo.major",
+         FT_UINT8, BASE_DEC, NULL, 0x7F,
+         "Fwd Open: Major Revision", HFILL }
+      },
+      { &hf_cip_cm_fwo_con_size,
+         { "Connection Size", "cip.cm.fwo.consize",
+         FT_UINT16, BASE_DEC, NULL, 0x01FF,
+         "Fwd Open: Connection size", HFILL }
+      },
+      { &hf_cip_cm_fwo_fixed_var,
+         { "Connection Size Type", "cip.cm.fwo.f_v",
+         FT_UINT16, BASE_DEC, VALS(cip_con_fw_vals), 0x0200,
+         "Fwd Open: Fixed or variable connection size", HFILL }
+      },
+      { &hf_cip_cm_fwo_prio,
+         { "Priority", "cip.cm.fwo.prio",
+         FT_UINT16, BASE_DEC, VALS(cip_con_prio_vals), 0x0C00,
+         "Fwd Open: Connection priority", HFILL }
+      },
+      { &hf_cip_cm_fwo_typ,
+         { "Connection Type", "cip.cm.fwo.type",
+         FT_UINT16, BASE_DEC, VALS(cip_con_type_vals), 0x6000,
+         "Fwd Open: Connection type", HFILL }
+      },
+      { &hf_cip_cm_fwo_own,
+         { "Owner", "cip.cm.fwo.owner",
+         FT_UINT16, BASE_DEC, VALS(cip_con_owner_vals), 0x8000,
+         "Fwd Open: Redundant owner bit", HFILL }
+      },
+      { &hf_cip_cm_fwo_dir,
+         { "Direction", "cip.cm.fwo.dir",
+         FT_UINT8, BASE_DEC, VALS(cip_con_dir_vals), 0x80,
+         "Fwd Open: Direction", HFILL }
+      },
+      { &hf_cip_cm_fwo_trigg,
+         { "Trigger", "cip.cm.fwo.trigger",
+         FT_UINT8, BASE_DEC, VALS(cip_con_trigg_vals), 0x70,
+         "Fwd Open: Production trigger", HFILL }
+      },
+      { &hf_cip_cm_fwo_class,
+         { "Class", "cip.cm.fwo.transport",
+         FT_UINT8, BASE_DEC, VALS(cip_con_class_vals), 0x0F,
+         "Fwd Open: Transport Class", HFILL }
+      }
+   };
+
+   static hf_register_info hf_mr[] = {
+      { &hf_cip_mr_rr,
+         { "Request/Response", "cip.rr",
+         FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
+         "Request or Response message", HFILL }
+      },
+      { &hf_cip_mr_sc,
+         { "Service", "cip.sc",
+         FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
+         "Service Code", HFILL }
+      }
+   };
+
+   static hf_register_info hf_cco[] = {
+      { &hf_cip_cco_rr,
+         { "Request/Response", "cip.rr",
+         FT_UINT8, BASE_HEX, VALS(cip_sc_rr), 0x80,
+         "Request or Response message", HFILL }
+      },
+      { &hf_cip_cco_sc,
+         { "Service", "cip.sc",
+         FT_UINT8, BASE_HEX, VALS(cip_sc_vals), 0x7F,
+         "Service Code", HFILL }
+      }
    };
 
    /* Setup protocol subtree array */
-       static gint *ett[] = {
-               &ett_cip,
-               &ett_path,
-               &ett_ekey_path,
-               &ett_rrsc,
-               &ett_mcsc,
-               &ett_ncp,
-               &ett_cia_path,
-               &ett_data_seg,
-               &ett_lsrcf,
-               &ett_mes_req,
-               &ett_cmd_data,
-               &ett_port_path,
-               &ett_mult_ser,
-               &ett_path,
-               &ett_status_item
-       };
+   static gint *ett[] = {
+      &ett_cip_class_generic,
+      &ett_cip,
+      &ett_path,
+      &ett_ekey_path,
+      &ett_rrsc,
+      &ett_mcsc,
+      &ett_cia_path,
+      &ett_data_seg,
+      &ett_cmd_data,
+      &ett_port_path,
+      &ett_status_item
+   };
+
+   static gint *ett_mr[] = {
+      &ett_cip_class_mr,
+      &ett_mr_rrsc,
+      &ett_mr_mult_ser,
+      &ett_mr_cmd_data
+   };
+
+   static gint *ett_cm[] = {
+      &ett_cip_class_cm,
+      &ett_cm_rrsc,
+      &ett_cm_mes_req,
+      &ett_cm_ncp,
+      &ett_cm_cmd_data
+   };
+
+   static gint *ett_cco[] = {
+      &ett_cip_class_cco,
+      &ett_cco_rrsc,
+      &ett_cco_cmd_data
+    };
 
    /* Register the protocol name and description */
    proto_cip = proto_register_protocol("Common Industrial Protocol",
-           "CIP", "cip");
+       "CIP", "cip");
 
    /* Required function calls to register the header fields and subtrees used */
    proto_register_field_array(proto_cip, hf, array_length(hf));
    proto_register_subtree_array(ett, array_length(ett));
+   subdissector_class_table = register_dissector_table("cip.class.iface",
+      "CIP Class Interface Handle", FT_UINT32, BASE_HEX);
+   subdissector_symbol_table = register_dissector_table("cip.data_segment.iface",
+      "CIP Data Segment Interface Handle", FT_UINT32, BASE_HEX);
+
+   /* Register the protocol name and description */
+   proto_cip_class_generic = proto_register_protocol("CIP Class Generic",
+       "CIPCLS", "cipcls");
 
+   /* Register the protocol name and description */
+   proto_cip_class_mr = proto_register_protocol("CIP Message Router",
+       "CIPMR", "cipmr");
+   proto_register_field_array(proto_cip_class_mr, hf_mr, array_length(hf_mr));
+   proto_register_subtree_array(ett_mr, array_length(ett_mr));
+
+   proto_cip_class_cm = proto_register_protocol("CIP Connection Manager",
+       "CIPCM", "cipcm");
+   proto_register_field_array(proto_cip_class_cm, hf_cm, array_length(hf_cm));
+   proto_register_subtree_array(ett_cm, array_length(ett_cm));
+
+   proto_cip_class_cco = proto_register_protocol("CIP Connection Configuration Object",
+       "CIPCCO", "cipcco");
+   proto_register_field_array(proto_cip_class_cco, hf_cco, array_length(hf_cco));
+   proto_register_subtree_array(ett_cco, array_length(ett_cco));
+
+   register_init_routine(&cip_init_protocol);
 } /* end of proto_register_cip() */
 
 
 void
 proto_reg_handoff_cip(void)
 {
-   dissector_handle_t cip_handle;
-
    /* Create dissector handles */
-   cip_handle = new_create_dissector_handle( dissect_cip, proto_cip );
-
    /* Register for UCMM CIP data, using EtherNet/IP SendRRData service*/
-   dissector_add( "enip.srrd.iface", ENIP_CIP_INTERFACE, cip_handle );
-
    /* Register for Connected CIP data, using EtherNet/IP SendUnitData service*/
+   cip_handle = new_create_dissector_handle( dissect_cip, proto_cip );
+   dissector_add( "enip.srrd.iface", ENIP_CIP_INTERFACE, cip_handle );
    dissector_add( "enip.sud.iface", ENIP_CIP_INTERFACE, cip_handle );
 
+   /* Create and register dissector handle for generic class */
+   cip_class_generic_handle = new_create_dissector_handle( dissect_cip_class_generic, proto_cip_class_generic );
+   dissector_add( "cip.class.iface", 0, cip_class_generic_handle );
+
+   /* Create and register dissector handle for Message Router */
+   cip_class_mr_handle = new_create_dissector_handle( dissect_cip_class_mr, proto_cip_class_mr );
+   dissector_add( "cip.class.iface", CI_CLS_MR, cip_class_mr_handle );
+
+   /* Create and register dissector handle for Connection Manager */
+   cip_class_cm_handle = new_create_dissector_handle( dissect_cip_class_cm, proto_cip_class_cm );
+   dissector_add( "cip.class.iface", CI_CLS_CM, cip_class_cm_handle );
+
+   /* Create and register dissector handle for Connection Configuration Object */
+   cip_class_cco_handle = new_create_dissector_handle( dissect_cip_class_cco, proto_cip_class_cco );
+   dissector_add( "cip.class.iface", CI_CLS_CCO, cip_class_cco_handle );
+
 } /* end of proto_reg_handoff_cip() */
index a900362c69ea47fe1ffa0891eb42ed69b782bc58..e36c797e2376d2c233293e364b37750cd0a275d5 100644 (file)
@@ -8,8 +8,6 @@
  *
  * Added support for Connection Configuration Object
  *   ryan wamsley * Copyright 2007
- * Added Additional Status text in Forward Open Response
- *   ryan wamsley * Copyright 2008
  *
  * $Id$
  *
 #define SC_GET_MEMBER            0x18
 #define SC_SET_MEMBER            0x19
 /* Class specific services */
-#define SC_FWD_CLOSE             0x4E
-#define SC_UNCON_SEND            0x52
-#define SC_FWD_OPEN              0x54
+/* Connection Manager */
+#define SC_CM_FWD_CLOSE             0x4E
+#define SC_CM_UNCON_SEND            0x52
+#define SC_CM_FWD_OPEN              0x54
 /* Connection Configuration Object services */
-#define SC_KICK_TIMER            0x4B
-#define SC_OPEN_CONN             0x4C
-#define SC_CLOSE_CONN            0x4D
-#define SC_STOP_CONN             0x4E  /* collision with SC_FWD_CLOSE */
-#define SC_CHANGE_START          0x4F
-#define SC_GET_STATUS            0x50
-#define SC_CHANGE_COMPLETE       0x51
-#define SC_AUDIT_CHANGE          0x52  /* collision with SC_UNCON_SEND */
-
+#define SC_CCO_KICK_TIMER            0x4B
+#define SC_CCO_OPEN_CONN             0x4C
+#define SC_CCO_CLOSE_CONN            0x4D
+#define SC_CCO_STOP_CONN             0x4E
+#define SC_CCO_CHANGE_START          0x4F
+#define SC_CCO_GET_STATUS            0x50
+#define SC_CCO_CHANGE_COMPLETE       0x51
+#define SC_CCO_AUDIT_CHANGE          0x52
 
 /* CIP Genral status codes */
 #define CI_GRC_SUCCESS              0x00
 
 #define CI_GRC_STILL_PROCESSING     0xFF
 
-/* Extended Status Error Codes */
-#define CI_SREC_CONNECTION_IN_USE      0x0100
-#define CI_SREC_TCLASS_TRIGGER_ERR     0x0103
-#define CI_SREC_OWNERSHIP_CONFLICT     0x0106
-#define CI_SREC_CONN_NOT_FOUND         0x0107
-#define CI_SREC_INVALID_CONN_TYPE      0x0108
-#define CI_SREC_INVALID_CONN_SIZE      0x0109
-#define CI_SREC_DEV_NOT_CONFIGURED     0x0110
-#define CI_SREC_UNSUPPORTED_RPI        0x0111
-#define CI_SREC_NO_MORE_CONNS          0x0113
-#define CI_SREC_VEN_OR_PCODE_MISMATCH  0x0114
-#define CI_SREC_PRODTYPE_MISMATCH      0x0115
-#define CI_SREC_REVISION_MISMATCH      0x0116
-#define CI_SREC_BAD_CONN_POINT         0x0117
-#define CI_SREC_INVAL_CONFIG_FRMT      0x0118
-#define CI_SREC_NO_CONTROL_CONN        0x0119
-#define CI_SREC_NO_MORE_CONN_SUPPORT   0x011A
-#define CI_SREC_RPI_SMALLERTHAN_PIT    0x011B
-#define CI_SREC_CONN_ALREADY_CLOSED    0x0203
-#define CI_SREC_UNCONN_SND_TIMEOUT     0x0204
-#define CI_SREC_UNCONN_PARM_ERR        0x0205
-#define CI_SREC_UCONN_TOO_LARGE        0x0206
-#define CI_SREC_UCONN_ACK_NO_REP       0x0207
-#define CI_SREC_NO_MEMORY              0x0301
-#define CI_SREC_NO_NET_BANDWIDTH       0x0302
-#define CI_SREC_NO_SCREENERS           0x0303
-#define CI_SREC_NO_REALTIME_CONFIG     0x0304
-#define CI_SREC_INVALID_PORT           0x0311
-#define CI_SREC_LINKADDR_NOT_AVAIL     0x0312
-#define CI_SREC_INVALID_SEGMENT_TYP    0x0315
-#define CI_SREC_CLOSE_PATH_ERR         0x0316
-#define CI_SREC_NO_SCHED               0x0317
-#define CI_SREC_INVALID_LINK_ADDR      0x0318
-#define CI_SREC_UNAVAIL_RESOURCE       0x0319
-#define CI_SREC_CONN_ALREADY_ESTAB     0x031A
-#define CI_SREC_DCONN_ALREADY_ESTAB    0x031B
-#define CI_SREC_MISC                   0x031C
-#define CI_SREC_REDUNDANT_MISMATCH     0x031D
-#define CI_SREC_NO_CONSUME_RESRC       0x031E
-#define CI_SREC_NO_CONN_RESRC          0x031F
-
 
 /* IOI Path types */
 #define CI_SEGMENT_TYPE_MASK        0xE0
 
 /* Device Profile:s */
 #define DP_GEN_DEV                           0x00
-#define DP_AC_DRIVE                            0x02
+#define DP_AC_DRIVE                          0x02
 #define DP_MOTOR_OVERLOAD                    0x03
 #define DP_LIMIT_SWITCH                      0x04
 #define DP_IND_PROX_SWITCH                   0x05
    { 899,   "Practicon Ltd" }, \
    { 900,   "Schunk GmbH & Co. KG" }, \
    { 902,   "Defontaine Groupe" }, \
-   { 903,   "Emerson Process Management Power & Water Solutions" },
-
+   { 903,   "Emerson Process Management Power & Water Solutions" }, \
+   { 981,   "Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG" },
 
 /*
 ** Exported variables
index 31ce1ce1a9cd1f5e5739731e998a585b609080f7..ce92991ba951948570a1163ded26270efb34e3a8 100644 (file)
@@ -6,6 +6,10 @@
  * 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
 #include <glib.h>
 
 #include <epan/packet.h>
-#include <prefs.h>
+#include <epan/emem.h>
+#include <epan/conversation.h>
+#include <epan/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  */
-
-/* Return codes of function classifying packets as query/response */
-#define REQUEST_PACKET    0
-#define RESPONSE_PACKET                1
-#define CANNOT_CLASSIFY                2
+#define ENIP_ENCAP_PORT    44818 /* EtherNet/IP located on port 44818    */
+#define ENIP_IO_PORT       2222  /* EtherNet/IP IO located on port 2222  */
 
 /* EtherNet/IP function codes */
 #define NOP                0x0000
@@ -118,6 +121,10 @@ 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;
@@ -135,322 +142,762 @@ 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( int command, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset, guint32 ifacehndl )
+dissect_cpf( enip_request_key_t *request_key, 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;
-
-       /* 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 ) );
+   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;
 
-               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;
+   /* 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 );
 
-                           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 );
-                                       }
+   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 );
 
-                               }
-                               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 */
+      /* 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 ) );
 
-                               break;
+      item        = tvb_get_letohs( tvb, offset+2 );
+      item_length = tvb_get_letohs( tvb, offset+4 );
 
+      if( item_length )
+      {
+         /* Add item data field */
 
-                           case LIST_IDENTITY_RESP:
+         switch( item )
+         {
+            case CONNECTION_BASED:
 
-                               /* 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 );
+               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 )  );
 
-                               /* 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 );
+               /* 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 struct - sin_family */
-                               proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinfamily,
-                                                   tvb, offset+8, 2, FALSE );
+               break;
 
-                               /* Socket address struct - sin_port */
-                               proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinport,
-                                                   tvb, offset+10, 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_address */
-                               proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinaddr,
-                                                   tvb, offset+12, 4, 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_zero */
-                               proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinzero,
-                                                   tvb, offset+16, 8, FALSE );
+               break;
 
-                               /* Vendor ID */
-                               proto_tree_add_item(item_tree, hf_enip_lir_vendor,
-                                                   tvb, offset+24, 2, TRUE );
+            case CONNECTION_TRANSPORT:
 
-                               /* Device Type */
-                               proto_tree_add_item(item_tree, hf_enip_lir_devtype,
-                                                   tvb, offset+26, 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: " );
 
-                               /* Product Code */
-                               proto_tree_add_item(item_tree, hf_enip_lir_prodcode,
-                                                   tvb, offset+28, 2, TRUE );
+               } /* End of if send unit data */
 
-                               /* 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 );
+               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 LIST_IDENTITY_RESP:
 
-                               /* 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 );
+               /* 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 */
-                               proto_tree_add_item(item_tree, hf_enip_lir_name,
-                                                   tvb, offset+39, name_length, TRUE );
+               /* 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 );
 
-                               /* 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_family */
+               proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinfamily,
+                     tvb, offset+8, 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_port */
+               proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinport,
+                     tvb, offset+10, 2, FALSE );
 
+               /* Socket address struct - sin_address */
+               proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinaddr,
+                     tvb, offset+12, 4, FALSE );
 
-                           case SOCK_ADR_INFO_OT:
-                           case SOCK_ADR_INFO_TO:
+               /* Socket address struct - sin_zero */
+               proto_tree_add_item(sockaddr_tree, hf_enip_lir_sinzero,
+                     tvb, offset+16, 8, FALSE );
 
-                               /* Socket address struct - sin_family */
-                               proto_tree_add_item(item_tree, hf_enip_lir_sinfamily,
-                                                   tvb, offset+6, 2, FALSE );
+               /* Vendor ID */
+               proto_tree_add_item(item_tree, hf_enip_lir_vendor,
+                     tvb, offset+24, 2, TRUE );
 
-                               /* Socket address struct - sin_port */
-                               proto_tree_add_item(item_tree, hf_enip_lir_sinport,
-                                                   tvb, offset+8, 2, FALSE );
+               /* Device Type */
+               proto_tree_add_item(item_tree, hf_enip_lir_devtype,
+                     tvb, offset+26, 2, TRUE );
 
-                               /* Socket address struct - sin_address */
-                               proto_tree_add_item(item_tree, hf_enip_lir_sinaddr,
-                                                   tvb, offset+10, 4, FALSE );
+               /* Product Code */
+               proto_tree_add_item(item_tree, hf_enip_lir_prodcode,
+                     tvb, offset+28, 2, TRUE );
 
-                               /* Socket address struct - sin_zero */
-                               proto_tree_add_item( item_tree, hf_enip_lir_sinzero,
-                                                    tvb, offset+14, 8, FALSE );
-                               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 SEQ_ADDRESS:
-                               proto_tree_add_item(item_tree, hf_enip_cpf_sai_connid,
-                                                   tvb, offset+6, 4, TRUE );
+               /* Serial Number */
+               proto_tree_add_item(item_tree, hf_enip_lir_serial,
+                     tvb, offset+34, 4, TRUE );
 
-                               proto_tree_add_item(item_tree, hf_enip_cpf_sai_seqnum,
-                                                   tvb, offset+10, 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 );
 
-                               /* Add info to column */
+               /* Product Name */
+               proto_tree_add_item(item_tree, hf_enip_lir_name,
+                     tvb, offset+39, name_length, TRUE );
 
-                               if(check_col(pinfo->cinfo, COL_INFO))
-                               {
-                                       col_clear(pinfo->cinfo, COL_INFO);
+               /* 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));
+               }
 
-                                       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 ) );
-                               }
+               /* State */
+               proto_tree_add_item(item_tree, hf_enip_lir_state,
+                     tvb, offset+name_length+39, 1, TRUE );
+               break;
 
-                               break;
 
-                           case LIST_SERVICES_RESP:
+            case SOCK_ADR_INFO_OT:
+            case SOCK_ADR_INFO_TO:
 
-                               /* 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_family */
+               proto_tree_add_item(item_tree, hf_enip_lir_sinfamily,
+                     tvb, offset+6, 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_port */
+               proto_tree_add_item(item_tree, hf_enip_lir_sinport,
+                     tvb, offset+8, 2, 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_address */
+               proto_tree_add_item(item_tree, hf_enip_lir_sinaddr,
+                     tvb, offset+10, 4, FALSE );
 
-                               /* 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) );
+               /* 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);
 
-                               /* 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) );
-                               }
+                  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;
 
-                               break;
+            case LIST_SERVICES_RESP:
 
+               /* 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 );
 
-                           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;
+               /* 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);
 
-                       } /* end of switch( item type ) */
+               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 if( item length ) */
+               /* 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) );
 
-               offset = offset + item_length + 4;
+               /* 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;
 
-       } /* end of while( item count ) */
+
+            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 dissect_cpf() */
 
@@ -459,17 +906,17 @@ dissect_cpf( int command, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, i
 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 RESPONSE_PACKET;
-               else if ( ENIP_ENCAP_PORT == pinfo->destport )
-                       return REQUEST_PACKET;
-       }
-       /* else, cannot classify */
-       return 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 ENIP_RESPONSE_PACKET;
+      else if ( ENIP_ENCAP_PORT == pinfo->destport )
+         return ENIP_REQUEST_PACKET;
+   }
+   /* else, cannot classify */
+   return ENIP_CANNOT_CLASSIFY;
 }
 
 static guint
@@ -493,17 +940,16 @@ 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;
-   tvbuff_t *next_tvb;
+   enip_request_key_t request_key;
+   conversation_t *conversation;
 
    /* Set up structures needed to add the protocol subtree and manage it */
    proto_item *ti, *encaph, *csf;
-   proto_tree *enip_tree = NULL;
-   proto_tree *header_tree = NULL;
-   proto_tree *csftree;
+   proto_tree *enip_tree, *header_tree = NULL, *csftree;
 
    /* Make entries in Protocol column and Info column on summary display */
    if (check_col(pinfo->cinfo, COL_PROTOCOL))
@@ -512,19 +958,18 @@ 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 );
-   encap_data_length = tvb_get_letohs( tvb, 2 );
+
+   packet_type = classify_packet(pinfo);
 
    if( check_col(pinfo->cinfo, COL_INFO) )
    {
-      packet_type = classify_packet(pinfo);
-
       switch ( packet_type )
       {
-         case REQUEST_PACKET:
+         case ENIP_REQUEST_PACKET:
             pkt_type_str="Req";
             break;
 
-         case RESPONSE_PACKET:
+         case ENIP_RESPONSE_PACKET:
             pkt_type_str="Rsp";
             break;
 
@@ -534,16 +979,48 @@ 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);
 
@@ -556,6 +1033,7 @@ 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 );
@@ -567,100 +1045,95 @@ 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)" ) );
 
-   } /* end of if (tree) */
-
-   /*
-   ** For some commands we want to add some info to the info column
-   */
+      /*
+      ** For some commands we want to add some info to the info column
+      */
 
-   if( check_col( pinfo->cinfo, COL_INFO ) )
-   {
-
-      switch( encap_cmd )
+      if( check_col( pinfo->cinfo, COL_INFO ) )
       {
-         case REGISTER_SESSION:
-         case UNREGISTER_SESSION:
-               col_append_fstr( pinfo->cinfo, COL_INFO, ", Session: 0x%08X",
-                                tvb_get_letohl( tvb, 4 ) );
 
-      } /* 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 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 */
+   } /* end of tree */
 
-         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( encap_cmd, tvb, pinfo, csftree, 24, 0 );
-               break;
+      switch( encap_cmd )
+      {
+         case NOP:
+            break;
 
-            case LIST_IDENTITY:
-               dissect_cpf( encap_cmd, tvb, pinfo, csftree, 24, 0 );
-               break;
+         case LIST_SERVICES:
+            dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 24, 0 );
+            break;
 
-            case LIST_INTERFACES:
-               dissect_cpf( encap_cmd, tvb, pinfo, csftree, 24, 0 );
-               break;
+         case LIST_IDENTITY:
+            dissect_cpf( &request_key, 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( &request_key, 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( 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 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 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;
+            ifacehndl = tvb_get_letohl( tvb, 24 );
+            dissect_cpf( &request_key, encap_cmd, tvb, pinfo, csftree, 30, ifacehndl );
+            break;
 
-         } /* end of switch() */
+         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;
 
-      } /* end of if( encapsulated data ) */
+      } /* end of switch() */
 
+   } /* end of if( encapsulated data ) */
 } /* end of dissect_enip_pdu() */
 
 static int
@@ -671,13 +1144,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_length(tvb) < 4)
+   if (!tvb_bytes_exist(tvb, 0, 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);
@@ -691,55 +1164,45 @@ 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_length(tvb) < 4)
+   if (!tvb_bytes_exist(tvb, 0, 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 int
+static void
 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 = NULL;
-       guint16 type_id;
-
-       g_tree = tree;
+   proto_item *ti;
+   proto_tree *enip_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 */
+   g_tree = tree;
 
    /* 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( 0xFFFF, tvb, pinfo, enip_tree, 0, 0 );
+      dissect_cpf( NULL, 0xFFFF, tvb, pinfo, enip_tree, 0, 0 );
+   }
 
-       return tvb_length(tvb);
 } /* end of dissect_enipio() */
 
 
@@ -753,161 +1216,178 @@ 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 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 */
+   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 */
       { &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_NONE, NULL, 0,
-                       "ListIdentity Reply: Socket Address.Sin Addr", HFILL }
-               },
+         { "sin_addr", "enip.lir.sa.sinaddr",
+         FT_IPv4, BASE_NONE, 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_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 }
+      },
       { &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 }
-               }
+         { "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 }
+      }
    };
 
 
 /* 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() */
 
 
@@ -918,22 +1398,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 = new_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 = 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() */
diff --git a/epan/dissectors/packet-enip.h b/epan/dissectors/packet-enip.h
new file mode 100644 (file)
index 0000000..018ca29
--- /dev/null
@@ -0,0 +1,40 @@
+/* packet-enip.h
+ * Routines for EtherNet/IP (Industrial Protocol) dissection
+ * EtherNet/IP Home: www.odva.org
+ *
+ * Conversation data support for CIP
+ *   Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG
+ *   Copyright 2007
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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.
+ */
+
+typedef struct {
+   guint32 req_num, rep_num;
+   nstime_t req_time;
+   void *cip_info;
+} enip_request_info_t;
+
+void enip_open_cip_connection( packet_info *pinfo,
+                               guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber,
+                               guint32 O2TConnID, guint32 T2OConnID );
+void enip_close_cip_connection( packet_info *pinfo,
+                                guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber );