/* packet-cip.c
 * Routines for Common Industrial Protocol (CIP) dissection
 * CIP Home: www.odva.org
 *
 * Copyright 2004
 * Magnus Hansson <mah@hms.se>
 * Joakim Wiberg <jow@hms.se>
 *
 * Added support for Connection Configuration Object
 *   ryan wamsley * Copyright 2007
 * Added Additional Status text in Forward Open Response
 *   ryan wamsley * Copyright 2008
 *
 * $Id: packet-cip.c 27050 2008-12-18 19:08:49Z wmeier $
 *
 * 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.
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glib.h>

#include <epan/packet.h>
#include "packet-tcp.h"
#include "packet-cip.h"

#define  ENIP_CIP_INTERFACE   0

/* 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 hf_cip_vendor              = -1;
static int hf_cip_devtype             = -1;
static int hf_cip_port                = -1;
static int hf_cip_link_address_byte   = -1;
static int hf_cip_link_address_string = -1;
static int hf_cip_class8              = -1;
static int hf_cip_class16             = -1;
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_attribute8          = -1;
static int hf_cip_attribute16         = -1;
static int hf_cip_attribute32         = -1;
static int hf_cip_conpoint8           = -1;
static int hf_cip_conpoint16          = -1;
static int hf_cip_conpoint32          = -1;
static int hf_cip_symbol              = -1;

static int hf_cip_data                = -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;


/* 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 }
};

/* 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 */

	{ 0,				            NULL }
};

/* Translate function to string - CIP Request/Response */
static const value_string cip_sc_rr[] = {
	{ 0,	      "Request"  },
	{ 1,	      "Response" },

	{ 0,			NULL }
};

/* Translate function to string - Compatibility */
static const value_string cip_com_bit_vals[] = {
	{ 0,	      "Bit Cleared" },
	{ 1,	      "Bit Set"     },

	{ 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,        NULL            }
};

/* Translate function to string - Connection size fixed or variable */
static const value_string cip_con_fw_vals[] = {
	{ 0,	      "Fixed"    },
	{ 1,	      "Variable" },

	{ 0,        NULL       }
};

/* Translate function to string - Connection owner */
static const value_string cip_con_owner_vals[] = {
	{ 0,	      "Exclusive" },
	{ 1,	      "Redundant" },

	{ 0,        NULL        }
};

/* Translate function to string - Connection direction */
static const value_string cip_con_dir_vals[] = {
	{ 0,	      "Client" },
	{ 1,	      "Server" },

	{ 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,        NULL        }
};

/* Translate function to string - Transport class */
static const value_string cip_con_class_vals[] = {
	{ 0,	      "0" },
	{ 1,	      "1" },
	{ 2,	      "2" },
	{ 3,	      "3" },

	{ 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             }
};

/* 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,        NULL    }
};

/* Translate function to string - CIP General Status codes */
static const value_string cip_gs_vals[] = {
	{ CI_GRC_SUCCESS,             "Success" },
   { CI_GRC_FAILURE,             "Connection failure" },
   { CI_GRC_NO_RESOURCE,         "Resource unavailable" },
   { CI_GRC_BAD_DATA,            "Invalid parameter value" },
   { CI_GRC_BAD_PATH,            "Path segment error" },
   { CI_GRC_BAD_CLASS_INSTANCE,  "Path destination unknown" },
   { CI_GRC_PARTIAL_DATA,        "Partial transfer" },
   { CI_GRC_CONN_LOST,           "Connection lost" },
   { CI_GRC_BAD_SERVICE,         "Service not supported" },
   { CI_GRC_BAD_ATTR_DATA,       "Invalid attribute value" },
   { CI_GRC_ATTR_LIST_ERROR,     "Attribute list error" },
   { CI_GRC_ALREADY_IN_MODE,     "Already in requested mode/state" },
   { CI_GRC_BAD_OBJ_MODE,        "Object state conflict" },
   { CI_GRC_OBJ_ALREADY_EXISTS,  "Object already exists" },
   { CI_GRC_ATTR_NOT_SETTABLE,   "Attribute not settable" },
   { CI_GRC_PERMISSION_DENIED,   "Privilege violation" },
   { CI_GRC_DEV_IN_WRONG_STATE,  "Device state conflict" },
   { CI_GRC_REPLY_DATA_TOO_LARGE,"Reply data too large" },
   { CI_GRC_FRAGMENT_PRIMITIVE,  "Fragmentation of a primitive value" },
   { CI_GRC_CONFIG_TOO_SMALL,    "Not enough data" },
   { CI_GRC_UNDEFINED_ATTR,      "Attribute not supported" },
   { CI_GRC_CONFIG_TOO_BIG,      "Too much data" },
   { CI_GRC_OBJ_DOES_NOT_EXIST,  "Object does not exist" },
   { CI_GRC_NO_FRAGMENTATION,    "Service fragmentation sequence not in progress" },
   { CI_GRC_DATA_NOT_SAVED,      "No stored attribute data" },
   { CI_GRC_DATA_WRITE_FAILURE,  "Store operation failure" },
   { CI_GRC_REQUEST_TOO_LARGE,   "Routing failure, request packet too large" },
   { CI_GRC_RESPONSE_TOO_LARGE,  "Routing failure, response packet too large" },
   { CI_GRC_MISSING_LIST_DATA,   "Missing attribute list entry data" },
   { CI_GRC_INVALID_LIST_STATUS, "Invalid attribute value list" },
   { CI_GRC_SERVICE_ERROR,       "Embedded service error" },
   { CI_GRC_CONN_RELATED_FAILURE,"Vendor specific error" },
   { CI_GRC_INVALID_PARAMETER,   "Invalid parameter" },
   { CI_GRC_WRITE_ONCE_FAILURE,  "Write-once value or medium already written" },
   { CI_GRC_INVALID_REPLY,       "Invalid Reply Received" },
   { CI_GRC_BAD_KEY_IN_PATH,     "Key Failure in path" },
   { CI_GRC_BAD_PATH_SIZE,       "Path Size Invalid" },
   { CI_GRC_UNEXPECTED_ATTR,     "Unexpected attribute in list" },
   { CI_GRC_INVALID_MEMBER,      "Invalid Member ID" },
   { CI_GRC_MEMBER_NOT_SETTABLE, "Member not settable" },

  	{ 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 }
};

/* Translate Device Profile:s */
const value_string cip_devtype_vals[] = {
   { DP_GEN_DEV,              "Generic Device"              },
   { DP_AC_DRIVE,             "AC Drive"                    },
   { DP_MOTOR_OVERLOAD,       "Motor Overload"              },
   { DP_LIMIT_SWITCH,         "Limit Switch"                },
   { DP_IND_PROX_SWITCH,      "Inductive Proximity Switch"  },
   { DP_PHOTO_SENSOR,         "Photoelectric Sensor"        },
   { DP_GENP_DISC_IO,         "General Purpose Discrete I/O"},
   { DP_RESOLVER,             "Resolver"                    },
   { DP_COM_ADAPTER,          "Communications Adapter"      },
   { DP_POS_CNT,              "Position Controller",        },
   { DP_DC_DRIVE,             "DC Drive"                    },
   { DP_CONTACTOR,            "Contactor",                  },
   { DP_MOTOR_STARTER,        "Motor Starter",              },
   { DP_SOFT_START,           "Soft Start",                 },
   { DP_HMI,                  "Human-Machine Interface"     },
   { DP_MASS_FLOW_CNT,        "Mass Flow Controller"        },
   { DP_PNEUM_VALVE,          "Pneumatic Valve"             },
   { DP_VACUUM_PRES_GAUGE,    "Vacuum Pressure Gauge"       },

   { 0, NULL }
};

/* 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                                    }
};


/* Dissect EPATH */
static void
dissect_epath( tvbuff_t *tvb, proto_item *epath_item, int offset, int path_length )
{
   int pathpos, temp_data, temp_data2, seg_size, i, temp_word;
   unsigned char segment_type, opt_link_size;
   proto_tree *path_tree, *port_tree, *net_tree;
   proto_item *qi, *cia_item, *ds_item;
   proto_tree *e_key_tree, *cia_tree, *ds_tree;
   proto_item *mcpi, *port_item, *net_item;
   proto_tree *mc_tree;
   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);

   pathpos = 0;

   while( pathpos < path_length )
   {
      /* Get segement type */
      segment_type = tvb_get_guint8( tvb, offset + pathpos );

      /* Determine the segment type */

      switch( segment_type & CI_SEGMENT_TYPE_MASK )
      {
      case CI_PORT_SEGMENT:

         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 );
         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" );

            /* 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  );

            /* Add extended link address */
            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) );

            /* Pad byte */
            if( opt_link_size % 2 )
            {
              proto_item_set_len( port_item, 3 + opt_link_size );
              pathpos = pathpos + 3 + opt_link_size;
            }
            else
            {
              proto_item_set_len( port_item, 2 + opt_link_size );
              pathpos = pathpos + 2 + opt_link_size;
            }
         }
         else
         {
            /* Add Extended Link Address flag */
            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 );
            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 ) );

            proto_item_set_len( port_item, 2 );
            pathpos += 2;
         }

         break;

      case CI_LOGICAL_SEGMENT:

         /* Logical segment, determin the logical type */

         switch( segment_type & CI_LOGICAL_SEG_TYPE_MASK )
         {
         case CI_LOGICAL_SEG_CLASS_ID:

            /* 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 );

               /* 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 );
               proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%02X" ) );

               /* 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 );
               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 );
               proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%04X" ) );

               /* 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 );
               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 );
               proto_item_append_text( epath_item, "%s", val_to_str( temp_data, cip_class_names_vals , "Class: 0x%08X" ) );

               /* 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_INST_ID:

            /* 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 );

               /* 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 );
               proto_item_append_text( epath_item, "Instance: 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 );
               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 );
               proto_item_append_text( epath_item, "Instance: 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 );
               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 );
               proto_item_append_text( epath_item, "Instance: 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 );

               /* 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 );
               proto_item_append_text( epath_item, "Attribute: 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 );
               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 );
               proto_item_append_text( epath_item, "Attribute: 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 );
               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 );
               proto_item_append_text( epath_item, "Attribute: 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_CON_POINT:

            /* 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 );
               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 */
               cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );

               /* Display the 8-bit connection point number */
               proto_tree_add_item( cia_tree, hf_cip_conpoint8, tvb, offset + pathpos + 1, 1, TRUE );
               proto_item_append_text( epath_item, "Connection Point: 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 );
               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 4, "16-Bit Logical Connection Point Segment (0x%02X)", segment_type );

               /* Create a sub tree for the connection point */
               cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );

               /* Display the 16-bit connection point number */
               proto_tree_add_item( cia_tree, hf_cip_conpoint16, tvb, offset + pathpos + 2, 2, TRUE );
               proto_item_append_text( epath_item, "Connection Point: 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 );
               cia_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 6, "32-Bit Logical Connection Point Segment (0x%02X)", segment_type );

               /* Create a sub tree for the connection point */
               cia_tree = proto_item_add_subtree( cia_item, ett_cia_path );

               /* Display the 32-bit connection point number */
               proto_tree_add_item( cia_tree, hf_cip_conpoint32, tvb, offset + pathpos + 2, 4, TRUE );
               proto_item_append_text( epath_item, "Connection Point: 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_SPECIAL:

            /* Logical Special ID, the only logical format sepcifyed is electronic key */

            if( ( segment_type & CI_LOGICAL_SEG_FORMAT_MASK ) == CI_LOGICAL_SEG_E_KEY )
            {
               /* Get the Key Format */
               temp_data = tvb_get_guint8( tvb, offset + pathpos + 1 );

               if( temp_data == CI_E_KEY_FORMAT_VAL )
               {
                  qi = proto_tree_add_text( path_tree, tvb, offset + pathpos, 10, "Electronic Key Segment (0x%02X): ",segment_type );

                  /* Create a sub tree for the IOI */
                  e_key_tree = proto_item_add_subtree( qi, ett_ekey_path );

                  /* Print the key type */
                  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 );
                  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);
                  proto_item_append_text( qi, ", DevTyp: 0x%04X", temp_data );

                  /* Product Code */
   		         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 );

   					/* 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 */
                  proto_tree_add_item(mc_tree, hf_cip_fwo_comp,
   							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 );

                  /* Minor revision */
                  temp_data2 = tvb_get_guint8( tvb, offset + pathpos + 9 );
                  proto_tree_add_text( e_key_tree, tvb, offset + pathpos + 9, 1, "Minor Revision: %d", temp_data2 );

                  proto_item_append_text( qi, ", %d.%d", ( temp_data & 0x7F ), temp_data2 );

                  proto_item_append_text(epath_item, "[Key]" );

                  /* Increment the path pointer */
                  pathpos += 10;
               }
               else
               {
                  /* Unsupported electronic key format */
                  proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Electronic Key Format" );
                  return;
               }
            }
            else
            {
               /* Unsupported special segment format */
               proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Special Segment Format" );
               return;
            }
            break;

         default:

            /* Unsupported logical segment type */
            proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Logical Segment Type" );
            return;

         } /* end of switch( segment_type & CI_LOGICAL_SEG_TYPE_MASK ) */
         break;


      case CI_DATA_SEGMENT:

         /* Data segment, determin the logical type */

         switch( segment_type )
         {
            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 );

               /* 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 )*2;
               proto_tree_add_text( ds_tree, tvb, offset + pathpos+1, 1, "Data Size: %d (words)", seg_size/2 );

               /* Segment data  */
               if( seg_size != 0 )
               {
                  qi = proto_tree_add_text( ds_tree, tvb, offset + pathpos+2, 0, "Data: " );

                  for( i=0; i < seg_size/2; i ++ )
                  {
                    temp_word = tvb_get_letohs( tvb, offset + pathpos+2+(i*2) );
                    proto_item_append_text(qi, " 0x%04X", temp_word );
                  }

                  proto_item_set_len(qi, seg_size);
               }

               proto_item_set_len( ds_item, 2 + seg_size );
               pathpos = pathpos + 2 + seg_size;

               proto_item_append_text(epath_item, "[Data]" );

               break;

            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 );

               /* 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 );

               /* 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 ) );

                  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++;
                     seg_size++;
                  }
               }

               proto_item_set_len( ds_item, 2 + seg_size );
               pathpos = pathpos + 2 + seg_size;

               break;

            default:
               proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Sub-Segment Type" );
               return;

            } /* End of switch sub-type */

            break;

      case CI_NETWORK_SEGMENT:

         /* Network segment -Determine the segment sub-type */

         switch( segment_type & CI_NETWORK_SEG_TYPE_MASK )
         {
           case CI_NETWORK_SEG_SCHEDULE:
               net_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "Network Segment - Schedule" );
               net_tree = proto_item_add_subtree( net_item, ett_port_path );

               proto_tree_add_text( net_tree, tvb, offset + pathpos + 1, 1, "Multiplier/Phase: %02X", tvb_get_guint8( tvb, offset + pathpos + 1 ) );

               /* 2 bytes of path used */
               pathpos += 2;
               break;

            case CI_NETWORK_SEG_FIXED_TAG:
               net_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "Network Segment - Fixed Tag" );
               net_tree = proto_item_add_subtree( net_item, ett_port_path );

               proto_tree_add_text( net_tree, tvb, offset + pathpos + 1, 1, "Fixed Tag: %02X", tvb_get_guint8( tvb, offset + pathpos + 1 ) );

               /* 2 bytes of path used */
               pathpos += 2;
               break;

            case CI_NETWORK_SEG_PROD_INHI:
               net_item = proto_tree_add_text( path_tree, tvb, offset + pathpos, 2, "Network Segment - Production Inhibit" );
               net_tree = proto_item_add_subtree( net_item, ett_port_path );

               proto_tree_add_text( net_tree, tvb, offset + pathpos + 1, 1, "Production Inhibit Time: %dms", tvb_get_guint8( tvb, offset + pathpos + 1 ) );

               /* 2 bytes of path used */
               pathpos += 2;
               break;

            default:
               proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Sub-Segment Type" );
               return;

            } /* End of switch sub-type */

      break;

     default:

         /* Unsupported segment type */
         proto_tree_add_text( path_tree, tvb, 0, 0, "Unsupported Segment Type" );
         return;

      } /* end of switch( segment_type & CI_SEGMENT_TYPE_MASK ) */

      /* Next path segment */
      if( pathpos < path_length )
         proto_item_append_text( epath_item, ", " );

   } /* end of while( pathpos < path_length ) */

} /* end of dissect_epath() */


static void
dissect_cip_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, *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;
   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 );

   /* 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 )
   {
      /* 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, "") );
      }
   }

   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, "") );
   }

   /* 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 */
      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)")   );

      /* 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, " %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) ) );
         }
      }

      proto_item_set_len( status_item, 2 + add_stat_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_cmd_data );

         if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
         {
            /* Success responses */

            if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_OPEN )
            {
               /* 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 );

               /* 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 );

               /* 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 );

               /* Display the originator vendor id */
               proto_tree_add_item( cmd_data_tree, hf_cip_vendor, tvb, offset+4+add_stat_size+10, 2, TRUE);

               /* 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 );

               /* 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 */

            } /* End of if forward open response */
            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_FWD_CLOSE )
            {
               /* Forward close response (Success) */

               /* 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 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 */

            } /* End of if forward close response */
            else if( ( tvb_get_guint8( tvb, offset ) & 0x7F ) == SC_UNCON_SEND )
            {
               /* Unconnected send response (Success) */

               /* 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 )
            {
               /* Multiple Service Reply (Success)*/

               /* Add number of replies */
               num_services = tvb_get_letohs( tvb, offset+4+add_stat_size );
               proto_tree_add_text( cmd_data_tree, tvb, offset+4+add_stat_size, 2, "Number of Replies: %d", num_services );

               /* Add replies */
               temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+add_stat_size+4, num_services*2, "Offsets: " );

               for( i=0; i < num_services; i++ )
               {
                  int serv_length;

                  serv_offset = tvb_get_letohs( tvb, offset+6+add_stat_size+(i*2) );

                  if( i == (num_services-1) )
                  {
                     /* Last service to add */
                     proto_item_append_text(temp_item, "%d", serv_offset );
                     serv_length = item_length-add_stat_size-serv_offset-4;
                  }
                  else
                  {
                     proto_item_append_text(temp_item, "%d, ", serv_offset );
                     serv_length = tvb_get_letohs( tvb, offset+6+add_stat_size+((i+1)*2) ) - serv_offset;
                  }

                  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 );

                  /*
                  ** 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+serv_offset+4, serv_length, pinfo );
               }
            } /* End if Multiple service Packet */
            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 */
               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 */
            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);
            } /* end of check service code */

         }
         else
         {
            /* 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);
            }

         } /* 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 , "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 );

         /* Check what service code that recived */

         if( tvb_get_guint8( tvb, offset ) == SC_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_ncp);

            /* 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 );

            /* Display target to originator requested packet interval */
            temp_data = tvb_get_letohl( tvb, offset+2+req_path_size+28 );
            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+28, 4, "T->O RPI: %dms (0x%08X)", temp_data / 1000, temp_data );

            /* 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);

            /* 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 );

            /* 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);

            /* 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_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 );

            /* Add path size */
            conn_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+35 )*2;
            proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+35, 1, "Connection Path Size: %d (words)", conn_path_size / 2 );

            /* 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 );
         }
         else if( tvb_get_guint8( tvb, offset ) == SC_FWD_CLOSE && ! collision)
         {
            /* Forward Close 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 connection serial number */
            temp_data = 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, "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+4, 2, TRUE);

            /* Display the originator serial number */
            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 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 );

         } /* End of forward close */
         else if( tvb_get_guint8( tvb, offset ) == SC_UNCON_SEND )
         {
	    /* 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 );
	    }

         } /* End if unconnected send */
         else if( tvb_get_guint8( tvb, offset ) == SC_MULT_SERV_PACK )
         {
            /* Multiple service packet */

            /* 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 services */
            temp_item = proto_tree_add_text( cmd_data_tree, tvb, offset+2+req_path_size+2, num_services*2, "Offsets: " );

            for( i=0; i < num_services; i++ )
            {
               int serv_length;

               serv_offset = tvb_get_letohs( tvb, offset+4+req_path_size+(i*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 );
               }

               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 );

               /*
               ** 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+serv_offset+6, serv_length, pinfo );
            }
         } /* End if Multiple service Packet */
         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 if ( tvb_get_guint8( tvb, offset ) == SC_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: ");

	    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);

         } /* End of check service code */

      } /* End of if command-specific data present */

   } /* End of if-else( request ) */

} /* 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;

   /* 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, "CIP" );

   if (check_col( pinfo->cinfo, COL_INFO ) )
      col_clear( pinfo->cinfo, COL_INFO );

   if( tree )
   {
      /* Create display subtree for the protocol */
      ti = proto_tree_add_item(tree, proto_cip, tvb, 0, -1, FALSE);
      cip_tree = proto_item_add_subtree( ti, ett_cip );
   }

   dissect_cip_data( cip_tree, tvb, 0, tvb_length(tvb), pinfo );

   return tvb_length(tvb);
}


void
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_HEX, NULL, 0,
			"EPath", HFILL }
		},
		{ &hf_cip_genstat,
			{ "General Status", "cip.genstat",
			FT_UINT8, BASE_HEX, VALS(cip_gs_vals), 0,
			"General Status", 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,
			"Link Address", HFILL }
		},
		{ &hf_cip_link_address_string,
			{ "Link Address", "cip.linkaddress",
			FT_STRING, BASE_NONE, NULL, 0,
			"Link Address", HFILL }
		},
		{ &hf_cip_class8,
			{ "Class", "cip.class",
			FT_UINT8, BASE_HEX, VALS(cip_class_names_vals), 0,
			"Class", HFILL }
		},
		{ &hf_cip_class16,
			{ "Class", "cip.class",
			FT_UINT16, BASE_HEX, VALS(cip_class_names_vals), 0,
			"Class", HFILL }
		},
		{ &hf_cip_class32,
			{ "Class", "cip.class",
			FT_UINT32, BASE_HEX, VALS(cip_class_names_vals), 0,
			"Class", HFILL }
		},
		{ &hf_cip_instance8,
			{ "Instance", "cip.instance",
			FT_UINT8, BASE_HEX, NULL, 0,
			"Instance", HFILL }
		},
		{ &hf_cip_instance16,
			{ "Instance", "cip.instance",
			FT_UINT16, BASE_HEX, NULL, 0,
			"Instance", HFILL }
		},
		{ &hf_cip_instance32,
			{ "Instance", "cip.instance",
			FT_UINT32, BASE_HEX, NULL, 0,
			"Instance", HFILL }
		},
		{ &hf_cip_attribute8,
			{ "Attribute", "cip.attribute",
			FT_UINT8, BASE_HEX, NULL, 0,
			"Attribute", HFILL }
		},
		{ &hf_cip_attribute16,
			{ "Attribute", "cip.attribute",
			FT_UINT16, BASE_HEX, NULL, 0,
			"Attribute", HFILL }
		},
		{ &hf_cip_attribute32,
			{ "Attribute", "cip.attribute",
			FT_UINT32, BASE_HEX, NULL, 0,
			"Attribute", HFILL }
		},
		{ &hf_cip_conpoint8,
			{ "Connection Point", "cip.connpoint",
			FT_UINT8, BASE_HEX, NULL, 0,
			"Connection Point", HFILL }
		},
		{ &hf_cip_conpoint16,
			{ "Connection Point", "cip.connpoint",
			FT_UINT16, BASE_HEX, NULL, 0,
			"Connection Point", HFILL }
		},
		{ &hf_cip_conpoint32,
			{ "Connection Point", "cip.connpoint",
			FT_UINT16, BASE_HEX, NULL, 0,
			"Connection Point", 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,
			"Vendor ID", HFILL }
		},
		{ &hf_cip_devtype,
			{ "Device Type", "cip.devtype",
			FT_UINT16, BASE_DEC, VALS(cip_devtype_vals), 0,
			"Device Type", 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,
			"Data", 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
	};

   /* Register the protocol name and description */
   proto_cip = proto_register_protocol("Common Industrial Protocol",
	    "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));

} /* 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*/
   dissector_add( "enip.sud.iface", ENIP_CIP_INTERFACE, cip_handle );

} /* end of proto_reg_handoff_cip() */
