/* packet-enip.c
 * Routines for EtherNet/IP (Industrial Protocol) dissection
 * EtherNet/IP Home: www.odva.org
 *
 * Copyright 2003-2004
 * Magnus Hansson <mah@hms.se>
 * Joakim Wiberg <jow@hms.se>
 *
 * $Id: packet-enip.c,v 1.11 2004/06/02 06:30:15 guy Exp $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@ethereal.com>
 * 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>

#ifdef NEED_SNPRINTF_H
# include "snprintf.h"
#endif

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

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

/* CIP Encapsulation function codes */
#define NOP                0x0000
#define LIST_SERVICES      0x0004
#define LIST_IDENTITY      0x0063
#define LIST_INTERFACES    0x0064
#define REGISTER_SESSION   0x0065
#define UNREGISTER_SESSION 0x0066
#define SEND_RR_DATA       0x006F
#define SEND_UNIT_DATA     0x0070
#define INDICATE_STATUS    0x0072
#define CANCEL             0x0073

/* CIP Encapsulation status codes */
#define SUCCESS               0x0000
#define INVALID_CMD           0x0001
#define NO_RESOURCES          0x0002
#define INCORRECT_DATA        0x0003
#define INVALID_SESSION       0x0064
#define INVALID_LENGTH        0x0065
#define UNSUPPORTED_PROT_REV  0x0069

/* CIP Common Data Format Type IDs */
#define CDF_NULL              0x0000
#define LIST_IDENTITY_RESP    0x000C
#define CONNECTION_BASED      0x00A1
#define CONNECTION_TRANSPORT  0x00B1
#define UNCONNECTED_MSG       0x00B2
#define LIST_SERVICES_RESP    0x0100
#define SOCK_ADR_INFO_OT      0x8000
#define SOCK_ADR_INFO_TO      0x8001
#define SEQ_ADDRESS           0x8002

/* CIP Service Codes */
#define SC_GET_ATT_ALL           0x01
#define SC_SET_ATT_ALL           0x02
#define SC_GET_ATT_LIST          0x03
#define SC_SET_ATT_LIST          0x04
#define SC_RESET                 0x05
#define SC_START                 0x06
#define SC_STOP                  0x07
#define SC_CREATE                0x08
#define SC_DELETE                0x09
#define SC_MULT_SERV_PACK        0x0A
#define SC_APPLY_ATTRIBUTES      0x0D
#define SC_GET_ATT_SINGLE        0x0E
#define SC_SET_ATT_SINGLE        0x10
#define SC_FIND_NEXT_OBJ_INST    0x11
#define SC_RESTOR                0x15
#define SC_SAVE                  0x16
#define SC_NO_OP                 0x17
#define SC_GET_MEMBER            0x18
#define SC_SET_MEMBER            0x19

#define SC_FWD_CLOSE             0x4E
#define SC_UNCON_SEND            0x52
#define SC_FWD_OPEN              0x54



/* CIP Genral status codes */
#define CI_GRC_SUCCESS              0x00
#define CI_GRC_FAILURE              0x01
#define CI_GRC_NO_RESOURCE          0x02
#define CI_GRC_BAD_DATA             0x03
#define CI_GRC_BAD_PATH             0x04
#define CI_GRC_BAD_CLASS_INSTANCE   0x05
#define CI_GRC_PARTIAL_DATA         0x06
#define CI_GRC_CONN_LOST            0x07
#define CI_GRC_BAD_SERVICE          0x08
#define CI_GRC_BAD_ATTR_DATA        0x09
#define CI_GRC_ATTR_LIST_ERROR      0x0A
#define CI_GRC_ALREADY_IN_MODE      0x0B
#define CI_GRC_BAD_OBJ_MODE         0x0C
#define CI_GRC_OBJ_ALREADY_EXISTS   0x0D
#define CI_GRC_ATTR_NOT_SETTABLE    0x0E
#define CI_GRC_PERMISSION_DENIED    0x0F
#define CI_GRC_DEV_IN_WRONG_STATE   0x10
#define CI_GRC_REPLY_DATA_TOO_LARGE 0x11
#define CI_GRC_FRAGMENT_PRIMITIVE   0x12
#define CI_GRC_CONFIG_TOO_SMALL     0x13
#define CI_GRC_UNDEFINED_ATTR       0x14
#define CI_GRC_CONFIG_TOO_BIG       0x15
#define CI_GRC_OBJ_DOES_NOT_EXIST   0x16
#define CI_GRC_NO_FRAGMENTATION     0x17
#define CI_GRC_DATA_NOT_SAVED       0x18
#define CI_GRC_DATA_WRITE_FAILURE   0x19
#define CI_GRC_REQUEST_TOO_LARGE    0x1A
#define CI_GRC_RESPONSE_TOO_LARGE   0x1B
#define CI_GRC_MISSING_LIST_DATA    0x1C
#define CI_GRC_INVALID_LIST_STATUS  0x1D
#define CI_GRC_SERVICE_ERROR        0x1E
#define CI_GRC_CONN_RELATED_FAILURE 0x1F
#define CI_GRC_INVALID_PARAMETER    0x20
#define CI_GRC_WRITE_ONCE_FAILURE   0x21
#define CI_GRC_INVALID_REPLY        0x22
#define CI_GRC_BAD_KEY_IN_PATH      0x25
#define CI_GRC_BAD_PATH_SIZE        0x26
#define CI_GRC_UNEXPECTED_ATTR      0x27
#define CI_GRC_INVALID_MEMBER       0x28
#define CI_GRC_MEMBER_NOT_SETTABLE  0x29

#define CI_GRC_STILL_PROCESSING     0xFF


/* IOI Path types */
#define CI_SEGMENT_TYPE_MASK        0xE0

#define CI_PATH_SEGMENT             0x00
#define CI_LOGICAL_SEGMENT          0x20
#define CI_NETWORK_SEGMENT          0x40
#define CI_SYMBOLIC_SEGMENT         0x60
#define CI_DATA_SEGMENT             0x80

#define CI_LOGICAL_SEG_TYPE_MASK    0x1C
#define CI_LOGICAL_SEG_CLASS_ID     0x00
#define CI_LOGICAL_SEG_INST_ID      0x04
#define CI_LOGICAL_SEG_MBR_ID       0x08
#define CI_LOGICAL_SEG_CON_POINT    0x0C
#define CI_LOGICAL_SEG_ATTR_ID      0x10
#define CI_LOGICAL_SEG_SPECIAL      0x14
#define CI_LOGICAL_SEG_SERV_ID      0x18
#define CI_LOGICAL_SEG_RES_1        0x1C

#define CI_LOGICAL_SEG_FORMAT_MASK  0x03
#define CI_LOGICAL_SEG_8_BIT        0x00
#define CI_LOGICAL_SEG_16_BIT       0x01
#define CI_LOGICAL_SEG_32_BIT       0x02
#define CI_LOGICAL_SEG_RES_2        0x03
#define CI_LOGICAL_SEG_E_KEY        0x00

#define CI_E_KEY_FORMAT_VAL         0x04

#define CI_DATA_SEG_SIMPLE          0x80
#define CI_DATA_SEG_SYMBOL          0x91

#define CI_NETWORK_SEG_TYPE_MASK    0x07
#define CI_NETWORK_SEG_SCHEDULE     0x01
#define CI_NETWORK_SEG_FIXED_TAG    0x02
#define CI_NETWORK_SEG_PROD_INHI    0x03


/* Device Profile:s */
#define DP_GEN_DEV                           0x00
#define DP_AC_DRIVE	                        0x02
#define DP_MOTOR_OVERLOAD                    0x03
#define DP_LIMIT_SWITCH                      0x04
#define DP_IND_PROX_SWITCH                   0x05
#define DP_PHOTO_SENSOR                      0x06
#define DP_GENP_DISC_IO                      0x07
#define DP_RESOLVER                          0x09
#define DP_COM_ADAPTER                       0x0C
#define DP_POS_CNT                           0x10
#define DP_DC_DRIVE                          0x13
#define DP_CONTACTOR                         0x15
#define DP_MOTOR_STARTER                     0x16
#define DP_SOFT_START                        0x17
#define DP_HMI                               0x18
#define DP_MASS_FLOW_CNT                     0x1A
#define DP_PNEUM_VALVE                       0x1B
#define DP_VACUUM_PRES_GAUGE                 0x1C



/* Initialize the protocol and registered fields */
static int proto_cipencap              = -1;
static int hf_enip_command             = -1;
static int hf_enip_ifacehnd            = -1;

static int hf_enip_cpf_typeid          = -1;

static int hf_enip_ucm_sc              = -1;
static int hf_enip_ucm_rr              = -1;
static int hf_enip_ucm_path            = -1;
static int hf_enip_ucm_genstat         = -1;
static int hf_enip_cpf_lir_sinfamily   = -1;
static int hf_enip_cpf_lir_sinport     = -1;
static int hf_enip_cpf_lir_sinaddr     = -1;
static int hf_enip_cpf_lir_sinzero     = -1;
static int hf_enip_cpf_lir_devtype     = -1;
static int hf_enip_cpf_lir_prodcode    = -1;
static int hf_enip_cpf_lir_status      = -1;
static int hf_enip_cpf_lir_sernbr      = -1;
static int hf_enip_cpf_lir_namelength  = -1;
static int hf_enip_cpf_lir_name        = -1;
static int hf_enip_cpf_lir_state       = -1;

static int hf_enip_cpf_sat_connid      = -1;
static int hf_enip_cpf_sat_seqnum      = -1;

static int hf_enip_vendors             = -1;

static int hf_enip_ucm_fwo_comp        = -1;
static int hf_enip_ucm_fwo_mrev        = -1;

static int hf_enip_ucm_fwo_con_size    = -1;
static int hf_enip_ucm_fwo_fixed_var   = -1;
static int hf_enip_ucm_fwo_prio        = -1;
static int hf_enip_ucm_fwo_typ         = -1;
static int hf_enip_ucm_fwo_own         = -1;

static int hf_enip_ucm_fwo_dir         = -1;
static int hf_enip_ucm_fwo_trigg       = -1;
static int hf_enip_ucm_fwo_class       = -1;

static int hf_enip_cpf_lsr_tcp         = -1;
static int hf_enip_cpf_lsr_udp         = -1;


/* Initialize the subtree pointers */
static gint ett_cipencap   = -1;
static gint ett_cip        = -1;
static gint ett_cpf        = -1;
static gint ett_path       = -1;
static gint ett_ekey_path  = -1;
static gint ett_cia_path   = -1;
static gint ett_data_seg   = -1;

static gint ett_cipencaph  = -1;
static gint ett_csf        = -1;
static gint ett_rrsc       = -1;
static gint ett_sockadd    = -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 gboolean cipencap_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                 }
};


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

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

/* Translate function to string - CIP Service codes */
static const value_string encap_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" },

	{ 0,				            NULL }
};

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

	{ 0,			NULL }
};


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

	{ 0,        NULL          }
};

/* Translate function to string - True/False */
static const value_string enip_true_false_vals[] = {
	{ 0,	      "False"       },
	{ 1,	      "True"        },

	{ 0,        NULL          }
};


/* Translate function to string - Connection priority */
static const value_string enip_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 enip_con_fw_vals[] = {
	{ 0,	      "Fixed"    },
	{ 1,	      "Variable" },

	{ 0,        NULL       }
};


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

	{ 0,        NULL        }
};

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

	{ 0,        NULL        }
};

/* Translate function to string - Production trigger */
static const value_string enip_con_trigg_vals[] = {
	{ 0,	      "Cyclic" },
	{ 1,	      "Change-Of-State" },
	{ 2,	      "Application Object" },

	{ 0,        NULL        }
};

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

	{ 0,        NULL        }
};


/* Translate function to string - Connection type */
static const value_string enip_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 enip_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 encap_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 Vendor ID:s */
static const value_string encap_cip_vendor_vals[] = {
   { 1,     "Rockwell Automation/Allen-Bradley"                   },
   { 5,     "Rockwell Automation/Reliance Electric"               },
   { 24,    "ODVA Special Reserve"                                },
   { 26,    "Festo Corporation"                                   },
   { 40,    "WAGO Corporation"                                    },
   { 48,    "Turck, Inc."                                         },
   { 49,    "Grayhill Inc."                                       },
   { 50,    "Real Time Automation (C&ID)"                         },
   { 52,    "Numatics, Inc."                                      },
   { 57,    "Pepperl + Fuchs"                                     },
   { 81,    "IXXAT Automation GmbH"                               },
   { 90,    "HMS Industrial Networks AB"                          },
   { 96,    "Digital Electronics Corp"                            },
   { 128,   "MAC Valves, Inc."                                    },
   { 133,   "Balogh T.A.G., Corporation"                          },
   { 170,   "Pyramid Solutions, Inc."                             },
   { 256,   "InterlinkBT LLC"                                     },
   { 258,   "Hardy Instruments, Inc."                             },
   { 275,   "Lantronix, Inc."                                     },
   { 283,   "Hilscher GmbH"                                       },
   { 287,   "Bosch Rexroth Corporation, Indramat"                 },
   { 356,   "Fanuc Robotics America"                              },
   { 553,   "Control Techniques PLC-NA"                           },
   { 562,   "Phoenix Contact"                                     },
   { 579,   "Applicom international"                              },
   { 588,   "West Instruments Limited"                            },
   { 590,   "Delta Computer Systems Inc."                         },
   { 596,   "Wire-Pro, Inc."                                      },
   { 635,   "The Siemon Company"                                  },
   { 638,   "Woodhead Connectivity"                               },
   { 651,   "Fife Corporation"                                    },
   { 668,   "Rockwell Automation/Entek IRD Intl."                 },
   { 678,   "Cognex Corporation"                                  },
   { 691,   "Startco Engineering Ltd"                             },
   { 734,   "Hakko Electronics Co., Ltd"                          },
   { 735,   "Tang & Associates"                                   },
   { 743,   "Linux Network Services"                              },
   { 748,   "DVT Corporation"                                     },
   { 759,   "FLS Automation A/S"                                  },
   { 768,   "CSIRO Mining Automation"                             },
   { 778,   "Harting, Inc. NA"                                    },
   { 784,   "Ci Technologies Pty Ltd (for Pelamos Industries)"    },
   { 796,   "Siemens Energy & Automation, Inc."                   },
   { 798,   "Tyco Electronics"                                    },
   { 803,   "ICP DAS Co., LTD"                                    },
   { 805,   "Digi International, Inc."                            },
   { 808,   "SICK AG"                                             },
   { 809,   "Ethernet Peripherals, Inc."                          },
   { 812,   "Process Control Corporation"                         },
   { 832,   "Quest Technical Solutions, Inc."                     },
   { 841,   "Panduit Corporation"                                 },
   { 850,   "Datalogic, Inc."                                     },
   { 851,   "SoftPLC Corporation"                                 },
   { 854,   "Frontline Test Equipment, Inc."                      },
   { 857,   "RVSI"                                                },
   { 859,   "Tennessee Rand Automation"                           },
   { 866,   "ATR Industrie-Elektronik GmbH Co."                   },
   { 875,   "FieldServer Technologies (Div Sierra Monitor Corp)"  },
   { 883,   "Automa SRL"                                          },

	{ 0,		NULL                                                  }
};

/* Translate Device Profile:s */
static const value_string encap_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 Dicrete 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,    "Vaccuum Pressure Gauge"      },

   { 0,				            NULL                          }
};


/* Translate class names */
static const value_string enip_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"                     },
	{ 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 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 = tvb_get_ptr( tvb, start, tmp_length );
   tmp2 = (char*)g_malloc( 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 );

   g_free( tmp2start );

   return( pi );

} /* end of add_byte_array_text_to_proto_tree() */



/* Decode and add epath to tree */
static void
show_epath( tvbuff_t *tvb, proto_item *pi, int offset, int path_length )
{
   int pathpos;
   int temp_data;
   int temp_data2;
   unsigned char segment_type, temp_byte, 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, *temp_item, *port_item, *ext_link_item, *net_item;
   proto_tree *mc_tree;
   int seg_size, i, temp_word;
   char *temp_string;

   /* Create a sub tree for the epath */
   path_tree = proto_item_add_subtree( pi, ett_path );

   proto_tree_add_item_hidden(path_tree, hf_enip_ucm_path,
							   tvb, offset, path_length, TRUE );

   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_PATH_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_text( port_tree, tvb, offset+pathpos, 1, "Port Identifier: %d", (segment_type & 0x0F)  );
         proto_item_append_text( pi, ", Port: %d", (segment_type & 0x0F)  );

         /* Add Extended Link Address flag */
         temp_item = proto_tree_add_text( port_tree, tvb, offset+pathpos, 1, "Extended Link Address: " );

         if( segment_type & 0x10 )
         {
            proto_item_append_text(temp_item, "TRUE");
            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  );
            ext_link_item = proto_tree_add_text( port_tree, tvb, offset+pathpos+2, opt_link_size, "Link Address: " );

            proto_item_append_text(pi, ", Addess: " );

            /* Add extended link address */
            for( i=0; i < opt_link_size; i++ )
            {
               temp_byte = tvb_get_guint8( tvb, offset + pathpos+2+i );
               proto_item_append_text(ext_link_item, "%c", temp_byte );
               proto_item_append_text(pi, "%c", temp_byte );
            }

            /* Pad byte */
            if( opt_link_size % 2 )
            {
              pathpos = pathpos + 3 + opt_link_size;
              proto_item_set_len(port_item, 3+opt_link_size);
            }
            else
            {
              pathpos = pathpos + 2 + opt_link_size;
              proto_item_set_len(port_item, 2+opt_link_size);
            }

         }
         else
         {
            proto_item_append_text(pi, ", Address: %d", tvb_get_guint8( tvb, offset + pathpos + 1 ) );

            proto_item_append_text(temp_item, "FALSE");
            proto_tree_add_text( port_tree, tvb, offset+pathpos+1, 1, "Link 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_text( cia_tree, tvb, offset + pathpos + 1, 1, "Class: %s (0x%02X)", val_to_str( temp_data, enip_class_names_vals , "Unknown class" ), temp_data );

               temp_string = match_strval( temp_data, enip_class_names_vals );

               if( temp_string )
               {
                  proto_item_append_text(pi, "%s", temp_string );
               }
               else
               {
                  proto_item_append_text(pi, "Class: 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, 3, "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_text( cia_tree, tvb, offset + pathpos + 2, 2, "Class: %s (0x%04X)", val_to_str( temp_data, enip_class_names_vals , "Unknown class" ), temp_data );
               temp_string = match_strval( temp_data, enip_class_names_vals );

               if( temp_string )
               {
                  proto_item_append_text(pi, "%s", temp_string );
               }
               else
               {
                  proto_item_append_text(pi, "Class: 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, 5, "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 instance number */
               proto_tree_add_text( cia_tree, tvb, offset + pathpos + 2, 4, "Class: %s (0x%08X)", val_to_str( temp_data, enip_class_names_vals , "Unknown class" ), temp_data );
               temp_string = match_strval( temp_data, enip_class_names_vals );

               if( temp_string )
               {
                  proto_item_append_text(pi, "%s", temp_string );
               }
               else
               {
                  proto_item_append_text(pi, "Class: 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_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_text( cia_tree, tvb, offset + pathpos + 1, 1, "Instance: 0x%02X", temp_data );
               proto_item_append_text(pi, ", Inst: 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, 3, "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_text( cia_tree, tvb, offset + pathpos + 2, 2, "Instance: 0x%04X", temp_data );
               proto_item_append_text(pi, ", Inst: 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, 5, "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 16-bit instance number */
               proto_tree_add_text( cia_tree, tvb, offset + pathpos + 2, 4, "Instance: 0x%08X", temp_data );
               proto_item_append_text(pi, ", Inst: 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 instance number */
               proto_tree_add_text( cia_tree, tvb, offset + pathpos + 1, 1, "Attribute: 0x%02X", temp_data );
               proto_item_append_text(pi, ", Att: 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, 3, "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 instance number */
               proto_tree_add_text( cia_tree, tvb, offset + pathpos + 2, 2, "Attribute: 0x%04X", temp_data );
               proto_item_append_text(pi, ", Att: 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, 5, "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 16-bit instance number */
               proto_tree_add_text( cia_tree, tvb, offset + pathpos + 2, 4, "Attribute: 0x%08X", temp_data );
               proto_item_append_text(pi, ", Att: 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 instance number */
               proto_tree_add_text( cia_tree, tvb, offset + pathpos + 1, 1, "Connection Point: 0x%02X", temp_data );
               proto_item_append_text(pi, ", ConPnt: 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, 3, "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 instance number */
               proto_tree_add_text( cia_tree, tvb, offset + pathpos + 2, 2, "Connection Point: 0x%04X", temp_data );
               proto_item_append_text(pi, ", ConPnt: 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, 5, "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 16-bit instance number */
               proto_tree_add_text( cia_tree, tvb, offset + pathpos + 2, 4, "Connection Point (0x%08X)", temp_data );
               proto_item_append_text(pi, ", ConPnt: 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_enip_vendors, 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_enip_cpf_lir_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_enip_ucm_fwo_comp,
   							tvb, offset + pathpos + 8, 1, TRUE );

                  proto_item_append_text( mcpi, "%s, Major Revision: %d",
                              val_to_str( ( temp_data & 0x80 )>>7, enip_com_bit_vals , "" ),
                              temp_data & 0x7F );

   					/* Major revision */
   					proto_tree_add_item(mc_tree, hf_enip_ucm_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, ", V.%d.%d", ( temp_data & 0x7F ), temp_data2 );

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

               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, 0, "Data: " );

                  for( i=0; i < seg_size; i++ )
                  {
                    temp_byte = tvb_get_guint8( tvb, offset + pathpos+2+i );
                    proto_item_append_text(qi, "%c", temp_byte );
                  }

                  proto_item_set_len(qi, seg_size);

                  if( seg_size %2 )
                  {
                     /* We have a PAD BYTE */
                     proto_tree_add_text( ds_tree, tvb, offset + pathpos+2+i, 1, "Pad Byte (0x%02X)",
                         tvb_get_guint8( tvb, offset + pathpos+2+i ) );
                     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 ) */

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

} /* end of show_epath() */



static void
add_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
{
   proto_item *pi, *rrsci, *ncppi, *ar_item, *temp_item, *temp_item2;
	proto_tree *temp_tree;
   proto_tree *rrsci_tree;
   proto_tree *ncp_tree;
   proto_tree *cmd_data_tree;
	int req_path_size, conn_path_size, mr_req_path_size;
	int temp_data;
	unsigned char gen_stat;
   unsigned char add_stat_size;
   unsigned char temp_byte, route_path_size;
   unsigned char app_rep_size, i;
   int msg_req_siz, num_services, serv_offset;


   /* Add Service code & Request/Response tree */
	rrsci = proto_tree_add_text(item_tree, tvb, offset, 1, "Service: ");
	rrsci_tree = proto_item_add_subtree(rrsci, ett_rrsc);

	/* Add Request/Response */
   proto_tree_add_item(rrsci_tree, hf_enip_ucm_rr,
			tvb, offset, 1, TRUE );

   proto_item_append_text( rrsci, "%s (%s)",
               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
                  encap_sc_vals , "Unknown Service (%x)"),
               val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x80 )>>7,
                  encap_sc_rr, "") );


	/* Add Service code */
	proto_tree_add_item(rrsci_tree, hf_enip_ucm_sc,
			tvb, offset, 1, TRUE );


	if( tvb_get_guint8( tvb, offset ) & 0x80 )
	{
	   /* Response message */

		/* Add general status */
		gen_stat = tvb_get_guint8( tvb, offset+2 );

		proto_tree_add_item(item_tree, hf_enip_ucm_genstat,
			tvb, offset+2, 1, TRUE );

      /* Add reply status to info column */
      if(check_col(pinfo->cinfo, COL_INFO))
      {
         col_append_fstr( pinfo->cinfo, COL_INFO, ", %s",
                  val_to_str( ( tvb_get_guint8( tvb, offset+2 ) ),
                     encap_cip_gs_vals , "Unknown Response (%x)") );
      }


      /* Add additional status size */
      temp_data = tvb_get_guint8( tvb, offset+3 );
      proto_tree_add_text( item_tree, tvb, offset+3, 1, "Additional Status Size: %d (word)", temp_data );

		add_stat_size = tvb_get_guint8( tvb, offset+3 )*2;

		if( add_stat_size )
		{
         /* Add additional status */
         pi = proto_tree_add_text( item_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) ) );
         }
		}

      /* 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_stat == CI_GRC_SUCCESS )
   		{
   			/* 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_enip_vendors, 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_enip_vendors, 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 */
               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_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 );
                  add_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 */
               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 Multiple service Packet */
            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_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_enip_vendors, 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 */
               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_fstr( pinfo->cinfo, COL_INFO, ", %s",
                  val_to_str( ( tvb_get_guint8( tvb, offset ) & 0x7F ),
                     encap_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: ");
      show_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_enip_vendors, 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, enip_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_enip_ucm_fwo_own,
   					tvb, offset+2+req_path_size+26, 2, TRUE );
   			proto_tree_add_item(ncp_tree, hf_enip_ucm_fwo_typ,
   					tvb, offset+2+req_path_size+26, 2, TRUE );
            proto_tree_add_item(ncp_tree, hf_enip_ucm_fwo_prio,
   					tvb, offset+2+req_path_size+26, 2, TRUE );
   			proto_tree_add_item(ncp_tree, hf_enip_ucm_fwo_fixed_var,
   					tvb, offset+2+req_path_size+26, 2, TRUE );
   			proto_tree_add_item(ncp_tree, hf_enip_ucm_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_enip_ucm_fwo_own,
   					tvb, offset+2+req_path_size+32, 2, TRUE );
   			proto_tree_add_item(ncp_tree, hf_enip_ucm_fwo_typ,
   					tvb, offset+2+req_path_size+32, 2, TRUE );
            proto_tree_add_item(ncp_tree, hf_enip_ucm_fwo_prio,
   					tvb, offset+2+req_path_size+32, 2, TRUE );
   			proto_tree_add_item(ncp_tree, hf_enip_ucm_fwo_fixed_var,
   					tvb, offset+2+req_path_size+32, 2, TRUE );
   			proto_tree_add_item(ncp_tree, hf_enip_ucm_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_enip_ucm_fwo_dir,
   					tvb, offset+2+req_path_size+34, 1, TRUE );

   			proto_tree_add_item(ncp_tree, hf_enip_ucm_fwo_trigg,
   					tvb, offset+2+req_path_size+34, 1, TRUE );

            proto_tree_add_item(ncp_tree, hf_enip_ucm_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: ");
            show_epath( tvb, pi, offset+2+req_path_size+36, conn_path_size );
         }
         else if( tvb_get_guint8( tvb, offset ) == SC_FWD_CLOSE )
         {
            /* 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_enip_vendors, 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: ");
            show_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 )
         {
            /* 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 );

            /* MR - Service */
            temp_data = tvb_get_guint8( tvb, offset+2+req_path_size+4 );
            proto_tree_add_text( temp_tree, tvb, offset+2+req_path_size+4, 1, "Service: %s (0x%02X)", val_to_str( temp_data, encap_sc_vals , "" ), temp_data );

            /* Add service to info column */
            if(check_col(pinfo->cinfo, COL_INFO))
            {
               col_append_fstr( pinfo->cinfo, COL_INFO, ", %s",
                        val_to_str( ( temp_data & 0x7F ),
                           encap_sc_vals , ", Unknown Service (%x)") );
            }

            /* MR - Request path Size */
   		   mr_req_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+5 )*2;
   		   proto_tree_add_text( temp_tree, tvb, offset+2+req_path_size+5, 1, "Request Path Size: %d (words)", mr_req_path_size/2 );

            /* MR - EPATH */
            temp_item = proto_tree_add_text(temp_tree, tvb, offset+2+req_path_size+6, mr_req_path_size, "Request Path: ");
            show_epath( tvb, temp_item, offset+2+req_path_size+6, mr_req_path_size );

            /* MR - Request data */
            if( ( msg_req_siz-2-mr_req_path_size ) != 0 )
            {
               add_byte_array_text_to_proto_tree( temp_tree, tvb, offset+2+req_path_size+6+mr_req_path_size, msg_req_siz-2-mr_req_path_size, "Request Data: " );
            }

            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");
            show_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 );
               add_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
         {
		      /* 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 add_cip_data() */



static void
show_cdf( int encap_service, tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset )
{
   proto_item *temp_item, *ri, *ci;
   proto_item *sockaddr_item;
	proto_tree *temp_tree, *cip_tree, *item_tree, *sockaddr_tree;
	int temp_data, item_count, item_length, item;
	unsigned char name_length;

	/* Show Common Data Format sub tree */
	item_count = tvb_get_letohs( tvb, offset );
   ri = proto_tree_add_text( tree, tvb, offset, 2, "Item Count: %d", item_count );
	cip_tree = proto_item_add_subtree(ri, ett_cip);

	while( item_count-- )
	{
		/* Add item type tree */
		ci = proto_tree_add_item(cip_tree, hf_enip_cpf_typeid, tvb, offset+2, 2, TRUE );
		item_tree = proto_item_add_subtree(ci, ett_cpf);

		/* Add length field */
      temp_data = tvb_get_letohs( tvb, offset+4 );
      proto_tree_add_text( item_tree, tvb, offset+4, 2, "Length: %d", temp_data );

		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;

			   case UNCONNECTED_MSG:

					/* Add CIP data tree*/
					add_cip_data( item_tree, tvb, offset+6, item_length, pinfo );

					break;

            case CONNECTION_TRANSPORT:

               if( encap_service == 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 ) );

                  /* Add CIP data tree */
                  add_cip_data( item_tree, tvb, offset+8, item_length-2, pinfo );

                  /* Add SEQ Count to Info col */

   			      /*
   			      if(check_col(pinfo->cinfo, COL_INFO))
   	            {
                     col_append_fstr(pinfo->cinfo, COL_INFO,
   				         ", SEQ=0x%04X",
   				         tvb_get_letohs( tvb, offset+6 ) );
   				   }
   				   */
               }
               else
               {
                  /* Display data */
                  add_byte_array_text_to_proto_tree( item_tree, tvb, offset+6, item_length, "Data: " );

               } /* End of if send unit data */

               break;


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

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

               /* Socket address struct - sin_family */
               proto_tree_add_item(sockaddr_tree, hf_enip_cpf_lir_sinfamily,
							tvb, offset+8, 2, FALSE );

               /* Socket address struct - sin_port */
               proto_tree_add_item(sockaddr_tree, hf_enip_cpf_lir_sinport,
							tvb, offset+10, 2, FALSE );

               /* Socket address struct - sin_address */
               proto_tree_add_item(sockaddr_tree, hf_enip_cpf_lir_sinaddr,
							tvb, offset+12, 4, FALSE );

               /* Socket address struct - sin_zero */
               proto_tree_add_item(sockaddr_tree, hf_enip_cpf_lir_sinzero,
							tvb, offset+16, 8, FALSE );

               /* Vendor ID */
               proto_tree_add_item(item_tree, hf_enip_vendors,
							tvb, offset+24, 2, TRUE );

               /* Device Type */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_devtype,
							tvb, offset+26, 2, TRUE );

               /* Product Code */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_prodcode,
							tvb, offset+28, 2, TRUE );

               /* 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_cpf_lir_status,
							tvb, offset+32, 2, TRUE );

               /* Serial Number */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_sernbr,
							tvb, offset+34, 4, TRUE );

               /* Product Name Length */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_namelength,
							tvb, offset+38, 1, TRUE );

               /* Get the lenth of the name */
               name_length = tvb_get_guint8( tvb, offset+38 );

               /* Product Name */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_name,
							tvb, offset+39, name_length, TRUE );

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

               /* State */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_state,
							tvb, offset+name_length+39, 1, TRUE );
               break;


            case SOCK_ADR_INFO_OT:
            case SOCK_ADR_INFO_TO:

               /* Socket address struct - sin_family */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_sinfamily,
							tvb, offset+6, 2, FALSE );

               /* Socket address struct - sin_port */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_sinport,
							tvb, offset+8, 2, FALSE );

               /* Socket address struct - sin_address */
               proto_tree_add_item(item_tree, hf_enip_cpf_lir_sinaddr,
							tvb, offset+10, 4, FALSE );

               /* Socket address struct - sin_zero */
               proto_tree_add_item( item_tree, hf_enip_cpf_lir_sinzero,
							tvb, offset+14, 8, FALSE );
				   break;


            case SEQ_ADDRESS:
               proto_tree_add_item(item_tree, hf_enip_cpf_sat_connid,
							tvb, offset+6, 4, TRUE );

               proto_tree_add_item(item_tree, hf_enip_cpf_sat_seqnum,
							tvb, offset+10, 4, TRUE );

               /* Add info to column */

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

                  col_add_fstr(pinfo->cinfo, COL_INFO,
				         "Connection:  ID=0x%08X, SEQ=%010d",
				         tvb_get_letohl( tvb, offset+6 ),
				         tvb_get_letohl( tvb, offset+10 ) );
				   }

				   break;

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

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

               proto_tree_add_item(temp_tree, hf_enip_cpf_lsr_tcp,
                  tvb, offset+8, 2, TRUE );
      		   proto_tree_add_item(temp_tree, hf_enip_cpf_lsr_udp,
      			   tvb, offset+8, 2, TRUE );

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

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


				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 show_cdf() */



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

static guint
get_cipencap_pdu_len(tvbuff_t *tvb, int offset)
{
   guint16 plen;

   /*
    * Get the length of the data from the encapsulation header.
    */
   plen = tvb_get_letohs(tvb, offset + 2);

   /*
    * That length doesn't include the encapsulation header itself;
    * add that in.
    */
   return plen + 24;
}

/* Code to actually dissect the packets */
static void
dissect_cipencap_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
   int	    packet_type;
   guint16  encap_cmd, encap_data_length;
   char     pkt_type_str[9] = "";
   guint32  status;

   /* Set up structures needed to add the protocol subtree and manage it */
   proto_item *ti, *encaph, *csf;
   proto_tree *cipencap_tree, *headertree, *csftree;

   /* 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_INFO))
      col_clear(pinfo->cinfo, COL_INFO);

   encap_cmd = tvb_get_letohs( tvb, 0 );

   if( check_col(pinfo->cinfo, COL_INFO) )
   {
      packet_type = classify_packet(pinfo);

      switch ( packet_type )
      {
         case REQUEST_PACKET:
            strcpy(pkt_type_str, "Req");
            break;

         case RESPONSE_PACKET:
            strcpy(pkt_type_str, "Rsp");
            break;

         default:
            strcpy(pkt_type_str, "?");
      }

      /* Add service and request/response to info column */
      col_add_fstr(pinfo->cinfo, COL_INFO,
                "%-20s (%s)",
	      val_to_str(encap_cmd, encap_cmd_vals, "Unknown (0x%04x)"),
	      pkt_type_str );


   } /* end of if( col exists ) */

   /* 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_cipencap, tvb, 0, -1, FALSE);

      cipencap_tree = proto_item_add_subtree(ti, ett_cipencap);

      /* Add encapsulation header tree */
      encaph     = proto_tree_add_text( cipencap_tree, tvb, 0, 24, "Encapsulation Header");
      headertree = proto_item_add_subtree(encaph, ett_cipencaph);

      /* CIP header information */
      proto_tree_add_uint(headertree, hf_enip_command, tvb, 0, 2, encap_cmd);

      encap_data_length = tvb_get_letohs( tvb, 2 );
      proto_tree_add_text( headertree, tvb, 2, 2, "Length: %u", encap_data_length );

      proto_tree_add_text( headertree, tvb, 4, 4, "Session Handle: 0x%08X",
                          tvb_get_letohl( tvb, 4 ) );

      status = tvb_get_letohl( tvb, 8 );
      proto_tree_add_text( headertree, tvb, 8, 4, "Status: %s (0x%08X)",
                          val_to_str( status, encap_status_vals,
                                     "Unknown Status Code" ),
                          status);

      add_byte_array_text_to_proto_tree( headertree, tvb, 12, 8, "Sender context: " );

      proto_tree_add_text( headertree, tvb, 20, 4, "Options: 0x%08X",
                          tvb_get_letohl( tvb, 20 ) );

      /*
      ** For some commands we want to add some info to the info column
      */

      if( check_col( pinfo->cinfo, COL_INFO ) )
      {

         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 switch() */

      } /* end of id info column */

      /* Command specific data - create tree */
      if( encap_data_length )
      {
         /* The packet have some command specific data, buid a sub tree for it */

         csf = proto_tree_add_text( cipencap_tree, tvb, 24, encap_data_length,
                                   "Command Specific Data");

         csftree = proto_item_add_subtree(csf, ett_csf);

         switch( encap_cmd )
         {
            case NOP:
               break;

            case LIST_SERVICES:
               show_cdf( encap_cmd, tvb, pinfo, csftree, 24 );
               break;

            case LIST_IDENTITY:
               show_cdf( encap_cmd, tvb, pinfo, csftree, 24 );
               break;

            case LIST_INTERFACES:
               show_cdf( encap_cmd, tvb, pinfo, csftree, 24 );
               break;

            case REGISTER_SESSION:
               proto_tree_add_text( csftree, tvb, 24, 2, "Protocol Version: 0x%04X",
                                   tvb_get_letohs( tvb, 24 ) );

               proto_tree_add_text( csftree, tvb, 26, 2, "Option Flags: 0x%04X",
                                   tvb_get_letohs( tvb, 26 ) );

               break;

            case UNREGISTER_SESSION:
               break;

            case SEND_RR_DATA:
            case SEND_UNIT_DATA:
               proto_tree_add_item(csftree, hf_enip_ifacehnd, tvb, 24, 4, TRUE);

               proto_tree_add_text( csftree, tvb, 28, 2, "Timeout: %u",
                                   tvb_get_letohs( tvb, 28 ) );

               show_cdf( encap_cmd, tvb, pinfo, csftree, 30 );
               break;

            case INDICATE_STATUS:
            case CANCEL:
            default:

               /* Can not decode - Just show the data */
               add_byte_array_text_to_proto_tree( headertree, tvb, 24, encap_data_length, "Encap Data: " );
               break;

         } /* end of switch() */

      } /* end of if( encapsulated data ) */

   }
} /* end of dissect_cipencap() */

static int
dissect_cipencap_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
   guint16  encap_cmd;

   /* An ENIP packet is at least 4 bytes long - we need the command type. */
   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 */

   dissect_cipencap_pdu(tvb, pinfo, tree);
   return tvb_length(tvb);
}

static int
dissect_cipencap_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
   guint16  encap_cmd;

   /* An ENIP packet is at least 4 bytes long - we need the command type. */
   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 */

   tcp_dissect_pdus(tvb, pinfo, tree, cipencap_desegment, 4,
	get_cipencap_pdu_len, dissect_cipencap_pdu);
   return tvb_length(tvb);
}

/* Code to actually dissect the io packets*/
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 *cipencap_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");

   /* 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_cipencap, tvb, 0, -1, FALSE);

		cipencap_tree = proto_item_add_subtree(ti, ett_cipencap);

      show_cdf( 0xFFFF, tvb, pinfo, cipencap_tree, 0 );
	}

} /* end of dissect_enipio() */


/* Register the protocol with Ethereal */

/* this format is require because a script is used to build the C function
   that calls all the protocol registration.
*/

void
proto_register_cipencap(void)
{

/* Setup list of header fields  lengthSee Section 1.6.1 for details*/
	static hf_register_info hf[] = {
		{ &hf_enip_command,
			{ "Command",           "enip.command",
			FT_UINT16, BASE_HEX, VALS(encap_cmd_vals), 0,
			"Encapsulation command", HFILL }
		},

		/* Encapsulated data headers */
		/* Common Packet Format */
		{ &hf_enip_cpf_typeid,
			{ "Type ID",          "enip.cpf.typeid",
			FT_UINT16, BASE_HEX, VALS(cdf_type_vals), 0,
			"Type of encapsulated item", HFILL }
		},

		/* Send RR Data */
		{ &hf_enip_ifacehnd,
			{ "Interface Handle",           "enip.cpf.rr.ifacehnd",
			FT_UINT32, BASE_HEX, NULL, 0,
			"Interface handle", HFILL }
		},

		/* Unconnected message */
      { &hf_enip_ucm_rr,
			{ "Request/Response", "enip.cip.rr",
			FT_UINT8, BASE_HEX, VALS(encap_sc_rr), 0x80,
			"Request or Response message", HFILL }
		},
		{ &hf_enip_ucm_sc,
			{ "Service",           "enip.cip.sc",
			FT_UINT8, BASE_HEX, VALS(encap_sc_vals), 0x7F,
			"CIP Service code", HFILL }
		},
		{ &hf_enip_ucm_path,
			{ "Request Path",           "enip.cip.path",
			FT_BYTES, BASE_HEX, NULL, 0,
			"Request path", HFILL }
		},
		{ &hf_enip_ucm_genstat,
			{ "General Status",           "enip.cip.genstat",
			FT_UINT8, BASE_HEX, VALS(encap_cip_gs_vals), 0,
			"General Status", HFILL }
		},

		/* List identity response */
      { &hf_enip_cpf_lir_sinfamily,
			{ "sin_family", "enip.lir.sinfamily",
			FT_UINT16, BASE_DEC, NULL, 0,
			"Socket Address Sin Family", HFILL }
		},
      { &hf_enip_cpf_lir_sinport,
			{ "sin_port", "enip.lir.sinport",
			FT_UINT16, BASE_DEC, NULL, 0,
			"Socket Address Sin Port", HFILL }
		},
      { &hf_enip_cpf_lir_sinaddr,
			{ "sin_addr", "enip.lir.sinaddr",
			FT_IPv4, BASE_HEX, NULL, 0,
			"Socket Address Sin Addr", HFILL }
		},
      { &hf_enip_cpf_lir_sinzero,
			{ "sin_zero", "enip.lir.sinzero",
			FT_BYTES, BASE_HEX, NULL, 0,
			"Socket Address Sin Zero", HFILL }
		},
      { &hf_enip_cpf_lir_devtype,
			{ "Device Type", "enip.lir.devtype",
			FT_UINT16, BASE_DEC, VALS(encap_cip_devtype_vals), 0,
			"Device Type", HFILL }
		},
      { &hf_enip_cpf_lir_prodcode,
			{ "Product Code", "enip.lir.prodcode",
			FT_UINT16, BASE_DEC, NULL, 0,
			"Product Code", HFILL }
		},
      { &hf_enip_cpf_lir_status,
			{ "Status", "enip.lir.status",
			FT_UINT16, BASE_HEX, NULL, 0,
			"Status", HFILL }
		},
      { &hf_enip_cpf_lir_sernbr,
			{ "Serial Number", "enip.lir.ser",
			FT_UINT32, BASE_HEX, NULL, 0,
			"Serial Number", HFILL }
		},
      { &hf_enip_cpf_lir_namelength,
			{ "Product Name Length", "enip.lir.namelength",
			FT_UINT8, BASE_DEC, NULL, 0,
			"Product Name Length", HFILL }
		},
      { &hf_enip_cpf_lir_name,
			{ "Product Name", "enip.lir.name",
			FT_STRING, BASE_NONE, NULL, 0,
			"Product Name", HFILL }
		},
      { &hf_enip_cpf_lir_state,
			{ "State", "enip.lir.state",
			FT_UINT8, BASE_HEX, NULL, 0,
			"State", HFILL }
		},
      /* Vendor ID number */
		{ &hf_enip_vendors,
			{ "Vendor ID",           "enip.vnd",
			FT_UINT16, BASE_HEX, VALS(encap_cip_vendor_vals), 0,
			"Vendor ID number", HFILL }
		},
      { &hf_enip_ucm_fwo_comp,
			{ "Compatibility", "enip.cip.fwo.cmp",
			FT_UINT8, BASE_HEX, VALS(enip_com_bit_vals), 0x80,
			"Compatibility bit", HFILL }
		},
      { &hf_enip_ucm_fwo_mrev,
			{ "Major Revision", "enip.cip.fwo.mrev",
			FT_UINT8, BASE_DEC, NULL, 0x7F,
			"Major Revision", HFILL }
		},
      { &hf_enip_ucm_fwo_con_size,
			{ "Connection Size", "enip.cip.fwo.consize",
			FT_UINT16, BASE_DEC, NULL, 0x01FF,
			"Connection size", HFILL }
		},
      { &hf_enip_ucm_fwo_fixed_var,
			{ "Connection Size Type", "enip.cip.fwo.f_v",
			FT_UINT16, BASE_DEC, VALS(enip_con_fw_vals), 0x0200,
			"Fixed or variable connection size", HFILL }
		},
      { &hf_enip_ucm_fwo_prio,
			{ "Priority", "enip.cip.fwo.prio",
			FT_UINT16, BASE_DEC, VALS(enip_con_prio_vals), 0x0C00,
			"Connection priority", HFILL }
		},
      { &hf_enip_ucm_fwo_typ,
			{ "Connection Type", "enip.cip.fwo.typ",
			FT_UINT16, BASE_DEC, VALS(enip_con_type_vals), 0x6000,
			"Connection type", HFILL }
		},
      { &hf_enip_ucm_fwo_own,
			{ "Owner", "enip.cip.fwo.own",
			FT_UINT16, BASE_DEC, VALS(enip_con_owner_vals), 0x8000,
			"Redundant owner bit", HFILL }
		},
		{ &hf_enip_ucm_fwo_dir,
			{ "Direction", "enip.cip.fwo.dir",
			FT_UINT8, BASE_DEC, VALS(enip_con_dir_vals), 0x80,
			"Direction", HFILL }
		},
      { &hf_enip_ucm_fwo_trigg,
			{ "Trigger", "enip.cip.fwo.trigg",
			FT_UINT8, BASE_DEC, VALS(enip_con_trigg_vals), 0x70,
			"Production trigger", HFILL }
		},
      { &hf_enip_ucm_fwo_class,
			{ "Class", "enip.cip.fwo.class",
			FT_UINT8, BASE_DEC, VALS(enip_con_class_vals), 0x0F,
			"Transport Class", HFILL }
		},
		/* Sequenced Address Type */
      { &hf_enip_cpf_sat_connid,
			{ "Connection ID", "enip.sat.connid",
			FT_UINT32, BASE_HEX, NULL, 0,
			"Connection ID from forward open reply", HFILL }
		},
      { &hf_enip_cpf_sat_seqnum,
			{ "Sequence Number", "enip.sat.seq",
			FT_UINT32, BASE_DEC, NULL, 0,
			"Sequence Number", HFILL }
		},
		{ &hf_enip_cpf_lsr_tcp,
			{ "Supports CIP Encapsultion via TCP", "enip.ls.tcp",
			FT_UINT16, BASE_DEC, VALS(enip_true_false_vals), 0x0020,
			"Supports CIP Encapsultion via TCP", HFILL }
		},
		{ &hf_enip_cpf_lsr_udp,
			{ "Supports CIP Class 0 or 1 via UDP", "enip.ls.udp",
			FT_UINT16, BASE_DEC, VALS(enip_true_false_vals), 0x0100,
			"Supports CIP Class 0 or 1 via UDP", HFILL }
		}

   };


/* Setup protocol subtree array */
	static gint *ett[] = {
		&ett_cipencap,
		&ett_cip,
		&ett_cpf,
		&ett_path,
		&ett_ekey_path,
		&ett_cipencaph,
		&ett_csf,
		&ett_rrsc,
		&ett_sockadd,
		&ett_mcsc,
		&ett_ncp,
		&ett_cia_path,
		&ett_data_seg,
		&ett_lsrcf,
		&ett_mes_req,
		&ett_cmd_data,
		&ett_port_path,
		&ett_mult_ser
	};
	module_t *cipencap_module;

/* Register the protocol name and description */
	proto_cipencap = 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_cipencap, hf, array_length(hf));
	proto_register_subtree_array(ett, array_length(ett));

	cipencap_module = prefs_register_protocol(proto_cipencap, NULL);
	prefs_register_bool_preference(cipencap_module, "desegment",
	    "Desegment all EtherNet/IP messages spanning multiple TCP segments",
	    "Whether the EtherNet/IP dissector should desegment all messages spanning multiple TCP segments",
	    &cipencap_desegment);
} /* end of proto_register_cipencap() */


/* If this dissector uses sub-dissector registration add a registration routine.
   This format is required because a script is used to find these routines and
   create the code that calls these routines.
*/
void
proto_reg_handoff_cipencap(void)
{
	dissector_handle_t cipencap_udp_handle, cipencap_tcp_handle;
	dissector_handle_t enipio_handle;

	/* Register for encapsulated CIP data, using both TCP/UDP */
	cipencap_tcp_handle = new_create_dissector_handle(dissect_cipencap_tcp, proto_cipencap);
	dissector_add("tcp.port", ENIP_ENCAP_PORT, cipencap_tcp_handle);
	cipencap_udp_handle = new_create_dissector_handle(dissect_cipencap_udp, proto_cipencap);
	dissector_add("udp.port", ENIP_ENCAP_PORT, cipencap_udp_handle);

	/* Register for IO data over UDP */
	enipio_handle = create_dissector_handle(dissect_enipio, proto_cipencap);
	dissector_add("udp.port", ENIP_IO_PORT, enipio_handle);

} /* end of proto_reg_handoff_cipencap() */
