/* packet-cip.c
 * Routines for Common Industrial Protocol (CIP) dissection
 * CIP Home: www.odva.org
 *
 * This dissector includes items from:
 *    CIP Volume 1: Common Industrial Protocol, Edition 3.34
 *    CIP Volume 5: Integration of Modbus Devices into the CIP Architecture, Edition 2.17
 *    CIP Volume 7: CIP Safety, Edition 1.9
 *    CIP Volume 8: CIP Security, Edition 1.11
 *
 * Copyright 2004
 * Magnus Hansson <mah@hms.se>
 * Joakim Wiberg <jow@hms.se>
 *
 * Added support for Connection Configuration Object
 *   ryan wamsley * Copyright 2007
 *
 * Object dependend services based on IOI
 *   Jan Bartels, Siempelkamp Maschinen- und Anlagenbau GmbH & Co. KG
 *   Copyright 2007
 *
 * Improved support for CoCo, CM, MB objects
 * Heuristic object support for common services
 *   Michael Mann * Copyright 2011
 *
 * Added support for PCCC Objects
 *   Jared Rittle - Cisco Talos
 *   Copyright 2017
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

// There are multiple different ways to add functionality based on CIP class-specific behavior:
// 1. Dissector Table "cip.io.iface" - Use this when Class 0/1 I/O needs different parsing based on
//    the CIP Class in the Forward Open
// 2. Dissector Table "cip.connection.class" - Use this when Class 2/3 data needs different parsing
//    based on the CIP Class in the Forward Open
// 3. Dissector Table "cip.class.iface" - Use this when a CIP Class has significantly different
//    behavior that would be best handled through a separate dissector
// 4. Dissector Table "cip.data_segment.iface" - Unknown. This may be removed in the future
// 5. attribute_info_t: Use this to add handling for an attribute, using a 3 tuple key (Class, Instance, Attribute)
//    See 'cip_attribute_vals' for an example.
// 6. cip_service_info_t: Use this to add handling for a service, using a 2 tuple key (Class, Service)
//    See 'cip_obj_spec_service_table' for an example.

#include "config.h"

#include <epan/packet.h>
#include <epan/expert.h>
#include <epan/prefs.h>
#include <epan/proto_data.h>
#include <epan/wmem_scopes.h>
#include "packet-cip.h"
#include "packet-cipmotion.h"
#include "packet-cipsafety.h"
#include "packet-mbtcp.h"

void proto_register_cip(void);
void proto_reg_handoff_cip(void);

typedef struct mr_mult_req_info {
   guint8 service;
   int num_services;
   cip_req_info_t *requests;
} mr_mult_req_info_t;

static dissector_handle_t cip_handle;
static dissector_handle_t cip_class_generic_handle;
static dissector_handle_t cip_class_cm_handle;
static dissector_handle_t cip_class_pccc_handle;
static dissector_handle_t cip_class_mb_handle;
static dissector_handle_t modbus_handle;
static dissector_handle_t cip_class_cco_handle;
static heur_dissector_list_t  heur_subdissector_service;

static gboolean cip_enhanced_info_column = TRUE;

/* Initialize the protocol and registered fields */
static int proto_cip = -1;
static int proto_cip_class_generic = -1;
static int proto_cip_class_cm = -1;
static int proto_cip_class_pccc = -1;
static int proto_cip_class_mb = -1;
static int proto_cip_class_cco = -1;
static int proto_enip = -1;
static int proto_modbus = -1;

int hf_attr_class_revision = -1;
int hf_attr_class_max_instance = -1;
int hf_attr_class_num_instance = -1;
int hf_attr_class_opt_attr_num = -1;
int hf_attr_class_attr_num = -1;
int hf_attr_class_opt_service_num = -1;
int hf_attr_class_service_code = -1;
int hf_attr_class_num_class_attr = -1;
int hf_attr_class_num_inst_attr = -1;
static int hf_cip_data = -1;
static int hf_cip_service = -1;
static int hf_cip_service_code = -1;
static int hf_cip_reqrsp = -1;
static int hf_cip_epath = -1;
static int hf_cip_genstat = -1;
static int hf_cip_addstat_size = -1;
static int hf_cip_add_stat = -1;
static int hf_cip_request_path_size = -1;

static int hf_cip_cm_sc = -1;
static int hf_cip_cm_genstat = -1;
static int hf_cip_cm_addstat_size = -1;
static int hf_cip_cm_add_status = -1;
static int hf_cip_cm_ext_status = -1;
static int hf_cip_cm_priority = -1;
static int hf_cip_cm_tick_time = -1;
static int hf_cip_cm_timeout_tick = -1;
static int hf_cip_cm_timeout = -1;
static int hf_cip_cm_ot_connid = -1;
static int hf_cip_cm_to_connid = -1;
static int hf_cip_connid = -1;
static int hf_cip_cm_conn_serial_num = -1;
static int hf_cip_cm_orig_serial_num = -1;
static int hf_cip_cm_vendor = -1;
static int hf_cip_cm_timeout_multiplier = -1;
static int hf_cip_cm_ot_rpi = -1;
static int hf_cip_cm_ot_timeout = -1;
static int hf_cip_cm_ot_net_params32 = -1;
static int hf_cip_cm_ot_net_params16 = -1;
static int hf_cip_cm_to_rpi = -1;
static int hf_cip_cm_to_timeout = -1;

static int hf_cip_safety_nte_ms = -1;

static int hf_cip_cm_to_net_params32 = -1;
static int hf_cip_cm_to_net_params16 = -1;
static int hf_cip_cm_transport_type_trigger = -1;
static int hf_cip_cm_conn_path_size = -1;
static int hf_cip_cm_ot_api = -1;
static int hf_cip_cm_to_api = -1;
static int hf_cip_cm_app_reply_size = -1;
static int hf_cip_cm_app_reply_data = -1;
static int hf_cip_cm_consumer_number = -1;
static int hf_cip_cm_targ_vendor_id = -1;
static int hf_cip_cm_targ_dev_serial_num = -1;
static int hf_cip_cm_targ_conn_serial_num = -1;
static int hf_cip_cm_initial_timestamp = -1;
static int hf_cip_cm_initial_rollover = -1;
static int hf_cip_cm_remain_path_size = -1;
static int hf_cip_cm_msg_req_size = -1;
static int hf_cip_cm_route_path_size = -1;
static int hf_cip_cm_fwo_con_size = -1;
static int hf_cip_cm_lfwo_con_size = -1;
static int hf_cip_cm_fwo_fixed_var = -1;
static int hf_cip_cm_lfwo_fixed_var = -1;
static int hf_cip_cm_fwo_prio = -1;
static int hf_cip_cm_lfwo_prio = -1;
static int hf_cip_cm_fwo_typ = -1;
static int hf_cip_cm_lfwo_typ = -1;
static int hf_cip_cm_fwo_own = -1;
static int hf_cip_cm_lfwo_own = -1;
static int hf_cip_cm_fwo_dir = -1;
static int hf_cip_cm_fwo_trigg = -1;
static int hf_cip_cm_fwo_class = -1;
static int hf_cip_cm_gco_conn = -1;
static int hf_cip_cm_gco_coo_conn = -1;
static int hf_cip_cm_gco_roo_conn = -1;
static int hf_cip_cm_gco_last_action = -1;
static int hf_cip_cm_ext112_ot_rpi_type = -1;
static int hf_cip_cm_ext112_to_rpi_type = -1;
static int hf_cip_cm_ext112_ot_rpi = -1;
static int hf_cip_cm_ext112_to_rpi = -1;
static int hf_cip_cm_ext126_size = -1;
static int hf_cip_cm_ext127_size = -1;
static int hf_cip_cm_ext128_size = -1;

static int hf_cip_pccc_sc = -1;
static int hf_cip_pccc_req_id_len = -1;
static int hf_cip_pccc_cip_vend_id = -1;
static int hf_cip_pccc_cip_serial_num = -1;
static int hf_cip_pccc_cmd_code = -1;
static int hf_cip_pccc_sts_code = -1;
static int hf_cip_pccc_ext_sts_code = -1;
static int hf_cip_pccc_tns_code = -1;
static int hf_cip_pccc_fnc_code_06 = -1;
static int hf_cip_pccc_fnc_code_07 = -1;
static int hf_cip_pccc_fnc_code_0f = -1;
static int hf_cip_pccc_byte_size = -1;
static int hf_cip_pccc_file_num = -1;
static int hf_cip_pccc_file_type = -1;
static int hf_cip_pccc_element_num = -1;
static int hf_cip_pccc_subelement_num = -1;
#if 0
static int hf_cip_pccc_cpu_mode_3a = -1;
#endif
static int hf_cip_pccc_cpu_mode_80 = -1;
static int hf_cip_pccc_resp_code = -1;
static int hf_cip_pccc_execute_multi_count = -1;
static int hf_cip_pccc_execute_multi_len = -1;
static int hf_cip_pccc_execute_multi_fnc = -1;
static int hf_cip_pccc_data = -1;

static int hf_cip_mb_sc = -1;
static int hf_cip_mb_read_coils_start_addr = -1;
static int hf_cip_mb_read_coils_num_coils = -1;
static int hf_cip_mb_read_coils_data = -1;
static int hf_cip_mb_read_discrete_inputs_start_addr = -1;
static int hf_cip_mb_read_discrete_inputs_num_inputs = -1;
static int hf_cip_mb_read_discrete_inputs_data = -1;
static int hf_cip_mb_read_holding_register_start_addr = -1;
static int hf_cip_mb_read_holding_register_num_registers = -1;
static int hf_cip_mb_read_holding_register_data = -1;
static int hf_cip_mb_read_input_register_start_addr = -1;
static int hf_cip_mb_read_input_register_num_registers = -1;
static int hf_cip_mb_read_input_register_data = -1;
static int hf_cip_mb_write_coils_start_addr = -1;
static int hf_cip_mb_write_coils_outputs_forced = -1;
static int hf_cip_mb_write_coils_num_coils = -1;
static int hf_cip_mb_write_coils_data = -1;
static int hf_cip_mb_write_registers_start_addr = -1;
static int hf_cip_mb_write_registers_outputs_forced = -1;
static int hf_cip_mb_write_registers_num_registers = -1;
static int hf_cip_mb_write_registers_data = -1;
static int hf_cip_mb_data = -1;

static int hf_cip_cco_con_type = -1;
static int hf_cip_cco_ot_rtf = -1;
static int hf_cip_cco_to_rtf = -1;
static int hf_cip_cco_sc = -1;
static int hf_cip_cco_format_number = -1;
static int hf_cip_cco_edit_signature = -1;
static int hf_cip_cco_con_flags = -1;
static int hf_cip_cco_tdi_vendor = -1;
static int hf_cip_cco_tdi_devtype = -1;
static int hf_cip_cco_tdi_prodcode = -1;
static int hf_cip_cco_tdi_compatibility = -1;
static int hf_cip_cco_tdi_comp_bit = -1;
static int hf_cip_cco_tdi_majorrev = -1;
static int hf_cip_cco_tdi_minorrev = -1;
static int hf_cip_cco_pdi_vendor = -1;
static int hf_cip_cco_pdi_devtype = -1;
static int hf_cip_cco_pdi_prodcode = -1;
static int hf_cip_cco_pdi_compatibility = -1;
static int hf_cip_cco_pdi_comp_bit = -1;
static int hf_cip_cco_pdi_majorrev = -1;
static int hf_cip_cco_pdi_minorrev = -1;
static int hf_cip_cco_cs_data_index = -1;
static int hf_cip_cco_ot_rpi = -1;
static int hf_cip_cco_to_rpi = -1;
static int hf_cip_cco_ot_net_param16 = -1;
static int hf_cip_cco_to_net_param16 = -1;
static int hf_cip_cco_fwo_own = -1;
static int hf_cip_cco_fwo_typ = -1;
static int hf_cip_cco_fwo_prio = -1;
static int hf_cip_cco_fwo_fixed_var = -1;
static int hf_cip_cco_fwo_con_size = -1;
static int hf_cip_cco_ot_net_param32 = -1;
static int hf_cip_cco_to_net_param32 = -1;
static int hf_cip_cco_lfwo_own = -1;
static int hf_cip_cco_lfwo_typ = -1;
static int hf_cip_cco_lfwo_prio = -1;
static int hf_cip_cco_lfwo_fixed_var = -1;
static int hf_cip_cco_lfwo_con_size = -1;
static int hf_cip_cco_conn_path_size = -1;
static int hf_cip_cco_proxy_config_size = -1;
static int hf_cip_cco_target_config_size = -1;
static int hf_cip_cco_iomap_format_number = -1;
static int hf_cip_cco_iomap_size = -1;
static int hf_cip_cco_connection_disable = -1;
static int hf_cip_cco_net_conn_param_attr = -1;
static int hf_cip_cco_timeout_multiplier = -1;
static int hf_cip_cco_transport_type_trigger = -1;
static int hf_cip_cco_fwo_dir = -1;
static int hf_cip_cco_fwo_trigger = -1;
static int hf_cip_cco_fwo_class = -1;
static int hf_cip_cco_proxy_config_data = -1;
static int hf_cip_cco_target_config_data = -1;
static int hf_cip_cco_iomap_attribute = -1;
static int hf_cip_cco_safety = -1;
static int hf_cip_cco_change_type = -1;
static int hf_cip_cco_connection_name = -1;
static int hf_cip_cco_ext_status = -1;

static int hf_cip_path_segment = -1;
static int hf_cip_path_segment_type = -1;
static int hf_cip_port_ex_link_addr = -1;
static int hf_cip_port = -1;
static int hf_cip_port_extended = -1;
static int hf_cip_link_address_size = -1;
static int hf_cip_link_address_byte = -1;
static int hf_cip_link_address_string = -1;
static int hf_cip_logical_seg_type = -1;
static int hf_cip_logical_seg_format = -1;
static int hf_cip_class8 = -1;
static int hf_cip_class16 = -1;
static int hf_cip_class32 = -1;
static int hf_cip_instance8 = -1;
static int hf_cip_instance16 = -1;
static int hf_cip_instance32 = -1;
static int hf_cip_member8 = -1;
static int hf_cip_member16 = -1;
static int hf_cip_member32 = -1;
static int hf_cip_attribute8 = -1;
static int hf_cip_attribute16 = -1;
static int hf_cip_attribute32 = -1;
static int hf_cip_conpoint8 = -1;
static int hf_cip_conpoint16 = -1;
static int hf_cip_conpoint32 = -1;
static int hf_cip_serviceid8 = -1;
static int hf_cip_ekey_format = -1;
static int hf_cip_ekey_vendor = -1;
static int hf_cip_ekey_devtype = -1;
static int hf_cip_ekey_prodcode = -1;
static int hf_cip_ekey_compatibility = -1;
static int hf_cip_ekey_comp_bit = -1;
static int hf_cip_ekey_majorrev = -1;
static int hf_cip_ekey_minorrev = -1;
static int hf_cip_ekey_serial_number = -1;
static int hf_cip_ext_logical8 = -1;
static int hf_cip_ext_logical16 = -1;
static int hf_cip_ext_logical32 = -1;
static int hf_cip_ext_logical_type = -1;
static int hf_cip_data_seg_type = -1;
static int hf_cip_data_seg_size_simple = -1;
static int hf_cip_data_seg_size_extended = -1;
static int hf_cip_data_seg_item = -1;
static int hf_cip_symbol = -1;
static int hf_cip_symbol_size = -1;
static int hf_cip_symbol_ascii = -1;
static int hf_cip_symbol_extended_format = -1;
static int hf_cip_symbol_numeric_format = -1;
static int hf_cip_symbol_double_size = -1;
static int hf_cip_symbol_triple_size = -1;
static int hf_cip_numeric_usint = -1;
static int hf_cip_numeric_uint = -1;
static int hf_cip_numeric_udint = -1;
static int hf_cip_network_seg_type = -1;
static int hf_cip_seg_schedule = -1;
static int hf_cip_seg_fixed_tag = -1;
static int hf_cip_seg_prod_inhibit_time = -1;
static int hf_cip_seg_prod_inhibit_time_us = -1;
static int hf_cip_seg_network_size = -1;
static int hf_cip_seg_network_subtype = -1;
static int hf_cip_seg_safety_format = -1;
static int hf_cip_seg_safety_reserved = -1;
static int hf_cip_seg_safety_configuration_crc = -1;
static int hf_cip_seg_safety_configuration_timestamp = -1;
static int hf_cip_seg_safety_configuration_date = -1;
static int hf_cip_seg_safety_configuration_time = -1;
static int hf_cip_seg_safety_time_correction_epi = -1;
static int hf_cip_seg_safety_time_correction_net_params = -1;
static int hf_cip_seg_safety_time_correction_own = -1;
static int hf_cip_seg_safety_time_correction_typ = -1;
static int hf_cip_seg_safety_time_correction_prio = -1;
static int hf_cip_seg_safety_time_correction_fixed_var = -1;
static int hf_cip_seg_safety_time_correction_con_size = -1;
static int hf_cip_seg_safety_tunid = -1;
static int hf_cip_seg_safety_tunid_snn_timestamp = -1;
static int hf_cip_seg_safety_tunid_snn_date = -1;
static int hf_cip_seg_safety_tunid_snn_time = -1;
static int hf_cip_seg_safety_tunid_nodeid = -1;
static int hf_cip_seg_safety_ounid = -1;
static int hf_cip_seg_safety_ounid_snn_timestamp = -1;
static int hf_cip_seg_safety_ounid_snn_date = -1;
static int hf_cip_seg_safety_ounid_snn_time = -1;
static int hf_cip_seg_safety_ounid_nodeid = -1;
static int hf_cip_seg_safety_ping_epi_multiplier = -1;
static int hf_cip_seg_safety_time_coord_msg_min_multiplier = -1;
static int hf_cip_seg_safety_network_time_expected_multiplier = -1;
static int hf_cip_seg_safety_timeout_multiplier = -1;
static int hf_cip_seg_safety_max_consumer_number = -1;
static int hf_cip_seg_safety_conn_param_crc = -1;
static int hf_cip_seg_safety_time_correction_conn_id = -1;
static int hf_cip_seg_safety_max_fault_number = -1;
static int hf_cip_seg_safety_init_timestamp = -1;
static int hf_cip_seg_safety_init_rollover = -1;
static int hf_cip_seg_safety_data = -1;
static int hf_cip_class_max_inst32 = -1;
static int hf_cip_class_num_inst32 = -1;
static int hf_cip_reserved8 = -1;
static int hf_cip_reserved24 = -1;
static int hf_cip_pad8 = -1;

static int hf_cip_sc_get_attr_list_attr_count = -1;
static int hf_cip_sc_get_attr_list_attr_status = -1;
static int hf_cip_sc_set_attr_list_attr_count = -1;
static int hf_cip_sc_set_attr_list_attr_status = -1;
static int hf_cip_sc_reset_param = -1;
static int hf_cip_sc_create_instance = -1;
static int hf_cip_sc_mult_serv_pack_num_services = -1;
static int hf_cip_sc_mult_serv_pack_offset = -1;
static int hf_cip_find_next_object_max_instance = -1;
static int hf_cip_find_next_object_num_instances = -1;
static int hf_cip_find_next_object_instance_item = -1;
static int hf_cip_sc_group_sync_is_sync = -1;

/* Parsed Attributes */
static int hf_id_vendor_id = -1;
static int hf_id_device_type = -1;
static int hf_id_product_code = -1;
static int hf_id_major_rev = -1;
static int hf_id_minor_rev = -1;
static int hf_id_status = -1;
static int hf_id_serial_number = -1;
static int hf_id_product_name = -1;
static int hf_id_state = -1;
static int hf_id_config_value = -1;
static int hf_id_heartbeat = -1;
static int hf_id_status_owned = -1;
static int hf_id_status_conf = -1;
static int hf_id_status_extended1 = -1;
static int hf_id_status_minor_fault_rec = -1;
static int hf_id_status_minor_fault_unrec = -1;
static int hf_id_status_major_fault_rec = -1;
static int hf_id_status_major_fault_unrec = -1;
static int hf_id_status_extended2 = -1;
static int hf_msg_rout_num_classes = -1;
static int hf_msg_rout_classes = -1;
static int hf_msg_rout_num_available = -1;
static int hf_msg_rout_num_active = -1;
static int hf_msg_rout_active_connections = -1;
static int hf_conn_mgr_open_requests = -1;
static int hf_conn_mgr_open_format_rejects = -1;
static int hf_conn_mgr_open_resource_rejects = -1;
static int hf_conn_mgr_other_open_rejects = -1;
static int hf_conn_mgr_close_requests = -1;
static int hf_conn_close_format_requests = -1;
static int hf_conn_mgr_close_other_requests = -1;
static int hf_conn_mgr_conn_timouts = -1;
static int hf_conn_mgr_num_conn_entries = -1;
static int hf_conn_mgr_num_conn_entries_bytes = -1;
static int hf_conn_mgr_conn_open_bits = -1;
static int hf_conn_mgr_cpu_utilization = -1;
static int hf_conn_mgr_max_buff_size = -1;
static int hf_conn_mgr_buff_size_remaining = -1;
static int hf_stringi_number_char = -1;
static int hf_stringi_language_char = -1;
static int hf_stringi_char_string_struct = -1;
static int hf_stringi_char_set = -1;
static int hf_stringi_international_string = -1;
static int hf_file_filename = -1;
static int hf_time_sync_ptp_enable = -1;
static int hf_time_sync_is_synchronized = -1;
static int hf_time_sync_sys_time_micro = -1;
static int hf_time_sync_sys_time_nano = -1;
static int hf_time_sync_offset_from_master = -1;
static int hf_time_sync_max_offset_from_master = -1;
static int hf_time_sync_mean_path_delay_to_master = -1;
static int hf_time_sync_gm_clock_clock_id = -1;
static int hf_time_sync_gm_clock_clock_class = -1;
static int hf_time_sync_gm_clock_time_accuracy = -1;
static int hf_time_sync_gm_clock_offset_scaled_log_variance = -1;
static int hf_time_sync_gm_clock_current_utc_offset = -1;
static int hf_time_sync_gm_clock_time_property_flags = -1;
static int hf_time_sync_gm_clock_time_property_flags_leap61 = -1;
static int hf_time_sync_gm_clock_time_property_flags_leap59 = -1;
static int hf_time_sync_gm_clock_time_property_flags_current_utc_valid = -1;
static int hf_time_sync_gm_clock_time_property_flags_ptp_timescale = -1;
static int hf_time_sync_gm_clock_time_property_flags_time_traceable = -1;
static int hf_time_sync_gm_clock_time_property_flags_freq_traceable = -1;
static int hf_time_sync_gm_clock_time_source = -1;
static int hf_time_sync_gm_clock_priority1 = -1;
static int hf_time_sync_gm_clock_priority2 = -1;
static int hf_time_sync_parent_clock_clock_id = -1;
static int hf_time_sync_parent_clock_port_number = -1;
static int hf_time_sync_parent_clock_observed_offset_scaled_log_variance = -1;
static int hf_time_sync_parent_clock_observed_phase_change_rate = -1;
static int hf_time_sync_local_clock_clock_id = -1;
static int hf_time_sync_local_clock_clock_class = -1;
static int hf_time_sync_local_clock_time_accuracy = -1;
static int hf_time_sync_local_clock_offset_scaled_log_variance = -1;
static int hf_time_sync_local_clock_current_utc_offset = -1;
static int hf_time_sync_local_clock_time_property_flags = -1;
static int hf_time_sync_local_clock_time_property_flags_leap61 = -1;
static int hf_time_sync_local_clock_time_property_flags_leap59 = -1;
static int hf_time_sync_local_clock_time_property_flags_current_utc_valid = -1;
static int hf_time_sync_local_clock_time_property_flags_ptp_timescale = -1;
static int hf_time_sync_local_clock_time_property_flags_time_traceable = -1;
static int hf_time_sync_local_clock_time_property_flags_freq_traceable = -1;
static int hf_time_sync_local_clock_time_source = -1;
static int hf_time_sync_num_ports = -1;
static int hf_time_sync_port_state_info_num_ports = -1;
static int hf_time_sync_port_state_info_port_num = -1;
static int hf_time_sync_port_state_info_port_state = -1;
static int hf_time_sync_port_enable_cfg_num_ports = -1;
static int hf_time_sync_port_enable_cfg_port_num = -1;
static int hf_time_sync_port_enable_cfg_port_enable = -1;
static int hf_time_sync_port_log_announce_num_ports = -1;
static int hf_time_sync_port_log_announce_port_num = -1;
static int hf_time_sync_port_log_announce_interval = -1;
static int hf_time_sync_port_log_sync_num_ports = -1;
static int hf_time_sync_port_log_sync_port_num = -1;
static int hf_time_sync_port_log_sync_port_log_sync_interval = -1;
static int hf_time_sync_priority1 = -1;
static int hf_time_sync_priority2 = -1;
static int hf_time_sync_domain_number = -1;
static int hf_time_sync_clock_type = -1;
static int hf_time_sync_clock_type_ordinary = -1;
static int hf_time_sync_clock_type_boundary = -1;
static int hf_time_sync_clock_type_end_to_end = -1;
static int hf_time_sync_clock_type_management = -1;
static int hf_time_sync_clock_type_slave_only = -1;
static int hf_time_sync_manufacture_id_oui = -1;
static int hf_time_sync_manufacture_id_reserved = -1;
static int hf_time_sync_prod_desc_size = -1;
static int hf_time_sync_prod_desc_str = -1;
static int hf_time_sync_revision_data_size = -1;
static int hf_time_sync_revision_data_str = -1;
static int hf_time_sync_user_desc_size = -1;
static int hf_time_sync_user_desc_str = -1;
static int hf_time_sync_port_profile_id_info_num_ports = -1;
static int hf_time_sync_port_profile_id_info_port_num = -1;
static int hf_time_sync_port_profile_id_info_profile_id = -1;
static int hf_time_sync_port_phys_addr_info_num_ports = -1;
static int hf_time_sync_port_phys_addr_info_port_num = -1;
static int hf_time_sync_port_phys_addr_info_phys_proto = -1;
static int hf_time_sync_port_phys_addr_info_addr_size = -1;
static int hf_time_sync_port_phys_addr_info_phys_addr = -1;
static int hf_time_sync_port_proto_addr_info_num_ports = -1;
static int hf_time_sync_port_proto_addr_info_port_num = -1;
static int hf_time_sync_port_proto_addr_info_network_proto = -1;
static int hf_time_sync_port_proto_addr_info_addr_size = -1;
static int hf_time_sync_port_proto_addr_info_port_proto_addr = -1;
static int hf_time_sync_steps_removed = -1;
static int hf_time_sync_sys_time_and_offset_time = -1;
static int hf_time_sync_sys_time_and_offset_offset = -1;
static int hf_port_entry_port = -1;
static int hf_port_type = -1;
static int hf_port_number = -1;
static int hf_port_min_node_num = -1;
static int hf_port_max_node_num = -1;
static int hf_port_name = -1;
static int hf_port_num_comm_object_entries = -1;
static int hf_path_len_usint = -1;
static int hf_path_len_uint = -1;

static int hf_32bitheader = -1;
static int hf_32bitheader_roo = -1;
static int hf_32bitheader_coo = -1;
static int hf_32bitheader_run_idle = -1;

static int hf_cip_connection = -1;
static int hf_cip_fwd_open_in = -1;
static int hf_cip_fwd_close_in = -1;

/* Initialize the subtree pointers */
static gint ett_cip = -1;
static gint ett_cip_class_generic = -1;
static gint ett_cip_class_cm = -1;
static gint ett_cip_class_pccc = -1;
static gint ett_cip_class_mb = -1;
static gint ett_cip_class_cco = -1;

static gint ett_path = -1;
static gint ett_path_seg = -1;
static gint ett_mcsc = -1;
static gint ett_cia_path = -1;
static gint ett_data_seg = -1;
static gint ett_port_path = -1;
static gint ett_network_seg = -1;
static gint ett_network_seg_safety = -1;
static gint ett_network_seg_safety_time_correction_net_params = -1;
static gint ett_cip_seg_safety_tunid = -1;
static gint ett_cip_seg_safety_tunid_snn = -1;
static gint ett_cip_seg_safety_ounid = -1;
static gint ett_cip_seg_safety_ounid_snn = -1;

static gint ett_rrsc = -1;
static gint ett_status_item = -1;
static gint ett_add_status_item = -1;
static gint ett_cmd_data = -1;

static gint ett_cip_get_attributes_all_item = -1;
static gint ett_cip_get_attribute_list = -1;
static gint ett_cip_get_attribute_list_item = -1;
static gint ett_cip_set_attribute_list = -1;
static gint ett_cip_set_attribute_list_item = -1;
static gint ett_cip_mult_service_packet = -1;
static gint ett_cip_msp_offset = -1;

static gint ett_cm_rrsc = -1;
static gint ett_cm_ncp = -1;
static gint ett_cm_mes_req = -1;
static gint ett_cm_cmd_data = -1;
static gint ett_cm_ttt = -1;
static gint ett_cm_add_status_item = -1;
static gint ett_cip_cm_pid = -1;
static gint ett_cip_cm_safety = -1;

static gint ett_pccc_rrsc = -1;
static gint ett_pccc_req_id = -1;
static gint ett_pccc_cmd_data = -1;

static gint ett_mb_rrsc = -1;
static gint ett_mb_cmd_data = -1;

static gint ett_cco_iomap = -1;
static gint ett_cco_con_status = -1;
static gint ett_cco_con_flag = -1;
static gint ett_cco_tdi = -1;
static gint ett_cco_pdi = -1;
static gint ett_cco_ncp = -1;
static gint ett_cco_rrsc = -1;
static gint ett_cco_cmd_data = -1;
static gint ett_cco_ttt = -1;

static gint ett_time_sync_gm_clock_flags = -1;
static gint ett_time_sync_local_clock_flags = -1;
static gint ett_time_sync_port_state_info = -1;
static gint ett_time_sync_port_enable_cfg = -1;
static gint ett_time_sync_port_log_announce = -1;
static gint ett_time_sync_port_log_sync = -1;
static gint ett_time_sync_clock_type = -1;
static gint ett_time_sync_port_profile_id_info = -1;
static gint ett_time_sync_port_phys_addr_info = -1;
static gint ett_time_sync_port_proto_addr_info = -1;
static gint ett_id_status = -1;
static gint ett_32bitheader_tree = -1;

static gint ett_connection_info = -1;

static expert_field ei_mal_identity_revision = EI_INIT;
static expert_field ei_mal_identity_status = EI_INIT;
static expert_field ei_mal_msg_rout_num_classes = EI_INIT;
static expert_field ei_mal_time_sync_gm_clock = EI_INIT;
static expert_field ei_mal_time_sync_parent_clock = EI_INIT;
static expert_field ei_mal_time_sync_local_clock = EI_INIT;
static expert_field ei_mal_time_sync_port_state_info = EI_INIT;
static expert_field ei_mal_time_sync_port_state_info_ports = EI_INIT;
static expert_field ei_mal_time_sync_port_enable_cfg = EI_INIT;
static expert_field ei_mal_time_sync_port_enable_cfg_ports = EI_INIT;
static expert_field ei_mal_time_sync_port_log_announce = EI_INIT;
static expert_field ei_mal_time_sync_port_log_announce_ports = EI_INIT;
static expert_field ei_mal_time_sync_port_log_sync = EI_INIT;
static expert_field ei_mal_time_sync_port_log_sync_ports = EI_INIT;
static expert_field ei_mal_time_sync_clock_type = EI_INIT;
static expert_field ei_mal_time_sync_manufacture_id = EI_INIT;
static expert_field ei_mal_time_sync_prod_desc = EI_INIT;
static expert_field ei_mal_time_sync_prod_desc_64 = EI_INIT;
static expert_field ei_mal_time_sync_prod_desc_size = EI_INIT;
static expert_field ei_mal_time_sync_revision_data = EI_INIT;
static expert_field ei_mal_time_sync_revision_data_32 = EI_INIT;
static expert_field ei_mal_time_sync_revision_data_size = EI_INIT;
static expert_field ei_mal_time_sync_user_desc = EI_INIT;
static expert_field ei_mal_time_sync_user_desc_128 = EI_INIT;
static expert_field ei_mal_time_sync_user_desc_size = EI_INIT;
static expert_field ei_mal_time_sync_port_profile_id_info = EI_INIT;
static expert_field ei_mal_time_sync_port_profile_id_info_ports = EI_INIT;
static expert_field ei_mal_time_sync_port_phys_addr_info = EI_INIT;
static expert_field ei_mal_time_sync_port_phys_addr_info_ports = EI_INIT;
static expert_field ei_mal_time_sync_port_proto_addr_info = EI_INIT;
static expert_field ei_mal_time_sync_port_proto_addr_info_ports = EI_INIT;
static expert_field ei_mal_time_sync_sys_time_and_offset = EI_INIT;
static expert_field ei_proto_log_seg_format = EI_INIT;
static expert_field ei_mal_incomplete_epath = EI_INIT;
static expert_field ei_proto_electronic_key_format = EI_INIT;
static expert_field ei_proto_special_segment_format = EI_INIT;
static expert_field ei_proto_log_seg_type = EI_INIT;
static expert_field ei_proto_log_sub_seg_type = EI_INIT;
static expert_field ei_proto_ext_string_format = EI_INIT;
static expert_field ei_proto_ext_network = EI_INIT;
static expert_field ei_proto_seg_type = EI_INIT;
static expert_field ei_proto_unsupported_datatype = EI_INIT;
static expert_field ei_mal_serv_gal = EI_INIT;
static expert_field ei_mal_serv_gal_count = EI_INIT;
static expert_field ei_mal_serv_sal = EI_INIT;
static expert_field ei_mal_serv_sal_count = EI_INIT;
static expert_field ei_mal_msp_services = EI_INIT;
static expert_field ei_mal_msp_inv_offset = EI_INIT;
static expert_field ei_mal_msp_missing_services = EI_INIT;
static expert_field ei_mal_serv_find_next_object = EI_INIT;
static expert_field ei_mal_serv_find_next_object_count = EI_INIT;
static expert_field ei_mal_rpi_no_data = EI_INIT;
static expert_field ei_mal_fwd_close_missing_data = EI_INIT;
static expert_field ei_mal_opt_attr_list = EI_INIT;
static expert_field ei_mal_opt_service_list = EI_INIT;
static expert_field ei_mal_padded_epath_size = EI_INIT;
static expert_field ei_mal_missing_string_data = EI_INIT;

static expert_field ei_cip_safety_open_type1 = EI_INIT;
static expert_field ei_cip_safety_open_type2a = EI_INIT;
static expert_field ei_cip_safety_open_type2b = EI_INIT;
static expert_field ei_cip_no_fwd_close = EI_INIT;

//// Concurrent Connections
static int hf_cip_cm_cc_version = -1;

static int hf_cip_cc_packet_length = -1;
static int hf_cip_cc_packet_options = -1;
static int hf_cip_cc_packet_type = -1;
static int hf_cip_cc_packet_keepalive = -1;
static int hf_cip_cc_packet_keepalive_hop_count = -1;
static int hf_cip_cc_packet_reserved = -1;
static int hf_cip_cc_packet_seq_number = -1;
static int hf_cip_cc_crc = -1;

// Parameters for Concurrent Extended Network Segment
static int hf_ext_net_seg_hops_count = -1;
static int hf_ext_net_seg_length = -1;
static int hf_ext_net_seg_hop = -1;
static int hf_ext_net_seg_hop_egress_cip_port = -1;
static int hf_ext_net_seg_hop_link_adr_type = -1;
static int hf_ext_net_seg_hop_number_of_linkadr = -1;
static int hf_ext_net_seg_link_address = -1;
static int hf_ext_net_seg_link_ipv4 = -1;
static int hf_ext_net_seg_link_hostname = -1;

static int proto_cc = -1;

/* Define the tree for the frame */
static gint ett_cc_header = -1;
static gint ett_cc_hop = -1;

static expert_field ei_cc_invalid_header_type = EI_INIT;

static const value_string cc_link_adr_type[] = {
    { 0, "8-bit numeric link addresses" },
    { 1, "IPv4 addresses" },
    { 2, "Hostnames" },

    { 0, NULL }
};

static const value_string cc_packet_type_vals[] = {
    { 0, "Invalid" },
    { 1, "Concurrent Connection Packet Format" },

    { 0, NULL }
};

static gint* ett_cc[] =
{
    &ett_cc_header,
    &ett_cc_hop,
};

static dissector_table_t   subdissector_class_table;
static dissector_table_t   subdissector_symbol_table;

/* Translate function to string - CIP Service codes */
static const value_string cip_sc_vals[] = {
   GENERIC_SC_LIST

   { 0,                       NULL }
};

/* Translate function to string - CIP Service codes for CM */
static const value_string cip_sc_vals_cm[] = {
   GENERIC_SC_LIST

   /* Some class specific services */
   { SC_CM_FWD_CLOSE,            "Forward Close" },
   { SC_CM_FWD_OPEN,             "Forward Open" },
   { SC_CM_UNCON_SEND,           "Unconnected Send" },
   { SC_CM_LARGE_FWD_OPEN,       "Large Forward Open" },
   { SC_CM_GET_CONN_DATA,        "Get Connection Data" },
   { SC_CM_SEARCH_CONN_DATA,     "Search Connection Data" },
   { SC_CM_GET_CONN_OWNER,       "Get Connection Owner" },
   { SC_CM_CONCURRENT_FWD_OPEN,  "Concurrent Forward Open" },
   { SC_CM_CONCURRENT_FWD_CLOSE, "Concurrent Forward Close" },

   { 0,                       NULL }
};

/* Translate function to string - CIP Service codes for PCCC */
static const value_string cip_sc_vals_pccc[] = {
   GENERIC_SC_LIST

   /* Some class specific services */
   { SC_PCCC_EXECUTE_PCCC,     "Execute PCCC" },

   { 0,                       NULL }
};

/* Translate function to string - CIP Service codes for MB */
static const value_string cip_sc_vals_mb[] = {
   GENERIC_SC_LIST

   /* Some class specific services */
   { SC_MB_READ_DISCRETE_INPUTS,    "Read Discrete" },
   { SC_MB_READ_COILS,              "Read Coils" },
   { SC_MB_READ_INPUT_REGISTERS,    "Read Input Registers" },
   { SC_MB_READ_HOLDING_REGISTERS,  "Read Holding Registers" },
   { SC_MB_WRITE_COILS,             "Write Coils" },
   { SC_MB_WRITE_HOLDING_REGISTERS, "Write Holding Registers" },
   { SC_MB_PASSTHROUGH,             "Modbus Passthrough" },

   { 0,                       NULL }
};

/* Translate function to string - CIP Service codes for CCO */
static const value_string cip_sc_vals_cco[] = {
   GENERIC_SC_LIST

   /* Some class specific services */
   { SC_CCO_KICK_TIMER,           "Kick Timer" },
   { SC_CCO_OPEN_CONN,            "Open Connection" },
   { SC_CCO_CLOSE_CONN,           "Close Connection" },
   { SC_CCO_STOP_CONN,            "Stop Connection" },
   { SC_CCO_CHANGE_START,         "Change Start" },
   { SC_CCO_GET_STATUS,           "Get Status" },
   { SC_CCO_CHANGE_COMPLETE,      "Change Complete" },
   { SC_CCO_AUDIT_CHANGE,         "Audit Changes" },

   { 0,                       NULL }
};

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

   { 0,        NULL }
};

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

   { 0,        NULL          }
};

const value_string cip_reset_type_vals[] = {
   { 0,        "Cycle Power" },
   { 1,        "Factory Default" },
   { 2,        "Keep Communication Parameters" },

   { 0,        NULL          }
};

/* Translate function to string - Connection priority */
const value_string cip_con_prio_vals[] = {
   { 0,        "Low Priority"  },
   { 1,        "High Priority" },
   { 2,        "Scheduled"     },
   { 3,        "Urgent"        },

   { 0,        NULL            }
};

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

   { 0,        NULL       }
};

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

   { 0,        NULL        }
};

/* Translate function to string - Connection type*/
static const value_string cip_con_vals[] = {
   { 0,        "Originator" },
   { 1,        "Target" },

   { 0,        NULL        }
};

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

   { 0,        NULL        }
};

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

   { 0,        NULL        }
};

/* Translate function to string - Connection type */
const value_string cip_con_type_vals[] = {
   { CONN_TYPE_NULL,        "Null"           },
   { CONN_TYPE_MULTICAST,   "Multicast"      },
   { CONN_TYPE_P2P,         "Point to Point" },
   { CONN_TYPE_RESERVED,    "Reserved"       },

   { 0,        NULL             }
};

/* Translate function to string - Timeout Multiplier */
const value_string cip_con_time_mult_vals[] = {
   { 0,        "*4"   },
   { 1,        "*8"   },
   { 2,        "*16"  },
   { 3,        "*32"  },
   { 4,        "*64"  },
   { 5,        "*128" },
   { 6,        "*256" },
   { 7,        "*512" },

   { 0,        NULL    }
};

/* Translate function to string - Connection Last Action */
static const value_string cip_con_last_action_vals[] = {
   { 0,        "No Owner"           },
   { 1,        "Owner Is Idle Mode" },
   { 2,        "Owner Is Run Mode"  },
   { 255,      "Implementation not supported" },

   { 0,        NULL             }
};

/* Translate function to string - real time transfer format type */
static const value_string cip_con_rtf_vals[] = {
   { 0,        "32-bit Header"  },
   { 1,        "Zero data length idle mode"},
   { 2,        "Modeless"  },
   { 3,        "Heartbeat"  },
   { 5,        "Safety"  },

   { 0,        NULL             }
};

/* Translate function to string - CCO change type */
static const value_string cip_cco_change_type_vals[] = {
   { 0,        "Full"           },
   { 1,        "Incremental"    },

   { 0,        NULL             }
};

static const value_string cip_time_sync_clock_class_vals[] = {
   { 6,        "Primary Reference"          },
   { 7,        "Primary Reference (Hold)"   },
   { 52,       "Degraded Reference A (Master only)"  },
   { 187,      "Degraded Reference B (Master/Slave)" },
   { 248,      "Default"                    },
   { 255,      "Slave Only"                 },

   { 0,        NULL             }
};

static const value_string cip_time_sync_time_accuracy_vals[] = {
   { 0x20,   "Accurate to within 25ns"  },
   { 0x21,   "Accurate to within 100ns" },
   { 0x22,   "Accurate to within 250ns" },
   { 0x23,   "Accurate to within 1us"   },
   { 0x24,   "Accurate to within 2.5us" },
   { 0x25,   "Accurate to within 10us"  },
   { 0x26,   "Accurate to within 25us"  },
   { 0x27,   "Accurate to within 100us" },
   { 0x28,   "Accurate to within 250us" },
   { 0x29,   "Accurate to within 1ms"   },
   { 0x2A,   "Accurate to within 2.5ms" },
   { 0x2B,   "Accurate to within 10ms"  },
   { 0x2C,   "Accurate to within 25ms"  },
   { 0x2D,   "Accurate to within 100ms" },
   { 0x2E,   "Accurate to within 250ms" },
   { 0x2F,   "Accurate to within 1s"    },
   { 0x30,   "Accurate to within 10s"   },
   { 0x31,   "Accurate to >10s"         },
   { 0,      NULL             }
};

static const value_string cip_time_sync_time_source_vals[] = {
   { 0x10,   "Atomic Clock"        },
   { 0x20,   "GPS"                 },
   { 0x30,   "Terrestrial Radio"   },
   { 0x40,   "PTP"                 },
   { 0x50,   "NTP"                 },
   { 0x60,   "Hand Set"            },
   { 0x90,   "Other"               },
   { 0xA0,   "Internal Oscillator" },
   { 0,      NULL             }
};

static const value_string cip_time_sync_port_state_vals[] = {
   { 1,      "INITIALIZING" },
   { 2,      "FAULTY"       },
   { 3,      "DISABLED"     },
   { 4,      "LISTENING"    },
   { 5,      "PRE_MASTER"   },
   { 6,      "MASTER"       },
   { 7,      "PASSIVE"      },
   { 8,      "UNCALIBRATED" },
   { 9,      "SLAVE"        },
   { 0,      NULL           }
};

static const value_string cip_time_sync_network_protocol_vals[] = {
   { 1,      "UDP/IPv4"     },
   { 2,      "UDP/IPv6"     },
   { 3,      "IEEE 802.3"   },
   { 4,      "DeviceNet"    },
   { 5,      "ControlNet"   },
   { 0xFFFF, "Local or Unknown protocol"   },
   { 0,      NULL           }
};

static const value_string cip_path_seg_vals[] = {
   { ((CI_PORT_SEGMENT>>5)&7),       "Port Segment" },
   { ((CI_LOGICAL_SEGMENT>>5)&7),    "Logical Segment" },
   { ((CI_NETWORK_SEGMENT>>5)&7),    "Network Segment" },
   { ((CI_SYMBOLIC_SEGMENT>>5)&7),   "Symbolic Segment" },
   { ((CI_DATA_SEGMENT>>5)&7),       "Data Segment" },
   { 5,             "Constructed Data Type" },
   { 6,             "Elementary Data Type" },
   { 7,             "Reserved" },

   { 0,                          NULL }
};

static const value_string cip_logical_segment_type_vals[] = {
   { ((CI_LOGICAL_SEG_CLASS_ID>>2)&7),      "Class ID" },
   { ((CI_LOGICAL_SEG_INST_ID>>2)&7),       "Instance ID" },
   { ((CI_LOGICAL_SEG_MBR_ID>>2)&7),        "Member ID" },
   { ((CI_LOGICAL_SEG_CON_POINT>>2)&7),     "Connection Point" },
   { ((CI_LOGICAL_SEG_ATTR_ID>>2)&7),       "Attribute ID" },
   { ((CI_LOGICAL_SEG_SPECIAL>>2)&7),       "Special" },
   { ((CI_LOGICAL_SEG_SERV_ID>>2)&7),       "Service ID" },
   { ((CI_LOGICAL_SEG_EXT_LOGICAL>>2)&7),   "Extended Logical" },

   { 0,                          NULL }
};

static const value_string cip_logical_segment_format_vals[] = {
   { CI_LOGICAL_SEG_8_BIT,           "8-bit Logical Segment" },
   { CI_LOGICAL_SEG_16_BIT,          "16-bit Logical Segment" },
   { CI_LOGICAL_SEG_32_BIT,          "32-bit Logical Segment" },
   { CI_LOGICAL_SEG_RES_2,           "Reserved" },

   { 0,                          NULL }
};

static const value_string cip_logical_seg_vals[] = {
   {((CI_LOGICAL_SEG_CLASS_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_8_BIT), "8-Bit Class Segment"},
   {((CI_LOGICAL_SEG_CLASS_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_16_BIT), "16-Bit Class Segment"},
   {((CI_LOGICAL_SEG_CLASS_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_32_BIT), "32-Bit Class Segment"},

   {((CI_LOGICAL_SEG_INST_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_8_BIT), "8-Bit Instance Segment"},
   {((CI_LOGICAL_SEG_INST_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_16_BIT), "16-Bit Instance Segment"},
   {((CI_LOGICAL_SEG_INST_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_32_BIT), "32-Bit Instance Segment"},

   {((CI_LOGICAL_SEG_MBR_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_8_BIT), "8-Bit Member Segment"},
   {((CI_LOGICAL_SEG_MBR_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_16_BIT), "16-Bit Member Segment"},
   {((CI_LOGICAL_SEG_MBR_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_32_BIT), "32-Bit Member Segment"},

   {((CI_LOGICAL_SEG_CON_POINT & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_8_BIT), "8-Bit Connection Point Segment"},
   {((CI_LOGICAL_SEG_CON_POINT & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_16_BIT), "16-Bit Connection Point Segment"},
   {((CI_LOGICAL_SEG_CON_POINT & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_32_BIT), "32-Bit Connection Point Segment"},

   {((CI_LOGICAL_SEG_ATTR_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_8_BIT), "8-Bit Attribute Segment"},
   {((CI_LOGICAL_SEG_ATTR_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_16_BIT), "16-Bit Attribute Segment"},
   {((CI_LOGICAL_SEG_ATTR_ID & CI_LOGICAL_SEG_TYPE_MASK)|CI_LOGICAL_SEG_32_BIT), "32-Bit Attribute Segment"},

   {((CI_LOGICAL_SEG_SERV_ID & CI_LOGICAL_SEG_TYPE_MASK) | CI_LOGICAL_SEG_8_BIT), "8-Bit Service ID Segment"},

   {CI_LOGICAL_SEG_SPECIAL, "Electronic Key Segment"},

   {((CI_LOGICAL_SEG_EXT_LOGICAL & CI_LOGICAL_SEG_TYPE_MASK) | CI_LOGICAL_SEG_8_BIT), "8-Bit Extended Logical Segment"},
   {((CI_LOGICAL_SEG_EXT_LOGICAL & CI_LOGICAL_SEG_TYPE_MASK) | CI_LOGICAL_SEG_16_BIT), "16-Bit Extended Logical Segment"},
   {((CI_LOGICAL_SEG_EXT_LOGICAL & CI_LOGICAL_SEG_TYPE_MASK) | CI_LOGICAL_SEG_32_BIT), "32-Bit Extended Logical Segment"},

   { 0,                          NULL }
};

static const value_string cip_ext_logical_segment_format_vals[] = {
    { 0,          "Reserved" },
    { 1,          "Array Index" },
    { 2,          "Indirect Array Index" },
    { 3,          "Bit Index" },
    { 4,          "Indirect Bit Index" },
    { 5,          "Structure Member Number" },
    { 6,          "Structure Member Handle" },

    { 0,                          NULL }
};

static const value_string cip_data_segment_type_vals[] = {
   {CI_DATA_SEG_SIMPLE, "Simple Data Segment"},
   {CI_DATA_SEG_SYMBOL, "ANSI Extended Symbol Segment"},

   { 0,                          NULL }
};

static const value_string cip_network_segment_type_vals[] = {
   {CI_NETWORK_SEG_SCHEDULE,     "Schedule Segment"},
   {CI_NETWORK_SEG_FIXED_TAG,    "Fixed Tag Segment"},
   {CI_NETWORK_SEG_PROD_INHI,    "Production Inhibit Time in Milliseconds"},
   {CI_NETWORK_SEG_SAFETY,       "Safety Segment"},
   {CI_NETWORK_SEG_PROD_INHI_US, "Production Inhibit Time in Microseconds"},
   {CI_NETWORK_SEG_EXTENDED,     "Extended Network Segment"},

   { 0,                          NULL }
};

static const value_string cip_symbolic_format_vals[] = {
    { 1,          "Double Byte Segment" },
    { 2,          "Triple Byte Segment" },
    { 6,          "Numeric Segment" },

    { 0,          NULL }
};

static const value_string cip_symbolic_numeric_format_vals[] = {
    { 6,          "USINT" },
    { 7,          "UINT" },
    { 8,          "UDINT" },

    { 0,          NULL }
};

static const value_string cip_safety_segment_format_type_vals[] = {
   {0,    "Target Format"},
   {1,    "Router Format"},
   {2,    "Extended Format"},

   { 0,                          NULL }
};

static const value_string cip_cm_rpi_type_vals[] = {
   {0,   "RPI acceptable"},
   {1,   "Unspecified"},
   {2,   "Minimum acceptable RPI"},
   {3,   "Maximum acceptable RPI"},
   {4,   "Required RPI to correct mismatch"},

   { 0,                          NULL }
};

/* Translate function to string - CIP General Status codes */
static const value_string cip_gs_vals[] = {
   { CI_GRC_SUCCESS,             "Success" },
   { CI_GRC_FAILURE,             "Connection failure" },
   { CI_GRC_NO_RESOURCE,         "Resource unavailable" },
   { CI_GRC_BAD_DATA,            "Invalid parameter value" },
   { CI_GRC_BAD_PATH,            "Path segment error" },
   { CI_GRC_BAD_CLASS_INSTANCE,  "Path destination unknown" },
   { CI_GRC_PARTIAL_DATA,        "Partial transfer" },
   { CI_GRC_CONN_LOST,           "Connection lost" },
   { CI_GRC_BAD_SERVICE,         "Service not supported" },
   { CI_GRC_BAD_ATTR_DATA,       "Invalid attribute value" },
   { CI_GRC_ATTR_LIST_ERROR,     "Attribute list error" },
   { CI_GRC_ALREADY_IN_MODE,     "Already in requested mode/state" },
   { CI_GRC_BAD_OBJ_MODE,        "Object state conflict" },
   { CI_GRC_OBJ_ALREADY_EXISTS,  "Object already exists" },
   { CI_GRC_ATTR_NOT_SETTABLE,   "Attribute not settable" },
   { CI_GRC_PERMISSION_DENIED,   "Privilege violation" },
   { CI_GRC_DEV_IN_WRONG_STATE,  "Device state conflict" },
   { CI_GRC_REPLY_DATA_TOO_LARGE,"Reply data too large" },
   { CI_GRC_FRAGMENT_PRIMITIVE,  "Fragmentation of a primitive value" },
   { CI_GRC_CONFIG_TOO_SMALL,    "Not enough data" },
   { CI_GRC_UNDEFINED_ATTR,      "Attribute not supported" },
   { CI_GRC_CONFIG_TOO_BIG,      "Too much data" },
   { CI_GRC_OBJ_DOES_NOT_EXIST,  "Object does not exist" },
   { CI_GRC_NO_FRAGMENTATION,    "Service fragmentation sequence not in progress" },
   { CI_GRC_DATA_NOT_SAVED,      "No stored attribute data" },
   { CI_GRC_DATA_WRITE_FAILURE,  "Store operation failure" },
   { CI_GRC_REQUEST_TOO_LARGE,   "Routing failure, request packet too large" },
   { CI_GRC_RESPONSE_TOO_LARGE,  "Routing failure, response packet too large" },
   { CI_GRC_MISSING_LIST_DATA,   "Missing attribute list entry data" },
   { CI_GRC_INVALID_LIST_STATUS, "Invalid attribute value list" },
   { CI_GRC_SERVICE_ERROR,       "Embedded service error" },
   { CI_GRC_CONN_RELATED_FAILURE,"Vendor specific error" },
   { CI_GRC_INVALID_PARAMETER,   "Invalid parameter" },
   { CI_GRC_WRITE_ONCE_FAILURE,  "Write-once value or medium already written" },
   { CI_GRC_INVALID_REPLY,       "Invalid reply received" },
   { CI_GRC_BUFFER_OVERFLOW,     "Buffer overflow" },
   { CI_GRC_MESSAGE_FORMAT,      "Invalid message format" },
   { 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" },
   { CI_GRC_G2_SERVER_FAILURE,   "Group 2 only server general failure" },
   { CI_GRC_UNKNOWN_MB_ERROR,    "Unknown Modbus error" },
   { CI_GRC_ATTRIBUTE_NOT_GET,   "Attribute not gettable" },

   { 0,                          NULL }
};

value_string_ext cip_gs_vals_ext = VALUE_STRING_EXT_INIT(cip_gs_vals);

/* Connection Manager Extended Status codes */
#define CM_ES_DUP_FWD_OPEN                            0x100
#define CM_ES_CLASS_AND_TRIGGER                       0x103
#define CM_ES_OWNERSHIP_CONFLICT                      0x106
#define CM_ES_TARGET_CONN_NOT_FOUND                   0x107
#define CM_ES_INVALID_NET_CONN_PARAM                  0x108
#define CM_ES_INVALID_CONNECTION_SIZE                 0x109
#define CM_ES_TARGET_CONNECTION_NOT_CONFIGURED        0x110
#define CM_ES_RPI_NOT_SUPPORTED                       0x111
#define CM_ES_RPI_NOT_ACCEPTABLE                      0x112
#define CM_ES_OUT_OF_CONNECTIONS                      0x113
#define CM_ES_VENDOR_ID_OR_PRODUCT_CODE_MISMATCH      0x114
#define CM_ES_DEVICE_TYPE_MISMATCH                    0x115
#define CM_ES_REVISION_MISMATCH                       0x116
#define CM_ES_INVALID_PROD_CONS_APP_PATH              0x117
#define CM_ES_INVALID_OR_INCONSISTENT_CONF_APP_PATH   0x118
#define CM_ES_NON_LISTEN_ONLY_CONN_NOT_OPENED         0x119
#define CM_ES_TARGET_OBJECT_OUT_OF_CONNECTIONS        0x11A
#define CM_ES_RPI_SMALLER_THAN_PROD_INHIBIT_TIME      0x11B
#define CM_ES_TRANSPORT_CLASS_NOT_SUPPORTED           0x11C
#define CM_ES_PRODUCTION_TRIGGER_NOT_SUPPORTED        0x11D
#define CM_ES_DIRECTION_NOT_SUPPORTED                 0x11E
#define CM_ES_INVALID_OT_NET_CONN_FIX_VAR             0x11F
#define CM_ES_INVALID_TO_NET_CONN_FIX_VAR             0x120
#define CM_ES_INVALID_OT_NET_CONN_PRIORITY            0x121
#define CM_ES_INVALID_TO_NET_CONN_PRIORITY            0x122
#define CM_ES_INVALID_OT_NET_CONN_TYPE                0x123
#define CM_ES_INVALID_TO_NET_CONN_TYPE                0x124
#define CM_ES_INVALID_OT_NET_CONN_REDUNDANT_OWNER     0x125
#define CM_ES_INVALID_CONFIGURATION_SIZE              0x126
#define CM_ES_INVALID_OT_SIZE                         0x127
#define CM_ES_INVALID_TO_SIZE                         0x128
#define CM_ES_INVALID_CONFIGURATION_APP_PATH          0x129
#define CM_ES_INVALID_CONSUMING_APP_PATH              0x12A
#define CM_ES_INVALID_PRODUCING_APP_PATH              0x12B
#define CM_ES_CONFIGURATION_SYMBOL_NOT_EXIST          0x12C
#define CM_ES_CONSUMING_SYMBOL_NOT_EXIST              0x12D
#define CM_ES_PRODUCING_SYMBOL_NOT_EXIST              0x12E
#define CM_ES_INCONSISTENT_APP_PATH_COMBO             0x12F
#define CM_ES_INCONSISTENT_CONSUME_DATA_FORMAT        0x130
#define CM_ES_INCONSISTENT_PRODUCE_DATA_FORMAT        0x131
#define CM_ES_NULL_FORWARD_OPEN_NOT_SUPPORTED         0x132
#define CM_ES_CONNECTION_TIMED_OUT                    0x203
#define CM_ES_UNCONNECTED_REQUEST_TIMED_OUT           0x204
#define CM_ES_PARAMETER_ERROR_IN_UNCONNECTED_REQUEST  0x205
#define CM_ES_MESSAGE_TOO_LARGE_FOR_UNCONNECTED_SEND  0x206
#define CM_ES_UNCONNECTED_ACK_WITHOUT_REPLY           0x207
#define CM_ES_NO_BUFFER_MEMORY_AVAILABLE              0x301
#define CM_ES_NETWORK_BANDWIDTH_NOT_AVAIL_FOR_DATA    0x302
#define CM_ES_NO_CONSUMED_CONN_ID_FILTER_AVAILABLE    0x303
#define CM_ES_NOT_CONFIGURED_TO_SEND_SCHEDULED_DATA   0x304
#define CM_ES_SCHEDULE_SIGNATURE_MISMATCH             0x305
#define CM_ES_SCHEDULE_SIGNATURE_VALIDATION_NOT_POSS  0x306
#define CM_ES_PORT_NOT_AVAILABLE                      0x311
#define CM_ES_LINK_ADDRESS_NOT_VALID                  0x312
#define CM_ES_INVALID_SEGMENT_IN_CONN_PATH            0x315
#define CM_ES_FWD_CLOSE_CONN_PATH_MISMATCH            0x316
#define CM_ES_SCHEDULING_NOT_SPECIFIED                0x317
#define CM_ES_LINK_ADDRESS_TO_SELF_INVALID            0x318
#define CM_ES_SECONDARY_RESOURCES_UNAVAILABLE         0x319
#define CM_ES_RACK_CONNECTION_ALREADY_ESTABLISHED     0x31A
#define CM_ES_MODULE_CONNECTION_ALREADY_ESTABLISHED   0x31B
#define CM_ES_MISCELLANEOUS                           0x31C
#define CM_ES_REDUNDANT_CONNECTION_MISMATCH           0x31D
#define CM_ES_NO_CONSUMER_RES_AVAIL_IN_PROD_MODULE    0x31E
#define CM_ES_NO_CONSUMER_RES_CONF_IN_PROD_MODULE     0x31F
#define CM_ES_NETWORK_LINK_OFFLINE                    0x800
#define CM_ES_INCOMPATIBLE_MULTICAST_RPI              0x801
#define CM_ES_INVALID_SAFETY_CONN_SIZE                0x802
#define CM_ES_INVALID_SAFETY_CONN_FORMAT              0x803
#define CM_ES_INVALID_TIME_CORRECTION_CONN_PARAM      0x804
#define CM_ES_INVALID_PING_INTERVAL_EPI_MULTIPLIER    0x805
#define CM_ES_TIME_COORDINATION_MSG_MIN_MULTIPLIER    0x806
#define CM_ES_NETWORK_TIME_EXPECTATION_MULTIPLIER     0x807
#define CM_ES_TIMEOUT_MULTIPLIER                      0x808
#define CM_ES_INVALID_MAX_CONSUMER_NUMBER             0x809
#define CM_ES_INVALID_CPCRC                           0x80A
#define CM_ES_TIME_CORRECTION_CONN_ID_INVALID         0x80B
#define CM_ES_SCID_MISMATCH                           0x80C
#define CM_ES_TUNID_NOT_SET                           0x80D
#define CM_ES_TUNID_MISMATCH                          0x80E
#define CM_ES_CONFIGURATION_OPERATION_NOT_ALLOWED     0x80F
#define CM_ES_NO_TARGET_APP_DATA_AVAILABLE            0x810
#define CM_ES_NO_ORIG_APP_DATA_AVAILABLE              0x811
#define CM_ES_NODE_ADDRESS_CHANGED_AFTER_SCHEDULED    0x812
#define CM_ES_NOT_CONFIGURED_MULTICAST                0x813
#define CM_ES_INVALID_PROD_CONS_DATA_FORMAT           0x814

/* Translate function to string - CIP Extended Status codes */
static const value_string cip_cm_ext_st_vals[] = {
   { CM_ES_DUP_FWD_OPEN,                           "Connection in use or duplicate Forward Open" },
   { CM_ES_CLASS_AND_TRIGGER,                      "Transport class and trigger combination not supported" },
   { CM_ES_OWNERSHIP_CONFLICT,                     "Ownership conflict" },
   { CM_ES_TARGET_CONN_NOT_FOUND,                  "Target connection not found" },
   { CM_ES_INVALID_NET_CONN_PARAM,                 "Invalid network connection parameter" },
   { CM_ES_INVALID_CONNECTION_SIZE,                "Invalid connection size" },
   { CM_ES_TARGET_CONNECTION_NOT_CONFIGURED,       "Target for connection not configured" },
   { CM_ES_RPI_NOT_SUPPORTED,                      "RPI not supported" },
   { CM_ES_RPI_NOT_ACCEPTABLE,                     "RPI value(s) not acceptable" },
   { CM_ES_OUT_OF_CONNECTIONS,                     "Out of connections" },
   { CM_ES_VENDOR_ID_OR_PRODUCT_CODE_MISMATCH,     "Vendor ID or product code mismatch" },
   { CM_ES_DEVICE_TYPE_MISMATCH,                   "Device type mismatch" },
   { CM_ES_REVISION_MISMATCH,                      "Revision mismatch" },
   { CM_ES_INVALID_PROD_CONS_APP_PATH,             "Invalid produced or consumed application path" },
   { CM_ES_INVALID_OR_INCONSISTENT_CONF_APP_PATH,  "Invalid or inconsistent configuration application path" },
   { CM_ES_NON_LISTEN_ONLY_CONN_NOT_OPENED,        "Non-listen only connection not opened" },
   { CM_ES_TARGET_OBJECT_OUT_OF_CONNECTIONS,       "Target object out of connections" },
   { CM_ES_RPI_SMALLER_THAN_PROD_INHIBIT_TIME,     "RPI is smaller than the production inhibit time" },
   { CM_ES_TRANSPORT_CLASS_NOT_SUPPORTED,          "Transport class not supported" },
   { CM_ES_PRODUCTION_TRIGGER_NOT_SUPPORTED,       "Production trigger not supported" },
   { CM_ES_DIRECTION_NOT_SUPPORTED,                "Direction not supported" },
   { CM_ES_INVALID_OT_NET_CONN_FIX_VAR,            "Invalid O->T Fixed/Variable" },
   { CM_ES_INVALID_TO_NET_CONN_FIX_VAR,            "Invalid T->O Fixed/Variable" },
   { CM_ES_INVALID_OT_NET_CONN_PRIORITY,           "Invalid O->T Priority" },
   { CM_ES_INVALID_TO_NET_CONN_PRIORITY,           "Invalid T->O Priority" },
   { CM_ES_INVALID_OT_NET_CONN_TYPE,               "Invalid O->T connection type" },
   { CM_ES_INVALID_TO_NET_CONN_TYPE,               "Invalid T->O connection type" },
   { CM_ES_INVALID_OT_NET_CONN_REDUNDANT_OWNER,    "Invalid O->T redundant owner" },
   { CM_ES_INVALID_CONFIGURATION_SIZE,             "Invalid configuration size" },
   { CM_ES_INVALID_OT_SIZE,                        "Invalid O->T size" },
   { CM_ES_INVALID_TO_SIZE,                        "Invalid T->O size" },
   { CM_ES_INVALID_CONFIGURATION_APP_PATH,         "Invalid configuration application path" },
   { CM_ES_INVALID_CONSUMING_APP_PATH,             "Invalid consuming application path" },
   { CM_ES_INVALID_PRODUCING_APP_PATH,             "Invalid producing application path" },
   { CM_ES_CONFIGURATION_SYMBOL_NOT_EXIST,         "Configuration symbol does not exist" },
   { CM_ES_CONSUMING_SYMBOL_NOT_EXIST,             "Consuming symbol does not exist" },
   { CM_ES_PRODUCING_SYMBOL_NOT_EXIST,             "Producing symbol does not exist" },
   { CM_ES_INCONSISTENT_APP_PATH_COMBO,            "Inconsistent application path combination" },
   { CM_ES_INCONSISTENT_CONSUME_DATA_FORMAT,       "Inconsistent consume data format" },
   { CM_ES_INCONSISTENT_PRODUCE_DATA_FORMAT,       "Inconsistent produce data format" },
   { CM_ES_NULL_FORWARD_OPEN_NOT_SUPPORTED,        "NULL ForwardOpen not supported" },
   { CM_ES_CONNECTION_TIMED_OUT,                   "Connection timed out" },
   { CM_ES_UNCONNECTED_REQUEST_TIMED_OUT,          "Unconnected request timed out" },
   { CM_ES_PARAMETER_ERROR_IN_UNCONNECTED_REQUEST, "Parameter error in unconnected request" },
   { CM_ES_MESSAGE_TOO_LARGE_FOR_UNCONNECTED_SEND, "Message too large for UnconnectedSend" },
   { CM_ES_UNCONNECTED_ACK_WITHOUT_REPLY,          "Unconnected acknowledged without reply" },
   { CM_ES_NO_BUFFER_MEMORY_AVAILABLE,             "No buffer memory available" },
   { CM_ES_NETWORK_BANDWIDTH_NOT_AVAIL_FOR_DATA,   "Network bandwidth not available for data" },
   { CM_ES_NO_CONSUMED_CONN_ID_FILTER_AVAILABLE,   "No consumed connection ID filter available" },
   { CM_ES_NOT_CONFIGURED_TO_SEND_SCHEDULED_DATA,  "Not configured to send scheduled priority data" },
   { CM_ES_SCHEDULE_SIGNATURE_MISMATCH,            "Schedule signature mismatch" },
   { CM_ES_SCHEDULE_SIGNATURE_VALIDATION_NOT_POSS, "Schedule signature validation not possible" },
   { CM_ES_PORT_NOT_AVAILABLE,                     "Port not available" },
   { CM_ES_LINK_ADDRESS_NOT_VALID,                 "Link address not valid" },
   { CM_ES_INVALID_SEGMENT_IN_CONN_PATH,           "Invalid segment in connection path" },
   { CM_ES_FWD_CLOSE_CONN_PATH_MISMATCH,           "ForwardClose connection path mismatch" },
   { CM_ES_SCHEDULING_NOT_SPECIFIED,               "Scheduling not specified" },
   { CM_ES_LINK_ADDRESS_TO_SELF_INVALID,           "Link address to self invalid" },
   { CM_ES_SECONDARY_RESOURCES_UNAVAILABLE,        "Secondary resources unavailable" },
   { CM_ES_RACK_CONNECTION_ALREADY_ESTABLISHED,    "Rack connection already established" },
   { CM_ES_MODULE_CONNECTION_ALREADY_ESTABLISHED,  "Module connection already established" },
   { CM_ES_MISCELLANEOUS,                          "Miscellaneous" },
   { CM_ES_REDUNDANT_CONNECTION_MISMATCH,          "Redundant connection mismatch" },
   { CM_ES_NO_CONSUMER_RES_AVAIL_IN_PROD_MODULE,   "No more user configurable link consumer resources available in the producing module" },
   { CM_ES_NO_CONSUMER_RES_CONF_IN_PROD_MODULE,    "No more user configurable link consumer resources configured in the producing module" },
   { CM_ES_NETWORK_LINK_OFFLINE,                   "Network link offline" },
   { CM_ES_INCOMPATIBLE_MULTICAST_RPI,             "Incompatible Multicast RPI" },
   { CM_ES_INVALID_SAFETY_CONN_SIZE,               "Invalid Safety Connection Size" },
   { CM_ES_INVALID_SAFETY_CONN_FORMAT,             "Invalid Safety Connection Format" },
   { CM_ES_INVALID_TIME_CORRECTION_CONN_PARAM,     "Invalid Time Correction Connection Parameters" },
   { CM_ES_INVALID_PING_INTERVAL_EPI_MULTIPLIER,   "Invalid Ping Interval EPI Multiplier" },
   { CM_ES_TIME_COORDINATION_MSG_MIN_MULTIPLIER,   "Time Coordination Msg Min Multiplier" },
   { CM_ES_NETWORK_TIME_EXPECTATION_MULTIPLIER,    "Network Time Expectation Multiplier" },
   { CM_ES_TIMEOUT_MULTIPLIER,                     "Timeout Multiplier" },
   { CM_ES_INVALID_MAX_CONSUMER_NUMBER,            "Invalid Max Consumer Number" },
   { CM_ES_INVALID_CPCRC,                          "Invalid CPCRC" },
   { CM_ES_TIME_CORRECTION_CONN_ID_INVALID,        "Time Correction Connection ID Invalid" },
   { CM_ES_SCID_MISMATCH,                          "SCID Mismatch" },
   { CM_ES_TUNID_NOT_SET,                          "TUNID not set" },
   { CM_ES_TUNID_MISMATCH,                         "TUNID Mismatch" },
   { CM_ES_CONFIGURATION_OPERATION_NOT_ALLOWED,    "Configuration operation not allowed" },
   { CM_ES_NO_TARGET_APP_DATA_AVAILABLE,           "No target application data available" },
   { CM_ES_NO_ORIG_APP_DATA_AVAILABLE,             "No originator application data available" },
   { CM_ES_NODE_ADDRESS_CHANGED_AFTER_SCHEDULED,   "Node address has changed since the network was scheduled" },
   { CM_ES_NOT_CONFIGURED_MULTICAST,               "Not configured for off-subnet multicast" },
   { CM_ES_INVALID_PROD_CONS_DATA_FORMAT,          "Invalid produce/consume data format" },

   { 0,                          NULL }
};

value_string_ext cip_cm_ext_st_vals_ext = VALUE_STRING_EXT_INIT(cip_cm_ext_st_vals);

/* Translate function to string - PCCC Status codes */
static const value_string cip_pccc_gs_st_vals[] = {
   { PCCC_GS_SUCCESS,                         "Success" },
   { PCCC_GS_ILLEGAL_CMD,                     "Illegal command or format" },
   { PCCC_GS_HOST_COMMS,                      "Host has a problem and will not communicate" },
   { PCCC_GS_MISSING_REMOTE_NODE,             "Remote node host is missing, disconnected, or shut down" },
   { PCCC_GS_HARDWARE_FAULT,                  "Host could not complete function due to hardware fault" },
   { PCCC_GS_ADDRESSING_ERROR,                "Addressing problem or memory protect rungs" },
   { PCCC_GS_CMD_PROTECTION,                  "Function not allowed due to command protection selection" },
   { PCCC_GS_PROGRAM_MODE,                    "Processor is in Program mode" },
   { PCCC_GS_MISSING_COMPATABILITY_FILE,      "Compatibility mode file missing or communication zone problem" },
   { PCCC_GS_BUFFER_FULL_1,                   "Remote node cannot buffer command" },
   { PCCC_GS_WAIT_ACK,                        "Wait ACK (1775-KA buffer full)" },
   { PCCC_GS_REMOTE_DOWNLOAD_ERROR,           "Remote node problem due to download" },
   { PCCC_GS_BUFFER_FULL_2,                   "Wait ACK (1775-KA buffer full)" },
   { PCCC_GS_NOT_USED_1,                      "Not used" },
   { PCCC_GS_NOT_USED_2,                      "Not used" },
   { PCCC_GS_USE_EXTSTS,                      "Error code in the EXT STS byte" },

   { 0,                          NULL }
};

static value_string_ext cip_pccc_gs_st_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_gs_st_vals);

/* Translate function to string - PCCC Extended Status codes */
static const value_string cip_pccc_es_st_vals[] = {
   { PCCC_ES_ILLEGAL_VALUE,                        "A field has an illegal value" },
   { PCCC_ES_SHORT_ADDRESS,                        "Less levels specified in address than minimum for any address" },
   { PCCC_ES_LONG_ADDRESS,                         "More levels specified in address than system supports" },
   { PCCC_ES_NOT_FOUND,                            "Symbol not found" },
   { PCCC_ES_BAD_FORMAT,                           "Symbol is of improper format" },
   { PCCC_ES_BAD_POINTER,                          "Address doesn't point to something usable" },
   { PCCC_ES_BAD_SIZE,                             "File is wrong size" },
   { PCCC_ES_SITUATION_CHANGED,                    "Cannot complete request, situation has changed since the start of the command" },
   { PCCC_ES_DATA_TOO_LARGE,                       "Data or file is too large" },
   { PCCC_ES_TRANS_TOO_LARGE,                      "Transaction size plus word address is too large" },
   { PCCC_ES_ACCESS_DENIED,                        "Access denied, improper privilege" },
   { PCCC_ES_NOT_AVAILABLE,                        "Condition cannot be generated - resource is not available" },
   { PCCC_ES_ALREADY_EXISTS,                       "Condition already exists - resource is already available" },
   { PCCC_ES_NO_EXECUTION,                         "Command cannot be executed" },
   { PCCC_ES_HIST_OVERFLOW,                        "Histogram overflow" },
   { PCCC_ES_NO_ACCESS,                            "No access" },
   { PCCC_ES_ILLEGAL_DATA_TYPE,                    "Illegal data type" },
   { PCCC_ES_INVALID_DATA,                         "Invalid parameter or invalid data" },
   { PCCC_ES_BAD_REFERENCE,                        "Address reference exists to deleted area" },
   { PCCC_ES_EXECUTION_FAILURE,                    "Command execution failure for unknown reason; possible PLC-3 histogram overflow" },
   { PCCC_ES_CONVERSION_ERROR,                     "Data conversion error" },
   { PCCC_ES_NO_COMMS,                             "Scanner not able to communicate with 1771 rack adapter" },
   { PCCC_ES_TYPE_MISMATCH,                        "Type mismatch" },
   { PCCC_ES_BAD_RESPONSE,                         "1771 module response was not valid" },
   { PCCC_ES_DUP_LABEL,                            "Duplicated label" },
   { PCCC_ES_FILE_ALREADY_OPEN,                    "File is open; another node owns it" },
   { PCCC_ES_PROGRAM_ALREADY_OWNED,                "Another node is the program owner" },
   { PCCC_ES_RESERVED_1,                           "Reserved" },
   { PCCC_ES_RESERVED_2,                           "Reserved" },
   { PCCC_ES_PROTECTION_VIOLATION,                 "Data table element protection violation" },
   { PCCC_ES_TMP_INTERNAL_ERROR,                   "Temporary internal problem" },
   { PCCC_ES_RACK_FAULT,                           "Remote rack fault" },
   { PCCC_ES_TIMEOUT,                              "Timeout" },
   { PCCC_ES_UNKNOWN,                              "Unknown error" },

   { 0,                          NULL }
};

static value_string_ext cip_pccc_es_st_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_es_st_vals);

/* Translate PCCC Function Codes */
static const value_string cip_pccc_fnc_06_vals[] = {
    { PCCC_FNC_06_00, "Echo" },
    { PCCC_FNC_06_01, "Read diagnostic counters" },
    { PCCC_FNC_06_02, "Set variables" },
    { PCCC_FNC_06_03, "Diagnostic status" },
    { PCCC_FNC_06_04, "Set timeout" },
    { PCCC_FNC_06_05, "Set NAKs" },
    { PCCC_FNC_06_06, "Set ENQs" },
    { PCCC_FNC_06_07, "Reset diagnostic counters" },
    { PCCC_FNC_06_08, "Set data table size" },
    { PCCC_FNC_06_09, "Read link parameters" },
    { PCCC_FNC_06_0A, "Set link parameters" },

   { 0,                          NULL }
};

static value_string_ext cip_pccc_fnc_06_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_fnc_06_vals);

static const value_string cip_pccc_fnc_07_vals[] = {
    { PCCC_FNC_07_00, "Disable outputs" },
    { PCCC_FNC_07_01, "Enable outputs" },
    { PCCC_FNC_07_03, "Enable PLC scanning" },
    { PCCC_FNC_07_04, "Enter download mode" },
    { PCCC_FNC_07_05, "Exit download/upload mode" },
    { PCCC_FNC_07_06, "Enter upload mode" },

   { 0,                          NULL }
};

static value_string_ext cip_pccc_fnc_07_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_fnc_07_vals);

static const value_string cip_pccc_fnc_0f_vals[] = {
    { PCCC_FNC_0F_00, "Word range write" },
    { PCCC_FNC_0F_01, "Word range read" },
    { PCCC_FNC_0F_02, "Bit write" },
    { PCCC_FNC_0F_03, "File write" },
    { PCCC_FNC_0F_04, "File read" },
    { PCCC_FNC_0F_05, "Download request" },
    { PCCC_FNC_0F_06, "Upload" },
    { PCCC_FNC_0F_07, "Shutdown" },
    { PCCC_FNC_0F_08, "Physical write" },
    { PCCC_FNC_0F_09, "Physical read" },
    { PCCC_FNC_0F_0A, "Restart request" },
    { PCCC_FNC_0F_11, "Get edit resource" },
    { PCCC_FNC_0F_12, "Return edit resource" },
    { PCCC_FNC_0F_17, "Read bytes physical" },
    { PCCC_FNC_0F_18, "Write bytes physical" },
    { PCCC_FNC_0F_26, "Read-modify-write" },
    { PCCC_FNC_0F_29, "Read section size" },
    { PCCC_FNC_0F_3A, "Set CPU mode" },
    { PCCC_FNC_0F_41, "Disable forces" },
    { PCCC_FNC_0F_50, "Download all request" },
    { PCCC_FNC_0F_52, "Download completed" },
    { PCCC_FNC_0F_53, "Upload all request (upload)" },
    { PCCC_FNC_0F_55, "Upload completed" },
    { PCCC_FNC_0F_57, "Initialize memory" },
    { PCCC_FNC_0F_5E, "Modify PLC-2 compatibility file" },
    { PCCC_FNC_0F_67, "Typed write" },
    { PCCC_FNC_0F_68, "Typed read" },
    { PCCC_FNC_0F_79, "Read-modify-write N" },
    { PCCC_FNC_0F_80, "Change CPU mode" },
    { PCCC_FNC_0F_81, "Open file" },
    { PCCC_FNC_0F_82, "Close file" },
    { PCCC_FNC_0F_88, "Execute Multiple Commands" },
    { PCCC_FNC_0F_8F, "Apply port configuration" },
    { PCCC_FNC_0F_A1, "Protected typed logical read with two address fields" },
    { PCCC_FNC_0F_A2, "Protected typed logical read with three address fields" },
    { PCCC_FNC_0F_A7, "Protected typed file read" },
    { PCCC_FNC_0F_A9, "Protected typed logical write with two address fields" },
    { PCCC_FNC_0F_AA, "Protected typed logical write with three address fields" },
    { PCCC_FNC_0F_AB, "Protected typed logical masked-write with three address fields" },
    { PCCC_FNC_0F_AF, "Protected typed file write" },

   { 0,                          NULL }
};

static value_string_ext cip_pccc_fnc_0f_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_fnc_0f_vals);

/* Translate PCCC File Types */
static const value_string cip_pccc_file_types_vals[] = {
   { PCCC_FILE_TYPE_LOGIC,               "Ladder Logic File" },
   { PCCC_FILE_TYPE_CHANNEL_CONFIG,      "Channel Configuration File" },
   { PCCC_FILE_TYPE_FUNCTION_ES1,        "EtherNet/IP Function File" },
   { PCCC_FILE_TYPE_ONLINE_EDIT,         "Online Editing File" },
   { PCCC_FILE_TYPE_FUNCTION_IOS,        "IOS Function File" },
   { PCCC_FILE_TYPE_DATA_OUTPUT,         "Output Data File" },
   { PCCC_FILE_TYPE_DATA_INPUT,          "Input Data File" },
   { PCCC_FILE_TYPE_DATA_STATUS,         "Status Data File" },
   { PCCC_FILE_TYPE_DATA_BINARY,         "Binary Data File" },
   { PCCC_FILE_TYPE_DATA_TIMER,          "Timer Data File" },
   { PCCC_FILE_TYPE_DATA_COUNTER,        "Counter Data File" },
   { PCCC_FILE_TYPE_DATA_INTEGER,        "Integer Data File" },
   { PCCC_FILE_TYPE_DATA_FLOAT,          "Float Data File" },
   { PCCC_FILE_TYPE_FORCE_OUTPUT,        "Output Force File" },
   { PCCC_FILE_TYPE_FORCE_INPUT,         "Input Force File" },
   { PCCC_FILE_TYPE_FUNCTION_ES0,        "ES0 Function File" },
   { PCCC_FILE_TYPE_FUNCTION_STI,        "STI Function File" },
   { PCCC_FILE_TYPE_FUNCTION_EII,        "EII Function File" },
   { PCCC_FILE_TYPE_FUNCTION_RTC,        "RTC Function File" },
   { PCCC_FILE_TYPE_FUNCTION_BHI,        "BHI Function File" },
   { PCCC_FILE_TYPE_FUNCTION_MMI,        "Memory Module Function File" },
   { PCCC_FILE_TYPE_FUNCTION_LCD,        "Built-in LCD Function File" },
   { PCCC_FILE_TYPE_FUNCTION_PTOX,       "PTOX Function File" },
   { PCCC_FILE_TYPE_FUNCTION_PWMX,       "PWMX Function File" },

   { 0,                          NULL }
};

static value_string_ext cip_pccc_file_type_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_file_types_vals);

/* Translate PCCC CPU Modes */
#if 0
static const value_string cip_pccc_cpu_mode_3a_vals[] = {
   { PCCC_CPU_3A_PROGRAM,           "Remote Program" },
   { PCCC_CPU_3A_RUN,               "Remote Run" },

   { 0,                          NULL }
};

value_string_ext cip_pccc_cpu_mode_3a_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_cpu_mode_3a_vals);
#endif

static const value_string cip_pccc_cpu_mode_80_vals[] = {
   { PCCC_CPU_80_PROGRAM,           "Remote Program" },
   { PCCC_CPU_80_RUN,               "Remote Run" },
   { PCCC_CPU_80_TEST_CONT,         "Remote Test Continuous" },
   { PCCC_CPU_80_TEST_SINGLE,       "Remote Test Single" },
   { PCCC_CPU_80_TEST_DEBUG,        "Remote Test Debug" },

   { 0,                          NULL }
};

static value_string_ext cip_pccc_cpu_mode_80_vals_ext = VALUE_STRING_EXT_INIT(cip_pccc_cpu_mode_80_vals);

/* Translate Vendor IDs */
static const value_string cip_vendor_vals[] = {
   {    1,   "Rockwell Automation/Allen-Bradley" },
   {    2,   "Namco Controls Corp." },
   {    3,   "Honeywell International Inc." },
   {    4,   "Parker Hannifin Corporation" },
   {    5,   "Rockwell Automation/Reliance Elec." },
   {    6,   "Reserved" },
   {    7,   "SMC Corporation" },
   {    8,   "Molex Incorporated" },
   {    9,   "Western Reserve Controls Inc." },
   {   10,   "Advanced Micro Controls Inc. (AMCI)" },
   {   11,   "ASCO Pneumatic Controls" },
   {   12,   "Banner Engineering Corporation" },
   {   13,   "Belden Wire & Cable Company" },
   {   14,   "Cooper Interconnect" },
   {   16,   "Daniel Woodhead Co." },
   {   17,   "Dearborn Group Inc." },
   {   18,   "Reserved" },
   {   19,   "Helm Instrument Company" },
   {   20,   "Huron Net Works" },
   {   21,   "Belden Deutschland GmbH" },
   {   22,   "Online Development, Inc. (OLDI)" },
   {   23,   "Vorne Industries, Inc." },
   {   24,   "ODVA" },
   {   25,   "Reserved" },
   {   26,   "Festo" },
   {   27,   "Reserved" },
   {   28,   "Reserved" },
   {   30,   "Unico, Inc." },
   {   31,   "Ross Controls" },
   {   33,   "Reserved" },
   {   34,   "Hohner Corp." },
   {   35,   "Micro Mo Electronics, Inc." },
   {   36,   "MKS Instruments, Inc." },
   {   37,   "Yaskawa Electric America formerly Magnetek Drives" },
   {   38,   "Reserved" },
   {   39,   "AVG Automation (Uticor)" },
   {   40,   "WAGO Corporation" },
   {   41,   "Celerity, Inc." },
   {   42,   "IMI Norgren Limited" },
   {   43,   "BALLUFF" },
   {   44,   "Yaskawa America, Inc." },
   {   45,   "Eurotherm by Schneider Electric" },
   {   46,   "ABB, Inc." },
   {   47,   "Omron Corporation" },
   {   48,   "TURCK" },
   {   49,   "Grayhill Inc." },
   {   50,   "Real Time Automation" },
   {   51,   "Reserved" },
   {   52,   "ASCO Numatics" },
   {   53,   "LÜTZE" },
   {   54,   "Reserved" },
   {   55,   "Reserved" },
   {   56,   "Softing" },
   {   57,   "Pepperl+Fuchs" },
   {   58,   "Spectrum Controls, Inc." },
   {   59,   "D.I.P. Inc. MKS Inst." },
   {   60,   "Applied Motion Products, Inc." },
   {   61,   "Sencon Inc." },
   {   62,   "High Country Tek" },
   {   63,   "SWAC Automation Consult GmbH" },
   {   64,   "Clippard Instrument Laboratory" },
   {   65,   "Reserved" },
   {   66,   "Reserved" },
   {   67,   "Reserved" },
   {   68,   "Eaton Electrical" },
   {   69,   "Reserved" },
   {   70,   "Reserved" },
   {   71,   "Toshiba Corporation" },
   {   72,   "Control Technology Incorporated" },
   {   73,   "TCS (NZ) Ltd." },
   {   74,   "Hitachi, Ltd." },
   {   75,   "ABB Robotics" },
   {   76,   "NKE Corporation" },
   {   77,   "Rockwell Software, Inc." },
   {   78,   "Escort Memory Systems (A Datalogic Group Co.)" },
   {   79,   "Leviton" },
   {   80,   "Industrial Devices Corporation" },
   {   81,   "IXXAT Automation GmbH" },
   {   82,   "Mitsubishi Electric Automation, Inc." },
   {   83,   "OPTO 22" },
   {   84,   "Reserved" },
   {   85,   "Reserved" },
   {   86,   "Horner Electric" },
   {   87,   "Buerkert Fluid Control Systems" },
   {   88,   "Reserved" },
   {   89,   "Industrial Indexing Systems, Inc." },
   {   90,   "HMS Networks" },
   {   91,   "Robicon" },
   {   92,   "Helix Technology (Granville-Phillips)" },
   {   93,   "Arlington Laboratory" },
   {   94,   "Advantech Corporation" },
   {   95,   "Square D Company" },
   {   96,   "Digital Electronics Corp." },
   {   97,   "Danfoss Drives A/S" },
   {   98,   "Reserved" },
   {   99,   "Reserved" },
   {  100,   "AVENTICS" },
   {  101,   "Applied Materials, Inc." },
   {  102,   "SWCC Showa Cable Systems Co., Ltd." },
   {  103,   "Pacific Scientific (API Controls Inc.)" },
   {  104,   "Sharp Manufacturing Systems Corporation" },
   {  105,   "Olflex Wire & Cable, Inc." },
   {  106,   "Reserved" },
   {  107,   "Unitrode" },
   {  108,   "Beckhoff Automation" },
   {  109,   "National Instruments" },
   {  110,   "Mykrolis Corporation (Millipore)" },
   {  111,   "International Motion Controls Corp." },
   {  112,   "Reserved" },
   {  113,   "SEG Kempen GmbH" },
   {  114,   "Reserved" },
   {  115,   "Reserved" },
   {  116,   "Temposonics, LLC" },
   {  117,   "Krones, Inc" },
   {  119,   "EXOR Electronic R & D" },
   {  120,   "SIEI S.p.A." },
   {  121,   "KUKA Deutschland GmbH" },
   {  122,   "Reserved" },
   {  123,   "SEC (Samsung Electronics Co., Ltd)" },
   {  124,   "Binary Electronics Ltd" },
   {  125,   "Flexible Machine Controls" },
   {  126,   "Reserved" },
   {  127,   "ABB Inc. (Entrelec)" },
   {  128,   "MAC Valves, Inc." },
   {  129,   "AUMA Riester GmbH & Co. KG" },
   {  130,   "JTEKT Corporation" },
   {  131,   "Reserved" },
   {  132,   "Reserved" },
   {  133,   "Balogh T.A.G., Corporation" },
   {  134,   "TR Electronic" },
   {  135,   "UNIPULSE Corporation" },
   {  136,   "Reserved" },
   {  137,   "Reserved" },
   {  138,   "Conxall Corporation Inc." },
   {  139,   "Reserved" },
   {  140,   "Reserved" },
   {  141,   "Kuramo Electric Co., Ltd." },
   {  142,   "Creative Micro Designs" },
   {  143,   "GE Industrial Systems" },
   {  144,   "Leybold GmbH" },
   {  145,   "Siemens Energy & Automation/Drives" },
   {  146,   "Kodensha Ltd" },
   {  147,   "Motion Engineering, Inc." },
   {  148,   "Honda Engineering Co., Ltd" },
   {  149,   "EIM Valve Controls" },
   {  150,   "Melec Inc." },
   {  151,   "Sony Manufacturing Systems Corporation" },
   {  152,   "North American Mfg." },
   {  153,   "Watlow" },
   {  154,   "Japan Radio Co., Ltd" },
   {  155,   "NADEX Co., Ltd" },
   {  156,   "Ametek Automation & Process Technologies" },
   {  157,   "Facts, Inc." },
   {  158,   "KVASER AB" },
   {  159,   "IDEC Corporation" },
   {  160,   "Mitsubishi Heavy Industries Ltd" },
   {  161,   "Mitsubishi Electric Corporation" },
   {  162,   "HORIBA STEC, Co., Ltd." },
   {  163,   "esd electronics gmbh" },
   {  164,   "DAIHEN Corporation" },
   {  165,   "Tyco Valves & Controls/Keystone" },
   {  166,   "EBARA Corporation" },
   {  168,   "Reserved" },
   {  169,   "Hokuyo Automatic Co., Ltd." },
   {  170,   "Pyramid Solutions, Inc." },
   {  171,   "Denso Wave Incorporated" },
   {  172,   "HLS Hard-Line Solutions Inc" },
   {  173,   "Caterpillar, Inc." },
   {  174,   "PDL Electronics Ltd." },
   {  175,   "Reserved" },
   {  176,   "Red Lion" },
   {  177,   "CANON ANELVA Corporation" },
   {  178,   "Toyo Denki Seizo KK" },
   {  179,   "Sanyo Denki Co., Ltd." },
   {  180,   "Hitachi Metals, Ltd. (formerly Advanced Energy Japan K.K.)" },
   {  181,   "Pilz GmbH & Co KG" },
   {  182,   "Marsh Bellofram-Bellofram PCD Division" },
   {  183,   "Reserved" },
   {  184,   "M-SYSTEM Co., Ltd." },
   {  185,   "Nissin Electric Co., Ltd." },
   {  186,   "Hitachi Metals Ltd." },
   {  187,   "Oriental Motor Co., Ltd" },
   {  188,   "A&D Company Limited" },
   {  189,   "Phasetronics, Inc." },
   {  190,   "Cummins Engine Company" },
   {  191,   "Deltron Inc." },
   {  192,   "Geneer Corporation" },
   {  193,   "Anatol Automation, Inc." },
   {  194,   "Reserved" },
   {  195,   "Reserved" },
   {  196,   "Medar, Inc." },
   {  197,   "XP Power LLC" },
   {  198,   "Advanced Energy Industries, Inc." },
   {  199,   "Reserved" },
   {  200,   "DAIDEN Co., Ltd" },
   {  201,   "CKD Corporation" },
   {  202,   "Toyo Electric Corporation" },
   {  203,   "Reserved" },
   {  204,   "AuCom Electronics Ltd" },
   {  205,   "Sinfonia Technology Co., Ltd." },
   {  206,   "Vector Informatik GmbH" },
   {  207,   "Reserved" },
   {  208,   "Moog Inc." },
   {  209,   "Contemporary Controls" },
   {  210,   "Tokyo Sokki Kenkyujo Co., Ltd" },
   {  211,   "Schenck Process Group" },
   {  212,   "The Oilgear Company" },
   {  213,   "Reserved" },
   {  214,   "ASM Japan K.K." },
   {  215,   "HIRATA Corp." },
   {  216,   "Panasonic Industrial Devices SUNX Co., Ltd." },
   {  217,   "Meidensha Corporation" },
   {  218,   "NIDEC SANKYO CORPORATION (Sankyo Seiki Mfg. Co., Ltd)" },
   {  219,   "KAMRO Corp." },
   {  220,   "NSD Co., Ltd." },
   {  221,   "EBARA Technologies Inc." },
   {  222,   "Reserved" },
   {  223,   "Reserved" },
   {  224,   "SG Co., Ltd" },
   {  225,   "Vaasa Institute of Technology" },
   {  226,   "MKS Instruments (ENI Technology)" },
   {  227,   "Tateyama System Laboratory Co., Ltd." },
   {  228,   "QLOG Corporation" },
   {  229,   "Matric Limited Inc." },
   {  230,   "NSD Corporation" },
   {  231,   "Reserved" },
   {  232,   "Sumitomo Wiring Systems, Ltd" },
   {  233,   "Group 3 Technology Ltd" },
   {  234,   "CTI Cryogenics" },
   {  235,   "POLSYS CORP" },
   {  236,   "Ampere Inc." },
   {  237,   "Reserved" },
   {  238,   "Simplatroll Ltd" },
   {  239,   "Reserved" },
   {  240,   "Reserved" },
   {  241,   "Leading Edge Design" },
   {  242,   "Humphrey Products" },
   {  243,   "Schneider Electric" },
   {  244,   "Westlock Controls Corp." },
   {  245,   "Nihon Weidmuller Co., Ltd" },
   {  246,   "Brooks Instrument" },
   {  247,   "Reserved" },
   {  248,   "Eaton Industries GmbH (formerly Moeller GmbH)" },
   {  249,   "Varian Vacuum Products" },
   {  250,   "Yokogawa Electric Corporation" },
   {  251,   "Electrical Design Daiyu Co., Ltd" },
   {  252,   "Omron Software Co., Ltd." },
   {  253,   "EDWARDS" },
   {  254,   "Control Technology Corporation" },
   {  255,   "Bosch Rexroth" },
   {  256,   "TURCK (formerly InterlinkBT)" },
   {  257,   "Control Techniques PLC" },
   {  258,   "Hardy Process Solutions" },
   {  259,   "LS ELECTRIC" },
   {  260,   "E.O.A. Systems Inc." },
   {  261,   "Reserved" },
   {  262,   "New Cosmos Electric Co., Ltd." },
   {  263,   "Sense Sense Eletronica LTDA" },
   {  264,   "Xycom, Inc." },
   {  265,   "Baldor Electric" },
   {  266,   "Reserved" },
   {  267,   "Patlite Corporation" },
   {  268,   "Reserved" },
   {  269,   "Mogami Wire & Cable Corporation" },
   {  270,   "Welding Technology Corporation" },
   {  271,   "Reserved" },
   {  272,   "Deutschmann Automation GmbH" },
   {  273,   "ICP Panel-Tec Inc." },
   {  274,   "Bray International, Inc" },
   {  275,   "Reserved" },
   {  276,   "Status Technologies" },
   {  277,   "Trio Motion Technology ltd" },
   {  278,   "Sherrex Systems Ltd" },
   {  279,   "Adept Technology, Inc." },
   {  280,   "Spang Power Electronics" },
   {  281,   "Reserved" },
   {  282,   "Acrosser Technology Co., Ltd" },
   {  283,   "Hilscher GmbH" },
   {  284,   "IMAX Corporation" },
   {  285,   "Electronic Innovation, Inc. (Falter Engineering)" },
   {  286,   "Netlogic Inc." },
   {  287,   "Bosch Rexroth AG" },
   {  288,   "Reserved" },
   {  289,   "Reserved" },
   {  290,   "Murata  Machinery Ltd." },
   {  291,   "MTT Company Ltd." },
   {  292,   "Kanematsu Semiconductor Corp." },
   {  293,   "Takebishi Electric Sales Co." },
   {  294,   "Tokyo Electron Device Ltd" },
   {  295,   "PFU Limited" },
   {  296,   "Hakko Automation Co., Ltd." },
   {  297,   "Advanet Inc." },
   {  298,   "Tokyo Electron Software Technologies Ltd." },
   {  299,   "Reserved" },
   {  300,   "Shinagawa Electric Wire Co., Ltd." },
   {  301,   "Yokogawa M&C Corporation" },
   {  302,   "KONAN Electric Co., Ltd." },
   {  303,   "Binar Elektronik AB" },
   {  304,   "Furukawa Electric Co." },
   {  305,   "Cooper Energy Services" },
   {  306,   "Schleicher GmbH & Co." },
   {  307,   "Hirose Electric Co., Ltd" },
   {  308,   "Western Servo Design Inc." },
   {  309,   "ProSoft Technology" },
   {  310,   "Reserved" },
   {  311,   "Towa Shoko Co., Ltd" },
   {  312,   "Kyopal Co., Ltd" },
   {  313,   "Extron Co." },
   {  314,   "Wieland Electric GmbH" },
   {  315,   "SEW Eurodrive GmbH" },
   {  316,   "Aera Corporation" },
   {  317,   "STA Reutlingen" },
   {  318,   "Reserved" },
   {  319,   "Fuji Electric Group" },
   {  320,   "Reserved" },
   {  321,   "Reserved" },
   {  322,   "ifm electronic gmbh" },
   {  323,   "Reserved" },
   {  324,   "IDEACOD-Hohner Automation S.A." },
   {  325,   "CommScope Inc." },
   {  326,   "Intelligent Platforms, LLC." },
   {  327,   "Matsushita Electric Industrial Co., Ltd" },
   {  328,   "Okaya Electronics Corporation" },
   {  329,   "KASHIYAMA Industries, Ltd" },
   {  330,   "JVC" },
   {  331,   "Interface Corporation" },
   {  332,   "Grape Systems Inc." },
   {  333,   "Reserved" },
   {  334,   "Reserved" },
   {  335,   "Toshiba IT & Control Systems Corporation" },
   {  336,   "Sanyo Machine Works, Ltd." },
   {  337,   "Vansco Electronics Ltd." },
   {  338,   "Dart Container Corp." },
   {  339,   "Livingston & Co., Inc." },
   {  340,   "Alfa Laval LKM as" },
   {  341,   "BF ENTRON Ltd. (British Federal)" },
   {  342,   "Bekaert Engineering NV" },
   {  343,   "Ferran  Scientific Inc." },
   {  344,   "KEBA Industrial Automation GmbH" },
   {  345,   "Endress + Hauser" },
   {  346,   "Lincoln Electric Company" },
   {  347,   "ABB ALSTOM Power UK Ltd. (EGT)" },
   {  348,   "Berger Lahr GmbH" },
   {  349,   "Reserved" },
   {  350,   "Federal Signal Corp." },
   {  351,   "Kawasaki Robotics (USA), Inc." },
   {  352,   "Bently Nevada Corporation" },
   {  354,   "FRABA Posital" },
   {  355,   "Elsag Bailey, Inc." },
   {  356,   "FANUC Robotics America" },
   {  357,   "Reserved" },
   {  358,   "Surface Combustion, Inc." },
   {  359,   "Reserved" },
   {  360,   "AILES Electronics Ind. Co., Ltd." },
   {  361,   "Wonderware Corporation" },
   {  362,   "Particle Measuring Systems, Inc." },
   {  363,   "Reserved" },
   {  364,   "Reserved" },
   {  365,   "BITS Co., Ltd" },
   {  366,   "Japan Aviation Electronics Industry Ltd" },
   {  367,   "Keyence Corporation" },
   {  368,   "Kuroda Precision Industries Ltd." },
   {  369,   "Mitsubishi Electric Semiconductor Application" },
   {  370,   "Nippon Seisen Cable, Ltd." },
   {  371,   "Omron ASO Co., Ltd" },
   {  372,   "Seiko Seiki Co., Ltd." },
   {  373,   "Sumitomo Heavy Industries, Ltd." },
   {  374,   "Tango Computer Service Corporation" },
   {  375,   "Technology Service, Inc." },
   {  376,   "Toshiba Information Systems (Japan) Corporation" },
   {  377,   "TOSHIBA Schneider Inverter Corporation" },
   {  378,   "Toyooki Kogyo Co., Ltd." },
   {  379,   "XEBEC" },
   {  380,   "Madison Cable Corporation" },
   {  381,   "Hitati Engineering & Services Co., Ltd" },
   {  382,   "TEM-TECH Lab Co., Ltd" },
   {  383,   "International Laboratory Corporation" },
   {  384,   "Dyadic Systems Co., Ltd." },
   {  385,   "SETO Electronics Industry Co., Ltd" },
   {  386,   "Tokyo Electron Kyushu Limited" },
   {  387,   "KEI System Co., Ltd" },
   {  388,   "Reserved" },
   {  389,   "Asahi Engineering Co., Ltd" },
   {  390,   "Contrex Inc." },
   {  391,   "Paradigm Controls Ltd." },
   {  392,   "Reserved" },
   {  393,   "Ohm Electric Co., Ltd." },
   {  394,   "RKC Instrument Inc." },
   {  395,   "Suzuki Motor Corporation" },
   {  396,   "Custom Servo Motors Inc." },
   {  397,   "PACE Control Systems" },
   {  398,   "Selectron Systems AG" },
   {  399,   "Reserved" },
   {  400,   "LINTEC Co., Ltd." },
   {  401,   "Hitachi Cable Ltd." },
   {  402,   "BUSWARE Direct" },
   {  403,   "Eaton Electric B.V. (former Holec Holland N.V.)" },
   {  404,   "VAT Vacuum Valves AG" },
   {  405,   "Omron Robotics and Safety Technologies, Inc." },
   {  406,   "Alfa Instrumentos Eletronicos Ltda" },
   {  407,   "TWK Elektronik GmbH" },
   {  408,   "ABB Welding Systems AB" },
   {  409,   "BYSTRONIC Maschinen AG" },
   {  410,   "Kimura Electric Co., Ltd" },
   {  411,   "Nissei Plastic Industrial Co., Ltd" },
   {  412,   "Reserved" },
   {  413,   "Kistler-Morse" },
   {  414,   "Proteus Industries Inc." },
   {  415,   "IDC Corporation" },
   {  416,   "Nordson Corporation" },
   {  417,   "Rapistan Systems" },
   {  418,   "LP-Elektronik GmbH" },
   {  419,   "GERBI & FASE S.p.A.(Fase Saldatura)" },
   {  420,   "Phoenix Digital Corporation" },
   {  421,   "Z-World Engineering" },
   {  422,   "Honda R&D Co., Ltd." },
   {  423,   "Bionics Instrument Co., Ltd." },
   {  424,   "Teknic, Incorporated" },
   {  425,   "R.Stahl, Inc." },
   {  426,   "Reserved" },
   {  427,   "Ryco Graphic Manufacturing Inc." },
   {  428,   "Giddings & Lewis, Inc." },
   {  429,   "Koganei Corporation" },
   {  430,   "Reserved" },
   {  431,   "Nichigoh Communication Electric Wire Co., Ltd." },
   {  433,   "Fujikura Ltd." },
   {  434,   "AD Link Technology Inc." },
   {  435,   "Valmet Flow Control Inc (formerly StoneL)" },
   {  436,   "Computer Optical Products, Inc." },
   {  437,   "CONOS Inc." },
   {  438,   "Erhardt+Leimer GmbH" },
   {  439,   "UNIQUE Co. Ltd" },
   {  440,   "Roboticsware, Inc." },
   {  441,   "Nachi Fujikoshi Corporation" },
   {  442,   "Hengstler GmbH" },
   {  443,   "Vacon Plc" },
   {  444,   "SUNNY GIKEN Inc." },
   {  445,   "Lenze Drive Systems GmbH" },
   {  446,   "CD Systems B.V." },
   {  447,   "FMT/Aircraft Gate Support Systems AB" },
   {  448,   "Axiomatic Technologies Corp" },
   {  449,   "Embedded System Products, Inc." },
   {  450,   "Reserved" },
   {  451,   "Mencom Corporation" },
   {  452,   "Kollmorgen" },
   {  453,   "Panasonic Connect Co., Ltd." },
   {  454,   "Dengensha Mfg. Co. Ltd." },
   {  455,   "Quinn Systems Ltd." },
   {  456,   "Tellima Technology Ltd" },
   {  457,   "MDT, Software" },
   {  458,   "Taiwan Keiso Co., Ltd" },
   {  459,   "Pinnacle Systems" },
   {  460,   "Ascom Hasler Mailing Sys" },
   {  461,   "INSTRUMAR Limited" },
   {  462,   "Reserved" },
   {  463,   "Navistar International Transportation Corp" },
   {  464,   "TRUMPF Huettinger" },
   {  465,   "OCM Technology Inc." },
   {  466,   "Professional Supply Inc." },
   {  467,   "Control Solutions" },
   {  468,   "Baumer IVO GmbH & Co. KG" },
   {  469,   "Worcester Controls Corporation" },
   {  470,   "Pyramid Technical Consultants, Inc." },
   {  471,   "Eilersen Electric A/S" },
   {  472,   "Apollo Fire Detectors Limited" },
   {  473,   "Avtron Manufacturing, Inc." },
   {  474,   "Reserved" },
   {  475,   "Tokyo Keiso Co., Ltd." },
   {  476,   "Daishowa Swiki Co., Ltd." },
   {  477,   "Kojima Instruments Inc." },
   {  478,   "Shimadzu Corporation" },
   {  479,   "Tatsuta Electric Wire & Cable Co., Ltd." },
   {  480,   "MECS Corporation" },
   {  481,   "Tahara Electric" },
   {  482,   "JTEKT ELECTRONICS CORPORATION" },
   {  483,   "Clever Devices" },
   {  484,   "GCD Hardware & Software GmbH" },
   {  485,   "Reserved" },
   {  486,   "Miller Electric Mfg Co." },
   {  487,   "GEA Tuchenhagen GmbH" },
   {  488,   "Riken Keiki Co., Ltd." },
   {  489,   "Keisokugiken Corporation" },
   {  490,   "Fuji Machine Mfg. Co., Ltd" },
   {  491,   "Reserved" },
   {  492,   "Nidec-Shimpo Corp." },
   {  493,   "UTEC Corporation" },
   {  494,   "Sanyo Electric Co. Ltd." },
   {  495,   "Reserved" },
   {  496,   "Reserved" },
   {  497,   "Okano Electric Wire Co. Ltd" },
   {  498,   "Shimaden Co. Ltd." },
   {  499,   "Teddington Controls Ltd" },
   {  500,   "Reserved" },
   {  501,   "YASKAWA Europe (formerly VIPA GmbH)" },
   {  502,   "Warwick Manufacturing Group" },
   {  503,   "Danaher Controls" },
   {  504,   "Reserved" },
   {  505,   "Reserved" },
   {  506,   "American Science & Engineering" },
   {  507,   "Accutron Controls International Inc." },
   {  508,   "Norcott Technologies Ltd" },
   {  509,   "TB Woods, Inc" },
   {  510,   "Proportion-Air, Inc." },
   {  511,   "SICK Stegmann GmbH" },
   {  512,   "Reserved" },
   {  513,   "Edwards Signaling" },
   {  514,   "Sumitomo Metal Industries, Ltd" },
   {  515,   "Cosmo Instruments Co., Ltd." },
   {  516,   "Denshosha Co., Ltd." },
   {  517,   "Kaijo Corp." },
   {  518,   "Michiproducts Co., Ltd." },
   {  519,   "Miura Corporation" },
   {  520,   "TG Information Network Co., Ltd." },
   {  521,   "Fujikin, Inc." },
   {  522,   "Estic Corp." },
   {  523,   "GS Hydraulic Sales" },
   {  524,   "Leuze Electronic GmbH & Co. KG" },
   {  525,   "MTE Limited" },
   {  526,   "Hyde Park Electronics, Inc." },
   {  527,   "Pfeiffer Vacuum GmbH" },
   {  528,   "Cyberlogic Technologies" },
   {  529,   "OKUMA Corporation FA Systems Division" },
   {  530,   "Reserved" },
   {  531,   "Kokusai Electric Corporation" },
   {  532,   "SHINKO TECHNOS" },
   {  533,   "Itoh Denki Co., Ltd." },
   {  534,   "Colorado Flow Tech Inc." },
   {  535,   "Love Controls Division/Dwyer Inst." },
   {  536,   "Alstom Drives and Controls" },
   {  537,   "The Foxboro Company" },
   {  538,   "Tescom Corporation" },
   {  539,   "Reserved" },
   {  540,   "Atlas Copco Airpower NV" },
   {  541,   "Reserved" },
   {  542,   "Autojet Technologies" },
   {  543,   "Prima Electronics S.p.A." },
   {  544,   "PMA GmbH" },
   {  545,   "Shimafuji Electric Co., Ltd" },
   {  546,   "Oki Electric Industry Co., Ltd" },
   {  547,   "Kyushu Matsushita Electric Co., Ltd" },
   {  548,   "JMACS" },
   {  549,   "Tsuken Electric Ind Co., Ltd" },
   {  550,   "Tamadic Co." },
   {  551,   "MAATEL SA" },
   {  552,   "OKUMA America" },
   {  553,   "Control Techniques PLC-NA" },
   {  554,   "TPC Wire & Cable" },
   {  555,   "ATI Industrial Automation" },
   {  556,   "Microcontrol (Australia) Pty Ltd" },
   {  557,   "Serra Soldadura, S.A." },
   {  558,   "Southwest Research Institute" },
   {  559,   "Cabinplant International" },
   {  560,   "Minebea Intec" },
   {  561,   "Comau S.p.A." },
   {  562,   "Phoenix Contact" },
   {  563,   "Yokogawa MAT Corporation" },
   {  564,   "asahi sangyo co., ltd." },
   {  565,   "Valcom" },
   {  566,   "Akita Myotoku Ltd." },
   {  567,   "OBARA Corp." },
   {  568,   "Suetron Electronic GmbH" },
   {  569,   "Reserved" },
   {  570,   "Serck Controls Limited" },
   {  571,   "Fairchild Industrial Products Company" },
   {  572,   "ARO Welding Technologies S.A.S." },
   {  573,   "M2C GmbH" },
   {  574,   "Shin Caterpillar Mitsubishi Ltd." },
   {  575,   "Santest Co., Ltd." },
   {  576,   "Cosmotechs Co., Ltd." },
   {  577,   "Hitachi Electric Systems" },
   {  578,   "Smartscan Ltd" },
   {  579,   "Woodhead Software & Electronics France" },
   {  580,   "Athena Controls, Inc." },
   {  581,   "Norgren Automation Solutions, LLC (previously Syron Engineering & Manufacturing, Inc.)" },
   {  582,   "Asahi Optical Co., Ltd." },
   {  583,   "Sansha Electric Mfg. Co.,Ltd." },
   {  584,   "CKD Nikki Denso Co,. Ltd." },
   {  585,   "Star Micronics, Co., Ltd." },
   {  586,   "Ecotecnia Socirtat Corp." },
   {  587,   "Lenze" },
   {  588,   "West Instruments Limited" },
   {  589,   "LinMot" },
   {  590,   "Delta Computer Systems, Inc." },
   {  591,   "FANUC CORPORATION" },
   {  592,   "Hearn-Gu Lee" },
   {  593,   "ABB AG" },
   {  594,   "Orion Machinery Co., Ltd." },
   {  595,   "Reserved" },
   {  596,   "Wire-Pro, Inc." },
   {  597,   "Beijing Huakong Technology Co. Ltd." },
   {  598,   "Yokoyama Shokai Co., Ltd." },
   {  599,   "Toyogiken Co., Ltd." },
   {  600,   "Coester Equipamentos Eletronicos Ltda." },
   {  601,   "Kawasaki Robot" },
   {  602,   "Electroplating Engineers of Japan Ltd." },
   {  603,   "Robox S.p.a." },
   {  604,   "Spraying Systems Company" },
   {  605,   "Benshaw Inc." },
   {  606,   "ZPA-DP A.S." },
   {  607,   "Wired Rite Systems" },
   {  608,   "Tandis Research, Inc." },
   {  609,   "SSD Drives GmbH" },
   {  610,   "ULVAC Japan Ltd." },
   {  611,   "DYNAX Corporation" },
   {  612,   "Nor-Cal Products, Inc." },
   {  613,   "Aros Electronics AB" },
   {  614,   "Jun-Tech Co., Ltd." },
   {  615,   "HAN-MI Co. Ltd." },
   {  616,   "uniNtech (formerly SungGi Internet)" },
   {  617,   "Hae Pyung Electronics Reserch Institute" },
   {  618,   "Milwaukee Electronics" },
   {  619,   "OBERG Industries" },
   {  620,   "Parker Hannifin/Compumotor Division" },
   {  621,   "TECHNO DIGITAL CORPORATION" },
   {  622,   "Network Supply Co., Ltd." },
   {  623,   "Union Electronics Co., Ltd." },
   {  624,   "Tritronics Services PM Ltd." },
   {  625,   "Rockwell Automation/Sprecher+Schuh" },
   {  626,   "Panasonic Corporation/Motor Company" },
   {  627,   "Rolls-Royce Energy Systems, Inc." },
   {  628,   "JEONGIL INTERCOM CO., LTD" },
   {  629,   "Interroll Software & Electronics GmbH" },
   {  630,   "Hubbell Wiring Device-Kellems (Delaware)" },
   {  631,   "Intelligent Motion Systems" },
   {  632,   "Reserved" },
   {  633,   "INFICON AG" },
   {  634,   "Hirschmann, a Belden brand" },
   {  635,   "The Siemon Company" },
   {  636,   "YAMAHA Motor Co. Ltd." },
   {  637,   "aska corporation" },
   {  638,   "Woodhead Connectivity" },
   {  639,   "Trimble AB" },
   {  640,   "Murrelektronik GmbH" },
   {  641,   "Creatrix Labs, Inc." },
   {  642,   "TopWorx" },
   {  643,   "Kumho Industrial Co., Ltd." },
   {  644,   "Wind River Systems, Inc." },
   {  645,   "Bihl + Wiedemann GmbH" },
   {  646,   "Harmonic Drive Systems Inc." },
   {  647,   "Rikei Corporation" },
   {  648,   "BL Autotec, Ltd." },
   {  649,   "Hana Information & Technology Co., Ltd." },
   {  650,   "Seoil Electric Co., Ltd." },
   {  651,   "Fife Corporation" },
   {  652,   "Shanghai Electrical Apparatus Research Institute" },
   {  653,   "Detector Electronics" },
   {  654,   "Parasense Development Centre" },
   {  655,   "Reserved" },
   {  656,   "Reserved" },
   {  657,   "Six Tau S.p.A." },
   {  658,   "Aucos GmbH" },
   {  659,   "Rotork Controls Ltd." },
   {  660,   "Automationdirect.com" },
   {  661,   "Thermo BLH" },
   {  662,   "System Controls, Ltd." },
   {  663,   "Univer S.p.A." },
   {  664,   "MKS-Tenta Technology" },
   {  665,   "Lika Electronic" },
   {  666,   "Mettler-Toledo" },
   {  667,   "DXL USA Inc." },
   {  668,   "Rockwell Automation/Entek IRD Intl." },
   {  669,   "Nippon Otis Elevator Company" },
   {  670,   "Sinano Electric, Co., Ltd." },
   {  671,   "Sony Manufacturing Systems" },
   {  672,   "Reserved" },
   {  673,   "CONTEC CO., LTD." },
   {  674,   "Automated Solutions" },
   {  675,   "Controlweigh" },
   {  677,   "Fincor Electronics" },
   {  678,   "Cognex Corporation" },
   {  679,   "Qualiflow" },
   {  680,   "Weidmuller, Inc." },
   {  681,   "Morinaga Milk Industry Co., Ltd." },
   {  682,   "Takagi Industrial Co., Ltd." },
   {  683,   "Wittenstein SE" },
   {  684,   "Sena Technologies, Inc." },
   {  685,   "Reserved" },
   {  686,   "SPX Flow Technology Germany GmbH" },
   {  687,   "Creator Teknisk Utvedkling AB" },
   {  688,   "Reserved" },
   {  689,   "Mibu Denki Industrial Co., Ltd." },
   {  690,   "Takamastsu Machineer Section" },
   {  691,   "Littelfuse" },
   {  692,   "Reserved" },
   {  693,   "Holjeron" },
   {  694,   "Pfeiffer Vacuum SAS" },
   {  695,   "Taesan LCD Co., Ltd." },
   {  696,   "POSCON" },
   {  697,   "VMIC" },
   {  698,   "Matsushita Electric Works, Ltd." },
   {  699,   "IAI Corporation" },
   {  700,   "Horst GmbH" },
   {  701,   "MicroControl GmbH & Co." },
   {  702,   "Leine & Linde AB" },
   {  703,   "Reserved" },
   {  704,   "EC Elettronica Srl" },
   {  705,   "VIT Software HB" },
   {  706,   "Bronkhorst High-Tech B.V." },
   {  707,   "Optex Co., Ltd." },
   {  708,   "Yosio Electronic Co." },
   {  709,   "Terasaki Electric Co., Ltd." },
   {  710,   "Sodick Co., Ltd." },
   {  711,   "MTS Systems Corporation-Automation Division" },
   {  712,   "Mesa Systemtechnik" },
   {  713,   "SHIN HO SYSTEM Co., Ltd." },
   {  714,   "Goyo Electronics Co, Ltd." },
   {  715,   "Loreme" },
   {  716,   "SAB Brockskes GmbH & Co. KG" },
   {  717,   "Trumpf Laser GmbH" },
   {  718,   "Niigata Electronic Instruments Co., Ltd." },
   {  719,   "Yokogawa Digital Computer Corporation" },
   {  720,   "O.N. Electronic Co., Ltd." },
   {  721,   "Industrial Control Communication, Inc." },
   {  722,   "ABB, Inc." },
   {  723,   "ElectroWave USA, Inc." },
   {  724,   "Industrial Network Controls, LLC" },
   {  725,   "KDT Systems Co., Ltd." },
   {  726,   "SEFA Technology Inc." },
   {  727,   "Nippon POP Rivets and Fasteners Ltd." },
   {  728,   "Yamato Scale Co., Ltd." },
   {  729,   "Zener Electric" },
   {  730,   "GSE Scale Systems" },
   {  731,   "ISAS (Integrated Switchgear & Sys. Pty Ltd)" },
   {  732,   "Beta LaserMike Limited" },
   {  733,   "TOEI Electric Co., Ltd." },
   {  734,   "Hakko Electronics Co., Ltd" },
   {  735,   "Reserved" },
   {  736,   "RFID, Inc." },
   {  737,   "Adwin Corporation" },
   {  738,   "Osaka Vacuum, Ltd." },
   {  739,   "A-Kyung Motion, Inc." },
   {  740,   "Camozzi Automation spa" },
   {  741,   "Crevis Co., LTD" },
   {  742,   "Rice Lake Weighing Systems" },
   {  743,   "Linux Network Services" },
   {  744,   "KEB Automation KG" },
   {  745,   "Hagiwara Electric Co., Ltd." },
   {  746,   "Glass Inc. International" },
   {  748,   "DVT Corporation" },
   {  749,   "Woodward Governor" },
   {  750,   "Mosaic Systems, Inc." },
   {  751,   "Laserline GmbH" },
   {  752,   "COM-TEC, Inc." },
   {  753,   "Weed Instrument" },
   {  754,   "Prof-face European Technology Center" },
   {  755,   "Fuji Automation Co., Ltd." },
   {  756,   "Matsutame Co., Ltd." },
   {  757,   "Hitachi Via Mechanics, Ltd." },
   {  758,   "Dainippon Screen Mfg. Co. Ltd." },
   {  759,   "FLS Automation A/S" },
   {  760,   "ABB Stotz Kontakt GmbH" },
   {  761,   "Technical Marine Service" },
   {  762,   "Advanced Automation Associates, Inc." },
   {  763,   "Baumer Ident GmbH" },
   {  764,   "Tsubakimoto Chain Co." },
   {  765,   "Reserved" },
   {  766,   "Furukawa Co., Ltd." },
   {  767,   "Active Power" },
   {  768,   "CSIRO Mining Automation" },
   {  769,   "Matrix Integrated Systems" },
   {  770,   "Digitronic Automationsanlagen GmbH" },
   {  771,   "SICK STEGMANN Inc." },
   {  772,   "TAE-Antriebstechnik GmbH" },
   {  773,   "Electronic Solutions" },
   {  774,   "Rocon L.L.C." },
   {  775,   "Dijitized Communications Inc." },
   {  776,   "Asahi Organic Chemicals Industry Co., Ltd." },
   {  777,   "Hodensha" },
   {  778,   "HARTING, Inc. of North America" },
   {  779,   "Kuebler GmbH" },
   {  780,   "Yamatake Corporation" },
   {  781,   "JEOL" },
   {  782,   "Yamatake Industrial Systems Co., Ltd." },
   {  783,   "HAEHNE Elektronische Messgerate GmbH" },
   {  784,   "Ci Technologies Pty Ltd (for Pelamos Industries)" },
   {  785,   "N. SCHLUMBERGER & CIE" },
   {  786,   "Teijin Seiki Co., Ltd." },
   {  787,   "DAIKIN Industries, Ltd" },
   {  788,   "RyuSyo Industrial Co., Ltd." },
   {  789,   "SAGINOMIYA SEISAKUSHO, INC." },
   {  790,   "Seishin Engineering Co., Ltd." },
   {  791,   "Japan Support System Ltd." },
   {  792,   "Decsys" },
   {  793,   "Metronix Messgeraete und Elektronik GmbH" },
   {  794,   "ROPEX Industrie - Elektronik GmbH" },
   {  795,   "Vaccon Company, Inc." },
   {  796,   "Siemens Industry, Inc." },
   {  797,   "Ten X Technology, Inc." },
   {  798,   "TE Connectivity" },
   {  799,   "Delta Electronics, Inc." },
   {  800,   "Denker" },
   {  801,   "Autonics Corporation" },
   {  802,   "JFE Electronic Engineering Pty. Ltd." },
   {  803,   "Reserved" },
   {  804,   "Electro-Sensors, Inc." },
   {  805,   "Digi International, Inc." },
   {  806,   "Texas Instruments" },
   {  807,   "ADTEC Plasma Technology Co., Ltd" },
   {  808,   "SICK AG" },
   {  809,   "Ethernet Peripherals, Inc." },
   {  810,   "Animatics Corporation" },
   {  811,   "Reserved" },
   {  812,   "Process Control Corporation" },
   {  813,   "SystemV. Inc." },
   {  814,   "Danaher Motion SRL" },
   {  815,   "SHINKAWA Sensor Technology, Inc." },
   {  816,   "Tesch GmbH & Co. KG" },
   {  817,   "Reserved" },
   {  818,   "Trend Controls Systems Ltd." },
   {  819,   "Guangzhou ZHIYUAN Electronic Co., Ltd." },
   {  820,   "Mykrolis Corporation" },
   {  821,   "Bethlehem Steel Corporation" },
   {  822,   "KK ICP" },
   {  823,   "Takemoto Denki Corporation" },
   {  824,   "Montalvo Corporation" },
   {  825,   "Reserved" },
   {  826,   "LEONI Special Cables GmbH" },
   {  827,   "Reserved" },
   {  828,   "ONO SOKKI CO.,LTD." },
   {  829,   "RS Automation Co., Ltd." },
   {  830,   "SHINDENGEN ELECTRIC MFG. CO. LTD" },
   {  831,   "Origin Electric Co. Ltd." },
   {  832,   "Quest Technical Solutions, Inc." },
   {  833,   "LS Cable" },
   {  834,   "NORD Electronic DRIVESYSTEMS GmbH" },
   {  835,   "Northwire Inc." },
   {  836,   "Engel Elektroantriebe GmbH" },
   {  837,   "The Stanley Works" },
   {  838,   "Celesco Transducer Products, Inc." },
   {  839,   "Chugoku Electric Wire and Cable Co." },
   {  840,   "Kongsberg Simrad AS" },
   {  841,   "Panduit Corporation" },
   {  842,   "Spellman High Voltage Electronics Corp." },
   {  843,   "Kokusai Electric Alpha Co., Ltd." },
   {  844,   "Brooks Automation, Inc." },
   {  845,   "ANYWIRE CORPORATION" },
   {  846,   "Honda Electronics Co. Ltd" },
   {  847,   "REO AG" },
   {  848,   "Heraeus Noblelight Fusion UV Inc." },
   {  849,   "ASI Advanced Semiconductor Instruments GmbH" },
   {  850,   "Datalogic, Inc." },
   {  851,   "SoftPLC Corporation" },
   {  852,   "Dynisco Instruments LLC" },
   {  853,   "WEG" },
   {  854,   "Teledyne LeCroy (formerly Frontline Test Equipment)" },
   {  855,   "Tamagawa Seiki Co., Ltd." },
   {  856,   "Multi Computing Co., Ltd." },
   {  857,   "RVSI" },
   {  858,   "Commercial Timesharing Inc." },
   {  859,   "Tennessee Rand Automation LLC" },
   {  860,   "Wacogiken Co., Ltd" },
   {  861,   "Reflex Integration Inc." },
   {  862,   "Siemens AG, A&D PI Flow Instruments" },
   {  863,   "G. Bachmann Electronic GmbH" },
   {  864,   "Entegris, Inc." },
   {  865,   "Schweitzer Engineering Laboratories" },
   {  866,   "ATR Industrie-Elektronik GmbH Co." },
   {  867,   "PLASMATECH Co., Ltd" },
   {  868,   "Reserved" },
   {  869,   "GEMUE GmbH & Co. KG" },
   {  870,   "Alcorn McBride Inc." },
   {  871,   "MORI SEIKI CO., LTD" },
   {  872,   "NodeTech Systems Ltd" },
   {  873,   "Emhart Teknologies" },
   {  874,   "Cervis, Inc." },
   {  875,   "MSA Safety" },
   {  876,   "NEDAP Power Supplies" },
   {  877,   "Nippon Sanso Corporation" },
   {  878,   "Mitomi Giken Co., Ltd." },
   {  879,   "PULS GmbH" },
   {  880,   "Reserved" },
   {  881,   "Japan Control Engineering Ltd" },
   {  882,   "Embedded Systems Korea (Former Zues Emtek Co Ltd.)" },
   {  883,   "Automa SRL" },
   {  884,   "Harms+Wende GmbH & Co KG" },
   {  885,   "R. STAHL" },
   {  886,   "Microwave Data Systems" },
   {  887,   "Bernecker + Rainer Industrie-Elektronik GmbH" },
   {  888,   "Hiprom Technologies" },
   {  889,   "Reserved" },
   {  890,   "Nitta Corporation" },
   {  891,   "Kontron Modular Computers GmbH" },
   {  892,   "Marlin Controls" },
   {  893,   "Elcis Encoder s.r.l." },
   {  894,   "Acromag, Inc." },
   {  895,   "Avery Weigh-Tronix" },
   {  896,   "Reserved" },
   {  897,   "Reserved" },
   {  899,   "Practicon Ltd." },
   {  900,   "SCHUNK GmbH & Co. KG" },
   {  901,   "MYNAH Technologies" },
   {  902,   "Defontaine Groupe" },
   {  903,   "Emerson Process Management Power & Water Solutions" },
   {  904,   "F.A. Elec" },
   {  905,   "Hottinger Baldwin Messtechnik GmbH" },
   {  906,   "Teledyne DALSA" },
   {  907,   "London Electronics Ltd." },
   {  908,   "HSD SpA" },
   {  909,   "Pepperl+Fuchs Comtrol" },
   {  910,   "TEAM, S.A. (Tecnica Electronica de Automatismo Y Medida)" },
   {  911,   "Regulateurs Europa Ltd" },
   {  912,   "Reserved" },
   {  913,   "Reserved" },
   {  914,   "Micro Motion" },
   {  915,   "Eckelmann AG" },
   {  916,   "Hanyoung Nux" },
   {  917,   "CFT Ransburg Japan KK" },
   {  918,   "Kun Hung Electric Co. Ltd." },
   {  919,   "Brimos wegbebakening b.v." },
   {  920,   "NITTO SEIKO CO., LTD." },
   {  921,   "Datasensing S.r.l." },
   {  922,   "Yamazaki Machinery Works" },
   {  923,   "Schmidt Technology" },
   {  924,   "Parker Hannifin SpA (SBC Division)" },
   {  925,   "HIMA Paul Hildebrandt GmbH" },
   {  926,   "RivaTek, Inc." },
   {  927,   "Misumi Corporation" },
   {  928,   "GE Multilin" },
   {  929,   "Measurement Computing Corporation" },
   {  930,   "Jetter AG" },
   {  931,   "Tokyo Electronics Systems Corporation" },
   {  932,   "Togami Electric Mfg. Co., Ltd." },
   {  933,   "HK Systems" },
   {  934,   "CDA Systems Ltd." },
   {  935,   "Aerotech Inc." },
   {  936,   "JVL A/S" },
   {  937,   "NovaTech Process Solutions LLC" },
   {  938,   "Reserved" },
   {  939,   "Cisco Systems" },
   {  940,   "Grid Connect" },
   {  941,   "ITW Automotive Finishing" },
   {  942,   "HanYang System" },
   {  943,   "ABB K.K. Technical Center" },
   {  944,   "Taiyo Cable (Dongguan) Co., Ltd." },
   {  945,   "Reserved" },
   {  946,   "SEREN IPS INC" },
   {  947,   "Belden" },
   {  948,   "ControlNet International" },
   {  949,   "Gefran S.P.A." },
   {  950,   "ABB (Jokab Safety)" },
   {  951,   "SUMITA OPTICAL GLASS, INC." },
   {  952,   "Biffi Italia srl" },
   {  953,   "Beck IPC GmbH" },
   {  954,   "Copley Controls" },
   {  955,   "Fagor Automation S. Coop." },
   {  956,   "DARCOM" },
   {  957,   "Frick Controls (div. of York International)" },
   {  958,   "SymCom, Inc." },
   {  959,   "Infranor" },
   {  960,   "Kyosan Electric Mfg" },
   {  961,   "Varian Vacuum Technologies" },
   {  962,   "Messung Systems" },
   {  963,   "Xantrex Technology, Inc." },
   {  964,   "StarThis Inc." },
   {  965,   "NF Chiyoda Electronics Co., Ltd." },
   {  966,   "Flowserve Corporation" },
   {  967,   "Spyder Controls Corp." },
   {  968,   "IBA AG" },
   {  969,   "SHIMOHIRA ELECTRIC MFG.CO.,LTD" },
   {  970,   "Reserved" },
   {  971,   "Siemens L&A" },
   {  972,   "Eaton Automation GmbH (formerly Micro Innovation)" },
   {  973,   "Switchgear & Instrumentation" },
   {  974,   "Pre-Tech Co., Ltd." },
   {  975,   "National Semiconductor" },
   {  976,   "Invensys Operations Management" },
   {  977,   "Ametek HDR Power Systems" },
   {  978,   "Reserved" },
   {  979,   "TETRA-K Corporation" },
   {  980,   "C&M Corporation" },
   {  981,   "Siempelkamp Maschinen" },
   {  982,   "Reserved" },
   {  983,   "Daifuku Co., Ltd" },
   {  984,   "Electro-Matic Products Inc." },
   {  985,   "BUSSAN MICROELECTRONICS CORP." },
   {  986,   "ELAU AG" },
   {  987,   "Hetronic USA" },
   {  988,   "NIIGATA POWER SYSTEMS Co., Ltd." },
   {  989,   "Software Horizons Inc." },
   {  990,   "B3 Systems, Inc." },
   {  991,   "Moxa, Inc." },
   {  992,   "Reserved" },
   {  993,   "S4 Integration" },
   {  994,   "Elettro Stemi S.R.L." },
   {  995,   "AquaSensors" },
   {  996,   "Ifak System GmbH" },
   {  997,   "SANKEI MANUFACTURING Co.,LTD." },
   {  998,   "Emerson Network Power Co., Ltd." },
   {  999,   "Fairmount Automation, Inc." },
   { 1000,   "Bird Electronic Corporation" },
   { 1001,   "Nabtesco Corporation" },
   { 1002,   "AGM Electronics, Inc." },
   { 1003,   "ARCX Inc." },
   { 1004,   "DELTA I/O Co." },
   { 1005,   "Chun IL Electric Ind. Co." },
   { 1006,   "N-Tron Corporation, a Red Lion Company" },
   { 1007,   "Nippon Pneumatics/Fludics System CO.,LTD." },
   { 1008,   "DDK Ltd." },
   { 1009,   "Seiko Epson Corporation" },
   { 1010,   "halstrup-walcher GmbH" },
   { 1011,   "ITT" },
   { 1012,   "Ground Fault Systems bv" },
   { 1013,   "Scolari Engineering S.p.A." },
   { 1014,   "Vialis Traffic bv" },
   { 1015,   "Weidmueller Group" },
   { 1016,   "Shanghai Sibotech Automation Co. Ltd" },
   { 1017,   "AEG Power Solutions GmbH" },
   { 1018,   "Komatsu Electronics Inc." },
   { 1019,   "Souriau" },
   { 1020,   "Baumuller Chicago Corp." },
   { 1021,   "J. Schmalz GmbH" },
   { 1022,   "SEN Corporation" },
   { 1023,   "Korenix Technology Co. Ltd" },
   { 1024,   "Cooper Power Tools" },
   { 1025,   "INNOBIS" },
   { 1026,   "Shinho System" },
   { 1027,   "Xm Services Ltd." },
   { 1028,   "KVC Co., Ltd." },
   { 1029,   "Sanyu Seiki Co., Ltd." },
   { 1030,   "TuxPLC" },
   { 1031,   "Northern Network Solutions" },
   { 1032,   "Converteam GmbH" },
   { 1033,   "Symbol Technologies" },
   { 1034,   "S-TEAM Lab" },
   { 1035,   "Maguire Products, Inc." },
   { 1036,   "AC&T" },
   { 1037,   "MITSUBISHI HEAVY INDUSTRIES, LTD. KOBE SHIPYARD & MACHINERY WORKS" },
   { 1038,   "Hurletron Inc." },
   { 1039,   "Chunichi Denshi Co., Ltd" },
   { 1040,   "Cardinal Scale Mfg. Co." },
   { 1041,   "METZ CONNECT USA Inc." },
   { 1042,   "Base2" },
   { 1043,   "ASRC Aerospace" },
   { 1044,   "Beijing Stone Automation" },
   { 1045,   "Changshu Switchgear Manufacture Ltd." },
   { 1046,   "METRONIX Corp." },
   { 1047,   "WIT" },
   { 1048,   "ORMEC Systems Corp." },
   { 1049,   "ASATech (China) Inc." },
   { 1050,   "Controlled Systems Limited" },
   { 1051,   "Mitsubishi Heavy Ind. Digital System Co., Ltd. (M.H.I.)" },
   { 1052,   "Electrogrip" },
   { 1053,   "TDS Automation" },
   { 1054,   "T&C Power Conversion, Inc." },
   { 1055,   "Robostar Co., Ltd" },
   { 1056,   "Scancon A/S" },
   { 1057,   "Haas Automation, Inc." },
   { 1058,   "Eshed Technology" },
   { 1059,   "Delta Electronic Inc." },
   { 1060,   "Innovasic" },
   { 1061,   "SoftDEL Systems Limited" },
   { 1062,   "FiberFin, Inc." },
   { 1063,   "Nicollet Technologies Corp." },
   { 1064,   "B.F. Systems" },
   { 1065,   "Empire Wire and Supply LLC" },
   { 1066,   "ENDO KOGYO CO., LTD" },
   { 1067,   "Elmo Motion Control LTD" },
   { 1068,   "Reserved" },
   { 1069,   "Asahi Keiki Co., Ltd." },
   { 1070,   "Joy Mining Machinery" },
   { 1071,   "MPM Engineering Ltd." },
   { 1072,   "Wolke Inks & Printers GmbH" },
   { 1073,   "Mitsubishi Electric Engineering Co., Ltd." },
   { 1074,   "COMET AG" },
   { 1075,   "Real Time Objects & Systems, LLC" },
   { 1076,   "MISCO Refractometer" },
   { 1077,   "JT Engineering Inc." },
   { 1078,   "Automated Packing Systems" },
   { 1079,   "Niobrara R&D Corp." },
   { 1080,   "Garmin Ltd." },
   { 1081,   "Japan Mobile Platform Co., Ltd" },
   { 1082,   "Advosol Inc." },
   { 1083,   "ABB Global Services Limited" },
   { 1084,   "Sciemetric Instruments Inc." },
   { 1085,   "Tata Elxsi Ltd." },
   { 1086,   "Mechatronics Co.,Ltd" },
   { 1087,   "Cooper Bussmann" },
   { 1088,   "Trinite Automatisering B.V." },
   { 1089,   "Peek Traffic B.V." },
   { 1090,   "Acrison, Inc." },
   { 1091,   "Applied Robotics, Inc." },
   { 1092,   "FireBus LLC" },
   { 1093,   "Sevenstar" },
   { 1094,   "Magnetek" },
   { 1095,   "Omron Microscan Systems, Inc." },
   { 1096,   "Air Water Inc." },
   { 1097,   "SensoPart Industriesensorik GmbH" },
   { 1098,   "Tiefenbach Control Systems GmbH" },
   { 1099,   "INOXPA S.A" },
   { 1100,   "Zurich University of Applied Sciences" },
   { 1101,   "Ethernet Direct" },
   { 1102,   "GSI-Micro-E Systems" },
   { 1103,   "S-Net Automation Co., Ltd." },
   { 1104,   "Power Electronics S.L." },
   { 1105,   "Renesas Electronics" },
   { 1106,   "NSWCCD-SSES" },
   { 1107,   "Porter Engineering Ltd." },
   { 1108,   "Meggitt Airdynamics, Inc." },
   { 1109,   "Inductive Automation" },
   { 1110,   "Neural ID" },
   { 1111,   "EEPod LLC" },
   { 1112,   "Hitachi Industrial Equipment Systems Co.,Ltd." },
   { 1113,   "Salem Automation" },
   { 1114,   "port GmbH" },
   { 1115,   "B & PLUS" },
   { 1116,   "Graco Inc." },
   { 1117,   "Altera Corporation" },
   { 1118,   "Technology Brewing Corporation" },
   { 1119,   "Reserved" },
   { 1120,   "Reserved" },
   { 1121,   "CSE Servelec" },
   { 1122,   "Reserved" },
   { 1123,   "Reserved" },
   { 1124,   "Fluke Networks" },
   { 1125,   "Tetra Pak Packaging Solutions SpA" },
   { 1126,   "Racine Federated, Inc." },
   { 1127,   "Pureron Japan Co., Ltd." },
   { 1128,   "Reserved" },
   { 1129,   "Reserved" },
   { 1130,   "Brother Industries, Ltd." },
   { 1131,   "Reserved" },
   { 1132,   "Leroy Automation" },
   { 1133,   "Reserved" },
   { 1134,   "THK Co., Ltd." },
   { 1135,   "Reserved" },
   { 1136,   "Reserved" },
   { 1137,   "TR-Electronic GmbH" },
   { 1138,   "ASCON S.p.A." },
   { 1139,   "Toledo do Brasil Industria de Balancas Ltda." },
   { 1140,   "Caterpillar Global Mining Europe GmbH" },
   { 1141,   "Emerson Process Management Valve Automation" },
   { 1142,   "Alstom Transport" },
   { 1143,   "Reserved" },
   { 1144,   "Matrox Electronic Systems" },
   { 1145,   "Littelfuse" },
   { 1146,   "PLASMART, Inc." },
   { 1147,   "Miyachi Corporation" },
   { 1148,   "Reserved" },
   { 1149,   "Reserved" },
   { 1150,   "Promess Incorporated" },
   { 1151,   "COPA-DATA GmbH" },
   { 1152,   "Precision Engine Controls Corporation" },
   { 1153,   "Alga Automacao e controle LTDA" },
   { 1154,   "Lapp Group" },
   { 1155,   "ICES" },
   { 1156,   "Philips Lighting bv" },
   { 1157,   "Aseptomag AG" },
   { 1158,   "ARC Informatique" },
   { 1159,   "Hesmor GmbH" },
   { 1160,   "Kobe Steel, Ltd." },
   { 1161,   "FLIR Systems" },
   { 1162,   "Xcelgo A/S" },
   { 1163,   "STRATON AUTOMATION" },
   { 1164,   "Zypcom, Inc." },
   { 1165,   "Swagelok" },
   { 1166,   "Elspec" },
   { 1167,   "ITT Water & Wastewater AB" },
   { 1168,   "Kunbus GmbH Industrial Communication" },
   { 1169,   "Reserved" },
   { 1170,   "Performance Controls, Inc." },
   { 1171,   "ACS Motion Control, Ltd." },
   { 1172,   "Reserved" },
   { 1173,   "IStar Technology Limited" },
   { 1174,   "Alicat Scientific, Inc." },
   { 1175,   "Reserved" },
   { 1176,   "ADFweb.com SRL" },
   { 1177,   "Tata Consultancy Services Limited" },
   { 1178,   "CXR Ltd." },
   { 1179,   "Vishay Nobel AB" },
   { 1180,   "Reserved" },
   { 1181,   "Emerson - SolaHD" },
   { 1182,   "Endress+Hauser" },
   { 1183,   "Bartec GmbH" },
   { 1184,   "Reserved" },
   { 1185,   "AccuSentry, Inc." },
   { 1186,   "Curtiss Wright - Exlar Actuator Solutions" },
   { 1187,   "ILS Technology" },
   { 1188,   "Control Concepts, Inc." },
   { 1189,   "Reserved" },
   { 1190,   "Procon Engineering A Division of National Oilwell Varco UK Ltd" },
   { 1191,   "Hermary" },
   { 1192,   "Q-Lambda" },
   { 1193,   "Reserved" },
   { 1194,   "VAMP Ltd" },
   { 1195,   "FlexLink" },
   { 1196,   "Office FA.com Co., Ltd." },
   { 1197,   "SPMC (Changzhou) Co. Ltd." },
   { 1198,   "Anton Paar GmbH" },
   { 1199,   "Zhuzhou CSR Times Electric Co., Ltd." },
   { 1200,   "DeStaCo" },
   { 1201,   "Synrad, Inc" },
   { 1202,   "Bonfiglioli Vectron GmbH" },
   { 1203,   "Pivotal Systems" },
   { 1204,   "TKSCT" },
   { 1205,   "Randy Nuernberger" },
   { 1206,   "CENTRALP" },
   { 1207,   "Tengen Group" },
   { 1208,   "OES, Inc." },
   { 1209,   "Actel Corporation" },
   { 1210,   "Monaghan Engineering, Inc." },
   { 1211,   "wenglor sensoric gmbh" },
   { 1212,   "HSA Systems" },
   { 1213,   "MKP Co., Ltd." },
   { 1214,   "Tappan Wire and Cable" },
   { 1215,   "Heinzmann GmbH & Co. KG" },
   { 1216,   "Process Automation International Ltd." },
   { 1217,   "Secure Crossing" },
   { 1218,   "SMA Railway Technology GmbH" },
   { 1219,   "FMS Force Measuring Systems AG" },
   { 1220,   "ABT Endustri Enerji Sistemleri Sanayi Tic. Ltd. Sti." },
   { 1221,   "MagneMotion Inc." },
   { 1222,   "STS Co., Ltd." },
   { 1223,   "Knorr-Bremse Espana, S.A. - Merak Division" },
   { 1224,   "ABOUNDI, Inc." },
   { 1225,   "Rosemount Inc." },
   { 1226,   "GEA FES, Inc." },
   { 1227,   "TMG Technologie und Engineering GmbH" },
   { 1228,   "embeX GmbH" },
   { 1229,   "GH Electrotermia, S.A." },
   { 1230,   "Tolomatic" },
   { 1231,   "Dukane" },
   { 1232,   "Elco (Tian Jin) Electronics Co., Ltd." },
   { 1233,   "Jacobs Automation" },
   { 1234,   "Noda Radio Frequency Technologies Co., Ltd." },
   { 1235,   "MSC Tuttlingen GmbH" },
   { 1236,   "Hitachi Cable Manchester" },
   { 1237,   "ACOREL SAS" },
   { 1238,   "Global Engineering Solutions Co., Ltd." },
   { 1239,   "ALTE Transportation, S.L." },
   { 1240,   "Penko Engineering B.V." },
   { 1241,   "Z-Tec Automation Systems Inc." },
   { 1242,   "ENTRON Controls LLC" },
   { 1243,   "Johannes Huebner Fabrik Elektrischer Maschinen GmbH" },
   { 1244,   "RF IDeas, Inc." },
   { 1245,   "Pentronic AB" },
   { 1246,   "Atlas Copco IAS GmbH" },
   { 1247,   "TDK-Lambda Corporation" },
   { 1248,   "Reserved" },
   { 1249,   "Reserved" },
   { 1250,   "Altronic LLC" },
   { 1251,   "Siemens AG" },
   { 1252,   "Liebherr Transportation Systems GmbH & Co KG" },
   { 1253,   "Reserved" },
   { 1254,   "Reserved" },
   { 1255,   "Reserved" },
   { 1256,   "LMI Technologies" },
   { 1257,   "Reserved" },
   { 1258,   "Reserved" },
   { 1259,   "Reserved" },
   { 1260,   "Reserved" },
   { 1261,   "CEPHALOS Automatisierung mbH" },
   { 1262,   "Reserved" },
   { 1263,   "Reserved" },
   { 1264,   "Reserved" },
   { 1265,   "Quabbin Wire & Cable Co., Inc." },
   { 1266,   "Reserved" },
   { 1267,   "Reserved" },
   { 1268,   "HORIBA Precision Instruments (Beijing) Co.,Ltd." },
   { 1269,   "Reserved" },
   { 1270,   "Rovema GmbH" },
   { 1271,   "Reserved" },
   { 1272,   "IEP GmbH" },
   { 1273,   "Reserved" },
   { 1274,   "Reserved" },
   { 1275,   "Reserved" },
   { 1276,   "Reserved" },
   { 1277,   "Control Chief Corporation" },
   { 1278,   "Reserved" },
   { 1279,   "Reserved" },
   { 1280,   "Reserved" },
   { 1281,   "Reserved" },
   { 1282,   "PRIMES GmbH" },
   { 1283,   "Branson Ultrasonics" },
   { 1284,   "DEIF A/S" },
   { 1285,   "CODESYS GmbH" },
   { 1286,   "Reserved" },
   { 1287,   "Smarteye Corporation" },
   { 1288,   "Shibaura Machine" },
   { 1289,   "HMS/BU Ewon" },
   { 1290,   "OFS" },
   { 1291,   "KROHNE" },
   { 1292,   "Reserved" },
   { 1293,   "Reserved" },
   { 1294,   "Reserved" },
   { 1295,   "Kistler Instrumente AG" },
   { 1296,   "Reserved" },
   { 1297,   "Reserved" },
   { 1298,   "Reserved" },
   { 1299,   "Reserved" },
   { 1300,   "Reserved" },
   { 1301,   "Xylem Analytics Germany GmbH" },
   { 1302,   "Lenord, Bauer & Co. GmbH" },
   { 1303,   "Carlo Gavazzi Controls" },
   { 1304,   "Faiveley Transport" },
   { 1305,   "Reserved" },
   { 1306,   "Sensia LLC" },
   { 1307,   "Kepware Technologies" },
   { 1308,   "duagon AG" },
   { 1309,   "Reserved" },
   { 1310,   "Xylem Water Solutions" },
   { 1311,   "Automation Professionals, LLC" },
   { 1312,   "Reserved" },
   { 1313,   "CEIA SpA" },
   { 1314,   "Reserved" },
   { 1315,   "Alphagate Automatisierungstechnik GmbH" },
   { 1316,   "Mecco Partners, LLC" },
   { 1317,   "LAP GmbH Laser Applikationen" },
   { 1318,   "ABB S.p.A. - SACE Division" },
   { 1319,   "Reserved" },
   { 1320,   "Reserved" },
   { 1321,   "C.E. Electronics, Inc." },
   { 1322,   "Thermo Ramsey Inc., a part of Thermo Fisher Scientific" },
   { 1323,   "Helmholz GmbH & Co. KG" },
   { 1324,   "EUCHNER GmbH + Co. KG" },
   { 1325,   "AMKmotion" },
   { 1326,   "Badger Meter" },
   { 1327,   "Reserved" },
   { 1328,   "Fisher-Rosemount Systems, Inc. doing business as Process Systems & Solutions" },
   { 1329,   "Conductix-Wampfler Automation GmbH" },
   { 1330,   "Fairbanks Scales, Inc." },
   { 1331,   "Imperx, Inc." },
   { 1332,   "FRONIUS International GmbH" },
   { 1333,   "Hoffman Enclosures" },
   { 1334,   "Elecsys Corporation" },
   { 1335,   "Reserved" },
   { 1336,   "RACO Manufacturing and Engineering" },
   { 1337,   "Hein Lanz Industrial Tech." },
   { 1338,   "Codenomicon" },
   { 1339,   "SABO Elektronik GmbH" },
   { 1340,   "Reserved" },
   { 1341,   "Sensirion AG" },
   { 1342,   "SIKO GmbH" },
   { 1343,   "Reserved" },
   { 1344,   "GRUNDFOS" },
   { 1345,   "Reserved" },
   { 1346,   "Beijer Electronics Products AB" },
   { 1347,   "Reserved" },
   { 1348,   "AIMCO" },
   { 1349,   "Reserved" },
   { 1350,   "Coval" },
   { 1351,   "Powell Industries" },
   { 1352,   "Reserved" },
   { 1353,   "IPDisplays" },
   { 1354,   "SCAIME SAS" },
   { 1355,   "Metal Work SpA" },
   { 1356,   "Telsonic AG" },
   { 1357,   "Reserved" },
   { 1358,   "Hauch & Bach ApS" },
   { 1359,   "Pago AG" },
   { 1360,   "ULTIMATE Europe Transportation Equipment GmbH" },
   { 1361,   "Reserved" },
   { 1362,   "FW Murphy Production Controls, LLC" },
   { 1363,   "Lake Cable LLC" },
   { 1364,   "Reserved" },
   { 1365,   "Reserved" },
   { 1366,   "Reserved" },
   { 1367,   "Reserved" },
   { 1368,   "Nanotec Electronic GmbH & Co. KG" },
   { 1369,   "SAMWON ACT Co., Ltd." },
   { 1370,   "Aparian Inc." },
   { 1371,   "Cosys Inc." },
   { 1372,   "Insight Automation Inc." },
   { 1373,   "Reserved" },
   { 1374,   "FASTECH" },
   { 1375,   "K.A. Schmersal GmbH & Co. KG" },
   { 1376,   "Reserved" },
   { 1377,   "Reserved" },
   { 1378,   "SEIDENSHA ELECTRONICS CO., LTD" },
   { 1379,   "Reserved" },
   { 1380,   "Don Electronics Ltd" },
   { 1381,   "burster gmbh & co kg" },
   { 1382,   "Unitronics (1989) (RG) LTD" },
   { 1383,   "OEM Technology Solutions" },
   { 1384,   "Allied Motion" },
   { 1385,   "Reserved" },
   { 1386,   "DENGENSHA TOA CO., LTD" },
   { 1387,   "Systec Systemtechnik und Industrieautomation GmbH" },
   { 1388,   "Reserved" },
   { 1389,   "Jenny Science AG" },
   { 1390,   "Baumer Optronic GmbH" },
   { 1391,   "Invertek Drives Ltd" },
   { 1392,   "High Grade Controls Corporation" },
   { 1393,   "Reserved" },
   { 1394,   "Reserved" },
   { 1395,   "Reserved" },
   { 1396,   "Actia Systems" },
   { 1397,   "Reserved" },
   { 1398,   "Beijing Tianma Intelligent Control Technology Co., Ltd" },
   { 1399,   "Universal Robots A/S" },
   { 1400,   "Reserved" },
   { 1401,   "Dialight" },
   { 1402,   "E-T-A Elektrotechnische Apparate GmbH" },
   { 1403,   "Kemppi Oy" },
   { 1404,   "Reserved" },
   { 1405,   "ORing Industrial Networking Corp." },
   { 1406,   "Reserved" },
   { 1407,   "Reserved" },
   { 1408,   "ELAP S.R.L." },
   { 1409,   "Applied Mining Technologies" },
   { 1410,   "KITZ SCT Corporation" },
   { 1411,   "VTEX Corporation" },
   { 1412,   "ESYSE GmbH Embedded Systems Engineering" },
   { 1413,   "Automation Controls" },
   { 1414,   "Reserved" },
   { 1415,   "Cincinnati Test Systems" },
   { 1416,   "Reserved" },
   { 1417,   "Zumbach Electronics Corp." },
   { 1418,   "Emerson Automation Solutions" },
   { 1419,   "CCS Inc." },
   { 1420,   "Videojet, Inc." },
   { 1421,   "Zebra Technologies" },
   { 1422,   "ANRITSU CORPORATION" },
   { 1423,   "Dimetix AG" },
   { 1424,   "General Measure (China)" },
   { 1425,   "Fortress Interlocks" },
   { 1426,   "Reserved" },
   { 1427,   "Task Force Tips" },
   { 1428,   "SERVO-ROBOT INC." },
   { 1429,   "Flow Devices and Systems, Inc." },
   { 1430,   "nLIGHT, Inc." },
   { 1431,   "Microchip Technology Inc." },
   { 1432,   "Reserved" },
   { 1433,   "Reserved" },
   { 1434,   "Accutron Instruments Inc." },
   { 1435,   "Kaeser Kompressoren SE" },
   { 1436,   "Reserved" },
   { 1437,   "Coherix, Inc." },
   { 1438,   "FLSmidth A/S" },
   { 1439,   "Reserved" },
   { 1440,   "Cole-Parmer Instrument Company" },
   { 1441,   "Wachendorff Automation GmbH & Co., KG" },
   { 1442,   "SMAC Moving Coil Actuators" },
   { 1444,   "PushCorp, Inc." },
   { 1445,   "Fluke Process Instruments GmbH" },
   { 1446,   "Mini Motor S.p.a" },
   { 1447,   "I-CON Industry Tech." },
   { 1448,   "Grace Technologies" },
   { 1449,   "Zaxis Inc." },
   { 1450,   "Lumasense Technologies" },
   { 1451,   "Domino Printing" },
   { 1452,   "Reserved" },
   { 1453,   "Reserved" },
   { 1454,   "Altus Sistemas de Automação S.A." },
   { 1455,   "Reserved" },
   { 1456,   "InterTech Development Company" },
   { 1457,   "Reserved" },
   { 1458,   "Perle Systems Limited" },
   { 1459,   "Utthunga Technologies Pvt Ltd.," },
   { 1460,   "Reserved" },
   { 1461,   "WIPOTEC GmbH" },
   { 1462,   "Atos spa" },
   { 1463,   "Solartron Metrology LTD" },
   { 1464,   "Reserved" },
   { 1465,   "Analog Devices" },
   { 1466,   "Power Electronics International, Inc." },
   { 1468,   "Campbell Wrapper Corporation" },
   { 1469,   "Herkules-Resotec Elektronik GmbH" },
   { 1470,   "aignep spa" },
   { 1471,   "Reserved" },
   { 1472,   "PMV Automation AB" },
   { 1473,   "Reserved" },
   { 1474,   "ProTec Dynatronix LLC dba Dynatronix" },
   { 1475,   "Reserved" },
   { 1476,   "Bitronics, LLC." },
   { 1477,   "Delta Tau Data Systems" },
   { 1478,   "Reserved" },
   { 1479,   "AUTOSOL" },
   { 1480,   "ADB Safegate" },
   { 1481,   "Reserved" },
   { 1482,   "Reserved" },
   { 1483,   "Artis GmbH" },
   { 1484,   "REJ Co., LTD" },
   { 1485,   "Vanderlande" },
   { 1486,   "Packet Power" },
   { 1487,   "ima-tec gmbh" },
   { 1488,   "Vision Automation A/S" },
   { 1489,   "PROCENTEC BV" },
   { 1490,   "HETRONIK GmbH" },
   { 1491,   "Lanmark Controls Inc." },
   { 1492,   "Reserved" },
   { 1493,   "flexlog GmbH" },
   { 1494,   "YUCHANGTECH" },
   { 1495,   "Dynapower Company" },
   { 1496,   "TAKIKAWA ENGINEERING" },
   { 1497,   "Ingersoll Rand" },
   { 1498,   "ASA-RT s.r.l" },
   { 1499,   "TRUMPF Schweiz AG" },
   { 1500,   "Reserved" },
   { 1501,   "Rinstrum" },
   { 1502,   "Reserved" },
   { 1503,   "Reserved" },
   { 1504,   "BlueBotics SA" },
   { 1505,   "Dynapar Corporation" },
   { 1506,   "Blum-Novotest" },
   { 1507,   "CIMON" },
   { 1508,   "Dalian SeaSky Automation Co., ltd" },
   { 1509,   "Rethink Robotics GmbH" },
   { 1510,   "Ingeteam Power Technology S. A." },
   { 1511,   "TOSEI ENGINEERING CORP." },
   { 1512,   "SAMSON AG" },
   { 1513,   "TGW Mechanics GmbH" },
   { 1514,   "Diatrend Corporation" },
   { 1515,   "Reserved" },
   { 1516,   "VAHLE Automation GmbH" },
   { 1517,   "JSL Technology Co.,Ltd." },
   { 1518,   "NetTechnix E&P GmbH" },
   { 1519,   "Reserved" },
   { 1520,   "Tecweigh" },
   { 1521,   "IVEK Corporation" },
   { 1522,   "Reserved" },
   { 1523,   "AQ M-TECH AB" },
   { 1524,   "Rexnord Industries LLC" },
   { 1525,   "Reserved" },
   { 1526,   "OPTEX FA Co., Ltd" },
   { 1527,   "Volktek Corporation" },
   { 1528,   "INGENIA" },
   { 1529,   "Reserved" },
   { 1530,   "Analytical Technology, Inc." },
   { 1531,   "Columbus McKinnon Corporation" },
   { 1532,   "HBC-radiomatic GmbH" },
   { 1533,   "Leonton Technologies" },
   { 1534,   "Mitsubishi Electric India Pvt. Ltd." },
   { 1535,   "FOBA Laser, ALLTEC GmbH" },
   { 1536,   "Leakmaster Inc" },
   { 1537,   "Buhler AG" },
   { 1538,   "LINAK Denmark A/S" },
   { 1539,   "Reserved" },
   { 1540,   "SIEB & MEYER AG" },
   { 1541,   "Reserved" },
   { 1542,   "Watson-Marlow Ltd" },
   { 1543,   "ABB Switzerland Ltd - Low Voltage Products" },
   { 1544,   "Reserved" },
   { 1545,   "adphos" },
   { 1546,   "Hangzhou Hikrobot Technology Co., Ltd." },
   { 1547,   "TOSS GmbH & Co. KG" },
   { 1548,   "Solar Turbines Incorporated" },
   { 1549,   "Reserved" },
   { 1550,   "Reserved" },
   { 1551,   "Carlo Gavazzi Industri" },
   { 1552,   "Nippon Gear" },
   { 1553,   "OSIsoft, LLC" },
   { 1554,   "Rinco Ultrasonics AG" },
   { 1555,   "Reserved" },
   { 1556,   "Reserved" },
   { 1557,   "Mitutoyo" },
   { 1558,   "swisca" },
   { 1559,   "Micro-Epsilon Messtechnik GmbH & Co. KG" },
   { 1560,   "AMADA MIYACHI AMERICA" },
   { 1561,   "Taihan Electric Wire Co., Ltd." },
   { 1562,   "JANOME Corporation" },
   { 1563,   "ISHIDA" },
   { 1564,   "NAKANISHI INC." },
   { 1565,   "Mecademic Inc." },
   { 1566,   "Reserved" },
   { 1567,   "Sigma (NSW) PTY LTD" },
   { 1568,   "Hammond Power Solutions Inc.-Mesta" },
   { 1569,   "Reserved" },
   { 1570,   "Reserved" },
   { 1571,   "TRIDIMEO" },
   { 1572,   "Motortronics UK Ltd." },
   { 1573,   "Doosan Robotics" },
   { 1574,   "ADVANCED Motion Controls" },
   { 1575,   "OnRobot A/S" },
   { 1576,   "Reserved" },
   { 1577,   "Oetiker" },
   { 1578,   "SICK OPTEX" },
   { 1579,   "Reserved" },
   { 1580,   "Reserved" },
   { 1581,   "Kahler Automation Corporation" },
   { 1582,   "Accuenergy (Canada) Inc." },
   { 1583,   "TCI, LLC - An Allied Motion Company" },
   { 1584,   "Sun Automation" },
   { 1585,   "READY Robotics Corporation" },
   { 1586,   "PEM, Power Eng & Mfg., Inc" },
   { 1587,   "Dürr Somac GmbH" },
   { 1588,   "Reserved" },
   { 1589,   "Reserved" },
   { 1590,   "Reserved" },
   { 1591,   "RICOH Industrial Solutions Inc." },
   { 1592,   "Shanghai Junqian Sensing Technology Co. Ltd." },
   { 1593,   "Knick Elektronische Messgeräte GmbH & Co. KG" },
   { 1594,   "Magnescale. Co., Ltd." },
   { 1595,   "Reserved" },
   { 1596,   "Weintek Labs., Inc" },
   { 1597,   "Sherpa, Inc." },
   { 1598,   "Inspekto A.M.V LTD" },
   { 1599,   "Hydronix Ltd" },
   { 1600,   "AIOI- SYSTEMS CO. LTD." },
   { 1601,   "Ingenieurbüro Mewes & Partner GmbH" },
   { 1602,   "HIGHYAG Lasertechnologie GmbH" },
   { 1603,   "Ningbo Jetron Technology Co. Ltd." },
   { 1604,   "Myostat Motion Control" },
   { 1605,   "A-T Controls, Inc" },
   { 1606,   "M2M craft Co., Ltd." },
   { 1607,   "FUTEK Advanced Sensor Technology, Inc." },
   { 1608,   "Cetek" },
   { 1609,   "Norgren Manufacturing Co., Ltd." },
   { 1610,   "Bernstein AG" },
   { 1611,   "Hitachi Industrial Products, Ltd." },
   { 1612,   "Reserved" },
   { 1613,   "Duplomatic MS spa" },
   { 1614,   "Ambrit Ltd" },
   { 1615,   "Highlight Tech Corp." },
   { 1616,   "New Power Plasma Co. Ltd." },
   { 1617,   "AGCO Corporation" },
   { 1618,   "Techman Robot" },
   { 1619,   "Nabeya Bi-tech Kaisha" },
   { 1620,   "Reserved" },
   { 1621,   "Panasonic Corporation / Electric Works Company" },
   { 1622,   "Cytiva" },
   { 1623,   "Janasi Industries Ltd." },
   { 1624,   "Haffmans BV" },
   { 1625,   "Reserved" },
   { 1626,   "Omniview Pty Ltd" },
   { 1627,   "Reserved" },
   { 1628,   "Dover Flexo Electronics" },
   { 1629,   "Reserved" },
   { 1630,   "IDEM Safety Switches" },
   { 1631,   "Sonotroagel GmbH" },
   { 1632,   "Thermo Gamma-Metrics LLC, a part of Thermo Fisher Scientific" },
   { 1633,   "BBH Products" },
   { 1634,   "RSI Elektrotechnik" },
   { 1635,   "Carlo Gavazzi Ltd" },
   { 1636,   "KOFLOC Corp." },
   { 1637,   "VTScada by Trihedral" },
   { 1638,   "Hach" },
   { 1639,   "Cogniac" },
   { 1640,   "Toshiba Infrastructure Systems & Solutions Corporation" },
   { 1641,   "Cannon-Automata" },
   { 1642,   "Rosenberger" },
   { 1643,   "Blue-White Industries" },
   { 1644,   "Cellumation GmbH" },
   { 1645,   "TEAC Corp." },
   { 1646,   "AEG Identifikationssysteme GmbH" },
   { 1647,   "MARS TOHKEN SOLUTION CO.LTD." },
   { 1648,   "Midas Technology Corp." },
   { 1649,   "Dinkle Enterprise Co., Ltd." },
   { 1650,   "THALES" },
   { 1651,   "Dunkermotoren" },
   { 1652,   "SONOTEC GmbH" },
   { 1653,   "Brinkmann Pumpen" },
   { 1654,   "Rheonics" },
   { 1655,   "Precimeter" },
   { 1656,   "Reserved" },
   { 1657,   "ALGO SYSTEM CO., LTD." },
   { 1658,   "Christ Electronic Systems GmbH" },
   { 1659,   "JFcontrol Co., Ltd." },
   { 1660,   "Shenzhen Inovance Technology Co., Ltd" },
   { 1661,   "Rheonik Coriolis Mass Flow Sensors" },
   { 1662,   "Ichor Systems, Inc." },
   { 1663,   "di-soric GmbH & Co. KG" },
   { 1664,   "Amphenol ICC" },
   { 1665,   "Ningbo AirTAC Automation Industrial Co., Ltd." },
   { 1666,   "RSI" },
   { 1667,   "Soft Robotics Inc." },
   { 1668,   "MUSCLE CORPORATION" },
   { 1669,   "Spotlight Systems LLC" },
   { 1670,   "Afag Holding AG" },
   { 1671,   "TELESIS TECHNOLOGIES INC" },
   { 1672,   "SSI Schaefer Automation GmbH" },
   { 1673,   "Super Systems, Inc." },
   { 1674,   "CoreTigo LTD" },
   { 1675,   "Inxpect SPA" },
   { 1676,   "Kostal Industrie Elektrik GmbH" },
   { 1677,   "JingQi（Tianjin）technology Co.,Ltd" },
   { 1678,   "AGI Suretrack" },
   { 1679,   "JAKA Robotics Co., Ltd." },
   { 1680,   "Polarteknik Oy" },
   { 1681,   "RoboteQ, Inc" },
   { 1682,   "Uson" },
   { 1683,   "Opt Machine Vision Tech Co., Ltd" },
   { 1684,   "Asyril SA" },
   { 1685,   "Georg Fischer Piping Systems" },
   { 1686,   "Aber Instruments Ltd" },
   { 1687,   "CodeWrights GmbH" },
   { 1688,   "Neurala, Inc." },
   { 1689,   "Panasonic Software Development Center Dalian Co.,Ltd." },
   { 1690,   "Perinet GmbH" },
   { 1691,   "MS Ultraschall Technologie GmbH" },
   { 1692,   "PLASUS GmbH" },
   { 1693,   "Nikon Corporation" },
   { 1694,   "Shenzhen Hengzhiyuan Technology Corporation Ltd." },
   { 1695,   "Kowa Optronics Co., Ltd." },
   { 1696,   "Specialist Mechanical Engineers" },
   { 1697,   "CMD Corporation" },
   { 1698,   "Sanwa Engineering Corp." },
   { 1699,   "Intellore Systems Pvt. Ltd" },
   { 1700,   "Toledo e Souza" },
   { 1701,   "PBS Biotech, Inc" },
   { 1702,   "PLANET Technology Corporation" },
   { 1703,   "Robatech AG" },
   { 1704,   "MARKEM-IMAJE" },
   { 1705,   "Novanta IMS" },
   { 1706,   "Bamboo-Dynamics" },
   { 1707,   "FACTS Engineering, LLC" },
   { 1708,   "Digital Dynamics" },
   { 1709,   "Fatek Automation Corporation" },
   { 1710,   "Hanwha" },
   { 1711,   "Fukuda" },
   { 1712,   "Zhejiang Eternal Automation Sci-Tec Co.,Ltd" },
   { 1713,   "KYOWA ELECTRONIC INSTRUMENTS CO.,LTD." },
   { 1714,   "Vaisala Oyj" },
   { 1715,   "Hennecke GmbH" },
   { 1716,   "Encoder Products Company" },
   { 1717,   "Converting Equipment International (dba CEI)" },
   { 1718,   "Reserved" },
   { 1719,   "Kinova" },
   { 1720,   "The Poling Group, Inc." },
   { 1721,   "plating electronic GmbH" },
   { 1722,   "HIWIN MIKROSYSTEM CORP." },
   { 1723,   "Wuxi Xinje Electric Co.,Ltd." },
   { 1724,   "ViSCO Technologies Corporation" },
   { 1725,   "MinebeaMitsumi Inc." },
   { 1726,   "FIAtec GmbH" },
   { 1727,   "eSOL Co.,Ltd" },
   { 1728,   "NTN TECHNICAL SERVICE" },
   { 1729,   "Shanghai Flexem" },
   { 1730,   "Magswitch" },
   { 1731,   "VEGA Grieshaber KG" },
   { 1732,   "H.D.T. S.R.L." },
   { 1733,   "Tool-Temp AG" },
   { 1734,   "Hollysys Technology Group Co., Ltd" },
   { 1735,   "Basler Electric Company" },
   { 1736,   "Shinwa Controls Co.,Ltd" },
   { 1737,   "Nanjing Decowell Automation Co.,Ltd." },
   { 1738,   "Reverity Inc" },
   { 1739,   "TOSHIBA MITSUBISHI-ELECTRIC INDUSTRIAL SYSTEMS CORPORATION" },
   { 1740,   "BizLink Special Cables Germany GmbH" },
   { 1741,   "Electronics Inc." },
   { 1742,   "Inexbot" },
   { 1743,   "Mujin, Inc." },
   { 1744,   "Shanghai AYAN Industry System Co., Ltd" },
   { 1745,   "EKE-Electronics Ltd." },
   { 1746,   "Bizerba SE & Co. KG" },
   { 1747,   "Astrodyne TDI" },
   { 9876,   "ODVA" },

   { 0, NULL }
};

value_string_ext cip_vendor_vals_ext = VALUE_STRING_EXT_INIT(cip_vendor_vals);

/* Translate Device Profile's */
static const value_string cip_devtype_vals[] = {
   { 0x00,        "Generic Device (deprecated)"         },
   { 0x02,        "AC Drive"                            },
   { 0x03,        "Motor Overload"                      },
   { 0x04,        "Limit Switch"                        },
   { 0x05,        "Inductive Proximity Switch"          },
   { 0x06,        "Photoelectric Sensor"                },
   { 0x07,        "General Purpose Discrete I/O"        },
   { 0x09,        "Resolver"                            },
   { 0x0C,        "Communications Adapter"              },
   { 0x0E,        "Programmable Logic Controller"       },
   { 0x10,        "Position Controller",                },
   { 0x13,        "DC Drive"                            },
   { 0x15,        "Contactor",                          },
   { 0x16,        "Motor Starter",                      },
   { 0x17,        "Soft Start",                         },
   { 0x18,        "Human-Machine Interface"             },
   { 0x1A,        "Mass Flow Controller"                },
   { 0x1B,        "Pneumatic Valve"                     },
   { 0x1C,        "Vacuum Pressure Gauge"               },
   { 0x1D,        "Process Control Value"               },
   { 0x1E,        "Residual Gas Analyzer"               },
   { 0x1F,        "DC Power Generator"                  },
   { 0x20,        "RF Power Generator"                  },
   { 0x21,        "Turbomolecular Vacuum Pump"          },
   { 0x22,        "Encoder"                             },
   { 0x23,        "Safety Discrete I/O Device"          },
   { 0x24,        "Fluid Flow Controller"               },
   { 0x25,        "CIP Motion Drive"                    },
   { 0x26,        "CompoNet Repeater"                   },
   { 0x27,        "Mass Flow Controller, Enhanced"      },
   { 0x28,        "CIP Modbus Device"                   },
   { 0x29,        "CIP Modbus Translator"               },
   { 0x2A,        "Safety Analog I/O Device"            },
   { 0x2B,        "Generic Device (keyable)"            },
   { 0x2C,        "Managed Ethernet Switch"             },
   { 0x2D,        "CIP Motion Safety Drive Device"      },
   { 0x2E,        "Safety Drive Device"                 },
   { 0x2F,        "CIP Motion Encoder"                  },
   { 0x30,        "CIP Motion Converter"                },
   { 0x31,        "CIP Motion I/O"                      },
   { 0x32,        "ControlNet Physical Layer Component" },
   { 0x33,        "Circuit Breaker"                     },
   { 0x34,        "HART Device"                         },
   { 0x35,        "CIP-HART Translator"                 },
   { 0xC8,        "Embedded Component"                  },

   { 0, NULL }
};

value_string_ext cip_devtype_vals_ext = VALUE_STRING_EXT_INIT(cip_devtype_vals);

/* Translate class names */
const value_string cip_class_names_vals[] = {
   { 0x01,     "Identity"                       },
   { 0x02,     "Message Router"                 },
   { 0x03,     "DeviceNet"                      },
   { 0x04,     "Assembly"                       },
   { 0x05,     "Connection"                     },
   { 0x06,     "Connection Manager"             },
   { 0x07,     "Register"                       },
   { 0x08,     "Discrete Input Point"           },
   { 0x09,     "Discrete Output Point"          },
   { 0x0A,     "Analog Input Point"             },
   { 0x0B,     "Analog Output Point"            },
   { 0x0E,     "Presence Sensing"               },
   { 0x0F,     "Parameter"                      },
   { 0x10,     "Parameter Group"                },
   { 0x12,     "Group"                          },
   { 0x1D,     "Discrete Input Group"           },
   { 0x1E,     "Discrete Output Group"          },
   { 0x1F,     "Discrete Group"                 },
   { 0x20,     "Analog Input Group"             },
   { 0x21,     "Analog Output Group"            },
   { 0x22,     "Analog Group"                   },
   { 0x23,     "Position Sensor"                },
   { 0x24,     "Position Controller Supervisor" },
   { 0x25,     "Position Controller"            },
   { 0x26,     "Block Sequencer"                },
   { 0x27,     "Command Block"                  },
   { 0x28,     "Motor Data"                     },
   { 0x29,     "Control Supervisor"             },
   { 0x2A,     "AC/DC Drive"                    },
   { 0x2B,     "Acknowledge Handler"            },
   { 0x2C,     "Overload"                       },
   { 0x2D,     "Softstart"                      },
   { 0x2E,     "Selection"                      },
   { 0x30,     "S-Device Supervisor"            },
   { 0x31,     "S-Analog Sensor"                },
   { 0x32,     "S-Analog Actuator"              },
   { 0x33,     "S-Single Stage Controller"      },
   { 0x34,     "S-Gas Calibration"              },
   { 0x35,     "Trip Point"                     },
   { 0x37,     "File"                           },
   { 0x38,     "S-Partial Pressure"             },
   { 0x39,     "Safety Supervisor"              },
   { 0x3A,     "Safety Validator"               },
   { 0x3B,     "Safety Discrete Output Point"   },
   { 0x3C,     "Safety Discrete Output Group"   },
   { 0x3D,     "Safety Discrete Input Point"    },
   { 0x3E,     "Safety Discrete Input Group"    },
   { 0x3F,     "Safety Dual Channel Output"     },
   { 0x40,     "S-Sensor Calibration"           },
   { 0x41,     "Event Log"                      },
   { 0x42,     "Motion Device Axis"             },
   { 0x43,     "Time Sync"                      },
   { 0x44,     "Modbus"                         },
   { 0x45,     "Originator Connection List"     },
   { 0x46,     "Modbus Serial Link"             },
   { 0x47,     "Device Level Ring (DLR)"        },
   { 0x48,     "QoS"                            },
   { 0x49,     "Safety Analog Input Point"      },
   { 0x4A,     "Safety Analog Input Group"      },
   { 0x4B,     "Safety Dual Channel Analog Input"  },
   { 0x4C,     "SERCOS III Link"                },
   { 0x4D,     "Target Connection List"         },
   { 0x4E,     "Base Energy"                    },
   { 0x4F,     "Electrical Energy"              },
   { 0x50,     "Non-Electrical Energy"          },
   { 0x51,     "Base Switch"                    },
   { 0x52,     "SNMP"                           },
   { 0x53,     "Power Management"               },
   { 0x54,     "RSTP Bridge"                    },
   { 0x55,     "RSTP Port"                      },
   { 0x56,     "PRP/HSR Protocol"               },
   { 0x57,     "PRP/HSR Nodes Table"            },
   { 0x58,     "Safety Feedback"                },
   { 0x59,     "Safety Dual Channel Feedback"   },
   { 0x5A,     "Safety Stop Functions"          },
   { 0x5B,     "Safety Limit Functions"         },
   { 0x5C,     "Power Curtailment"              },
   { 0x5D,     "CIP Security"                   },
   { 0x5E,     "EtherNet/IP Security"           },
   { 0x5F,     "Certificate Management"         },
   { 0x60,     "Authority"                      },
   { 0x61,     "Password Authenticator"         },
   { 0x62,     "Certificate Authenticator"      },
   { 0x67,     "PCCC Class"                     },
   { 0xF0,     "ControlNet"                     },
   { 0xF1,     "ControlNet Keeper"              },
   { 0xF2,     "ControlNet Scheduling"          },
   { 0xF3,     "Connection Configuration"       },
   { 0xF4,     "Port"                           },
   { 0xF5,     "TCP/IP Interface"               },
   { 0xF6,     "Ethernet Link"                  },
   { 0xF7,     "CompoNet"                       },
   { 0xF8,     "CompoNet Repeater"              },
   { 0xF9,     "HART Master Port"               },
   { 0xFA,     "I/O Aggregation"                },
   { 0x100,    "Protection Trip"                },
   { 0x101,    "Protection Alarm"               },
   { 0x102,    "Circuit Breaker Supervisor"     },
   { 0x103,    "Circuit Breaker Statistics"     },
   { 0x104,    "Electrical Demand"              },
   { 0x105,    "Electrical Statistics"          },
   { 0x106,    "Machine Base Data"              },
   { 0x107,    "HART Process Device"            },
   { 0x108,    "Process Device Diagnostics"     },
   { 0x109,    "LLDP Management"                },
   { 0x10A,    "LLDP Data Table"                },
   { 0x10B,    "IO-Link Service Parameter"      },
   { 0x10C,    "IO-Link Master PHY"             },
   { 0x10D,    "IO-Link Device PHY"             },
   { 0x10E,    "Pilot Light Supervisor"         },
   { 0x10F,    "Select Line Link"               },
   { 0x110,    "In-Cabinet Actual Topology"     },
   { 0x111,    "In-Cabinet Commissioning"       },

   { 0,        NULL                             }
};

const value_string cip_id_state_vals[] = {
   { 0, "Nonexistent" },
   { 1, "Device Self Testing" },
   { 2, "Standby" },
   { 3, "Operational" },
   { 4, "Major Recoverable Fault" },
   { 5, "Major Unrecoverable Fault" },

   { 0, NULL }
};

static const range_string cip_port_type_vals[] = {
   { 0, 0, "Any - no routing" },
   { 1, 1, "Reserved for legacy use" },
   { 2, 2, "ControlNet" },
   { 3, 3, "ControlNet with redundancy" },
   { 4, 4, "EtherNet/IP" },
   { 5, 5, "DeviceNet" },
   { 6, 99, "Reserved for legacy use" },
   { 100, 199, "Vendor Specific" },
   { 200, 200, "CompoNet" },
   { 201, 201, "Modbus/TCP" },
   { 202, 202, "Modbus/SL" },
   { 203, 203, "SERCOS III" },
   { 204, 65534, "Reserved for future use" },
   { 65535, 65535, "Any - user configurable" },

   { 0, 0, NULL }
};

const value_string cip_port_number_vals[] = {
   { 0, "Reserved" },
   { 1, "Backplane" },

   { 0, NULL }
};

value_string_ext cip_class_names_vals_ext = VALUE_STRING_EXT_INIT(cip_class_names_vals);

/* Translate function to string - Run/Idle */
static const value_string cip_run_idle_vals[] = {
   { 0, "Idle" },
   { 1, "Run" },

   { 0, NULL }
};

void cip_rpi_api_fmt(gchar *s, guint32 value)
{
   snprintf(s, ITEM_LABEL_LENGTH, "%.3fms", value / 1000.0);
}

static void add_cip_class_to_info_column(packet_info *pinfo, guint32 class_id, int display_type)
{
   cip_req_info_t *cip_req_info;

   /* Skip printing the top level class for certain common messages because it gets
      too wordy in the Info column. */
   cip_req_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
   if (cip_req_info
       && ((cip_req_info->bService == SC_CM_UNCON_SEND && class_id == CI_CLS_CM)
       || (cip_req_info->bService == SC_MULT_SERV_PACK && class_id == CI_CLS_MR)))
   {
       return;
   }

   if (display_type == DISPLAY_CONNECTION_PATH)
   {
       col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", val_to_str(class_id, cip_class_names_vals, "Class (0x%02x)"));
   }
   else if (display_type == DISPLAY_REQUEST_PATH)
   {
       col_append_fstr(pinfo->cinfo, COL_INFO, "%s - ", val_to_str(class_id, cip_class_names_vals, "Class (0x%02x)"));
   }
}

static void add_cip_symbol_to_info_column(packet_info *pinfo, gchar *symbol_name, int display_type)
{
   if (symbol_name == NULL)
   {
       return;
   }

   if (display_type == DISPLAY_CONNECTION_PATH)
   {
       col_append_fstr(pinfo->cinfo, COL_INFO, " ('%s')", symbol_name);
   }
   else if (display_type == DISPLAY_REQUEST_PATH)
   {
       col_append_fstr(pinfo->cinfo, COL_INFO, "'%s' - ", symbol_name);
   }
}

void add_cip_service_to_info_column(packet_info *pinfo, guint8 service, const value_string* service_vals)
{
   col_append_str( pinfo->cinfo, COL_INFO,
      val_to_str(service & CIP_SC_MASK, service_vals, "Service (0x%02x)"));
   col_set_fence(pinfo->cinfo, COL_INFO);
}

static void add_cip_pccc_function_to_info_column(packet_info *pinfo, guint8 fnc, const value_string* fnc_vals)
{
   col_append_fstr( pinfo->cinfo, COL_INFO,
      " - %s", val_to_str(fnc, fnc_vals, "Function (0x%02x)"));
   col_set_fence(pinfo->cinfo, COL_INFO);
}

static int dissect_id_revision(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_identity_revision);
      return total_len;
   }

   proto_tree_add_item( tree, hf_id_major_rev, tvb, offset, 1, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_id_minor_rev, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
   return 2;
}

static int dissect_id_status(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
   int offset, int total_len)
{
   static int * const status[] = {
      &hf_id_status_owned,
      &hf_id_status_conf,
      &hf_id_status_extended1,
      &hf_id_status_minor_fault_rec,
      &hf_id_status_minor_fault_unrec,
      &hf_id_status_major_fault_rec,
      &hf_id_status_major_fault_unrec,
      &hf_id_status_extended2,
      NULL
   };

   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_identity_status);
      return total_len;
   }

   proto_tree_add_bitmask(tree, tvb, offset, hf_id_status, ett_id_status, status, ENC_LITTLE_ENDIAN);

   return 2;
}

static int dissect_msg_rout_num_classes(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint16 i, num_classes;

   num_classes = tvb_get_letohs( tvb, offset);
   proto_tree_add_item( tree, hf_msg_rout_num_classes, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   if (total_len < (2+(num_classes*2)))
   {
      expert_add_info(pinfo, item, &ei_mal_msg_rout_num_classes);
      return total_len;
   }

   for (i = 0; i < num_classes; i++)
      proto_tree_add_item( tree, hf_msg_rout_classes, tvb, offset+2+(i*2), 2, ENC_LITTLE_ENDIAN);

   return (2+(num_classes*2));
}

static int dissect_cm_connection_entry_list(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
    int offset, int total_len _U_)
{
    guint32 num_conn_entries = 0;
    guint32 num_conn_entries_bytes;

    proto_tree_add_item_ret_uint(tree, hf_conn_mgr_num_conn_entries, tvb, offset, 2, ENC_LITTLE_ENDIAN, &num_conn_entries);

    num_conn_entries_bytes = (num_conn_entries+7)/8;
    proto_tree_add_uint(tree, hf_conn_mgr_num_conn_entries_bytes, tvb, 0, 0, num_conn_entries_bytes);

    for (guint32 i = 0; i < num_conn_entries_bytes; i++)
    {
        proto_tree_add_item(tree, hf_conn_mgr_conn_open_bits, tvb, offset + 2 + i, 1, ENC_LITTLE_ENDIAN);
    }

    return 2 + num_conn_entries_bytes;
}

static int dissect_time_sync_grandmaster_clock(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   if (total_len < 24)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_gm_clock);
      return total_len;
   }

   proto_tree_add_item( tree, hf_time_sync_gm_clock_clock_id, tvb, offset, 8, ENC_NA);
   proto_tree_add_item( tree, hf_time_sync_gm_clock_clock_class, tvb, offset+8, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_gm_clock_time_accuracy, tvb, offset+10, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_gm_clock_offset_scaled_log_variance, tvb, offset+12, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_gm_clock_current_utc_offset, tvb, offset+14, 2, ENC_LITTLE_ENDIAN);

   static int* const bits[] = {
      &hf_time_sync_gm_clock_time_property_flags_leap61,
      &hf_time_sync_gm_clock_time_property_flags_leap59,
      &hf_time_sync_gm_clock_time_property_flags_current_utc_valid,
      &hf_time_sync_gm_clock_time_property_flags_ptp_timescale,
      &hf_time_sync_gm_clock_time_property_flags_time_traceable,
      &hf_time_sync_gm_clock_time_property_flags_freq_traceable,
      NULL
   };
   proto_tree_add_bitmask(tree, tvb, offset + 16, hf_time_sync_gm_clock_time_property_flags, ett_time_sync_gm_clock_flags, bits, ENC_LITTLE_ENDIAN);

   proto_tree_add_item( tree, hf_time_sync_gm_clock_time_source, tvb, offset+18, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_gm_clock_priority1, tvb, offset+20, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_gm_clock_priority2, tvb, offset+22, 2, ENC_LITTLE_ENDIAN);
   return 24;
}

static int dissect_time_sync_parent_clock(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   if (total_len < 16)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_parent_clock);
      return total_len;
   }

   proto_tree_add_item( tree, hf_time_sync_parent_clock_clock_id, tvb, offset, 8, ENC_NA);
   proto_tree_add_item( tree, hf_time_sync_parent_clock_port_number, tvb, offset+8, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_parent_clock_observed_offset_scaled_log_variance, tvb, offset+10, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_parent_clock_observed_phase_change_rate, tvb, offset+12, 4, ENC_LITTLE_ENDIAN);
   return 16;
}

static int dissect_time_sync_local_clock(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   if (total_len < 20)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_local_clock);
      return total_len;
   }

   proto_tree_add_item( tree, hf_time_sync_local_clock_clock_id, tvb, offset, 8, ENC_NA);
   proto_tree_add_item( tree, hf_time_sync_local_clock_clock_class, tvb, offset+8, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_local_clock_time_accuracy, tvb, offset+10, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_local_clock_offset_scaled_log_variance, tvb, offset+12, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_local_clock_current_utc_offset, tvb, offset+14, 2, ENC_LITTLE_ENDIAN);

   static int* const bits[] = {
      &hf_time_sync_local_clock_time_property_flags_leap61,
      &hf_time_sync_local_clock_time_property_flags_leap59,
      &hf_time_sync_local_clock_time_property_flags_current_utc_valid,
      &hf_time_sync_local_clock_time_property_flags_ptp_timescale,
      &hf_time_sync_local_clock_time_property_flags_time_traceable,
      &hf_time_sync_local_clock_time_property_flags_freq_traceable,
      NULL
   };
   proto_tree_add_bitmask(tree, tvb, offset + 16, hf_time_sync_local_clock_time_property_flags, ett_time_sync_local_clock_flags, bits, ENC_LITTLE_ENDIAN);

   proto_tree_add_item( tree, hf_time_sync_local_clock_time_source, tvb, offset+18, 2, ENC_LITTLE_ENDIAN);
   return 20;
}

static int dissect_time_sync_port_state_info(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint16 i, num_ports;
   proto_tree* port_tree;

   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_state_info);
      return total_len;
   }

   num_ports = tvb_get_letohs( tvb, offset);
   proto_tree_add_item( tree, hf_time_sync_port_state_info_num_ports, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   if (2+num_ports*4 > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_state_info_ports);
      return total_len;
   }

   for (i = 0; i < num_ports; i++)
   {
       port_tree = proto_tree_add_subtree_format(tree, tvb, offset+2+i*4, 4, ett_time_sync_port_state_info, NULL, "Port #%d", i+1);
       proto_tree_add_item(port_tree, hf_time_sync_port_state_info_port_num, tvb, offset+2+i*4, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_state_info_port_state, tvb, offset+4+i*4, 2, ENC_LITTLE_ENDIAN);
   }

   return 2+num_ports*4;
}

static int dissect_time_sync_port_enable_cfg(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint16 i, num_ports;
   proto_tree* port_tree;

   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_enable_cfg);
      return total_len;
   }

   num_ports = tvb_get_letohs( tvb, offset);
   proto_tree_add_item( tree, hf_time_sync_port_enable_cfg_num_ports, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   if (2+num_ports*4 > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_enable_cfg_ports);
      return total_len;
   }

   for (i = 0; i < num_ports; i++)
   {
       port_tree = proto_tree_add_subtree_format(tree, tvb, offset+2+i*4, 4, ett_time_sync_port_enable_cfg, NULL, "Port #%d", i+1);
       proto_tree_add_item(port_tree, hf_time_sync_port_enable_cfg_port_num, tvb, offset+2+i*4, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_enable_cfg_port_enable, tvb, offset+4+i*4, 2, ENC_LITTLE_ENDIAN);
   }

   return 2+num_ports*4;
}

static int dissect_time_sync_port_log_announce(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint16 i, num_ports;
   proto_tree* port_tree;

   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_log_announce);
      return total_len;
   }

   num_ports = tvb_get_letohs( tvb, offset);
   proto_tree_add_item( tree, hf_time_sync_port_log_announce_num_ports, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   if (2+num_ports*4 > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_log_announce_ports);
      return total_len;
   }

   for (i = 0; i < num_ports; i++)
   {
       port_tree = proto_tree_add_subtree_format(tree, tvb, offset+2+i*4, 4, ett_time_sync_port_log_announce, NULL, "Port #%d", i+1);
       proto_tree_add_item(port_tree, hf_time_sync_port_log_announce_port_num, tvb, offset+2+i*4, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_log_announce_interval, tvb, offset+4+i*4, 2, ENC_LITTLE_ENDIAN);
   }

   return 2+num_ports*4;
}

static int dissect_time_sync_port_log_sync(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint16 i, num_ports;
   proto_tree* port_tree;

   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_log_sync);
      return total_len;
   }

   num_ports = tvb_get_letohs( tvb, offset);
   proto_tree_add_item( tree, hf_time_sync_port_log_sync_num_ports, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   if (2+num_ports*4 > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_log_sync_ports);
      return total_len;
   }

   for (i = 0; i < num_ports; i++)
   {
       port_tree = proto_tree_add_subtree_format(tree, tvb, offset+2+i*4, 4, ett_time_sync_port_log_sync, NULL, "Port #%d", i+1);
       proto_tree_add_item(port_tree, hf_time_sync_port_log_sync_port_num, tvb, offset+2+i*4, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_log_sync_port_log_sync_interval, tvb, offset+4+i*4, 2, ENC_LITTLE_ENDIAN);
   }

   return 2+num_ports*4;
}

static int dissect_time_sync_clock_type(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_clock_type);
      return total_len;
   }

   static int* const bits[] = {
      &hf_time_sync_clock_type_management,
      &hf_time_sync_clock_type_end_to_end,
      &hf_time_sync_clock_type_boundary,
      &hf_time_sync_clock_type_ordinary,
      &hf_time_sync_clock_type_slave_only,
      NULL
   };
   proto_tree_add_bitmask(tree, tvb, offset, hf_time_sync_clock_type, ett_time_sync_clock_type, bits, ENC_LITTLE_ENDIAN);

   return 2;
}

static int dissect_time_sync_manufacture_id(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   if (total_len < 4)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_manufacture_id);
      return total_len;
   }

   proto_tree_add_item( tree, hf_time_sync_manufacture_id_oui, tvb, offset, 3, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( tree, hf_time_sync_manufacture_id_reserved, tvb, offset+3, 1, ENC_LITTLE_ENDIAN);
   return 4;
}

static int dissect_time_sync_prod_desc(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint32 size;

   if (total_len < 4)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_prod_desc);
      return total_len;
   }

   proto_tree_add_item_ret_uint( tree, hf_time_sync_prod_desc_size, tvb, offset, 4, ENC_LITTLE_ENDIAN, &size);

   if (size > 64)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_prod_desc_64);
      return total_len;
   }

   if ((int)(size+4) > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_prod_desc_size);
      return total_len;
   }

   proto_tree_add_item( tree, hf_time_sync_prod_desc_str, tvb, offset+4, size, ENC_ASCII);
   return size+4;
}

static int dissect_time_sync_revision_data(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint32 size;

   if (total_len < 4)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_revision_data);
      return total_len;
   }

   proto_tree_add_item_ret_uint( tree, hf_time_sync_revision_data_size, tvb, offset, 4, ENC_LITTLE_ENDIAN, &size);

   if (size > 32)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_revision_data_32);
      return total_len;
   }

   if ((int)(size+4) > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_revision_data_size);
      return total_len;
   }

   proto_tree_add_item( tree, hf_time_sync_revision_data_str, tvb, offset+4, size, ENC_ASCII);
   return size+4;
}

static int dissect_time_sync_user_desc(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint32 size;

   if (total_len < 4)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_user_desc);
      return total_len;
   }

   proto_tree_add_item_ret_uint( tree, hf_time_sync_user_desc_size, tvb, offset, 4, ENC_LITTLE_ENDIAN, &size);

   if (size > 128)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_user_desc_128);
      return total_len;
   }

   if ((int)(size+4) > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_user_desc_size);
      return total_len;
   }

   proto_tree_add_item( tree, hf_time_sync_user_desc_str, tvb, offset+4, size, ENC_ASCII);
   return size+4;
}

static int dissect_time_sync_port_profile_id_info(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint16 i, num_ports;
   proto_tree* port_tree;

   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_profile_id_info);
      return total_len;
   }

   num_ports = tvb_get_letohs( tvb, offset);
   proto_tree_add_item( tree, hf_time_sync_port_profile_id_info_num_ports, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   if (2+num_ports*10 > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_profile_id_info_ports);
      return total_len;
   }

   for (i = 0; i < num_ports; i++)
   {
       port_tree = proto_tree_add_subtree_format(tree, tvb, offset+2+i*10, 10, ett_time_sync_port_profile_id_info, NULL, "Port #%d", i+1);
       proto_tree_add_item(port_tree, hf_time_sync_port_profile_id_info_port_num, tvb, offset+2+i*10, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_profile_id_info_profile_id, tvb, offset+4+i*10, 8, ENC_NA);
   }

   return 2+num_ports*10;
}

static int dissect_time_sync_port_phys_addr_info(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint16 i, num_ports;
   proto_tree* port_tree;

   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_phys_addr_info);
      return total_len;
   }

   num_ports = tvb_get_letohs( tvb, offset);
   proto_tree_add_item( tree, hf_time_sync_port_phys_addr_info_num_ports, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   if (2+num_ports*36 > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_phys_addr_info_ports);
      return total_len;
   }

   for (i = 0; i < num_ports; i++)
   {
       port_tree = proto_tree_add_subtree_format(tree, tvb, offset+2+i*36, 36, ett_time_sync_port_phys_addr_info, NULL, "Port #%d", i+1);
       proto_tree_add_item(port_tree, hf_time_sync_port_phys_addr_info_port_num, tvb, offset+2+i*36, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_phys_addr_info_phys_proto, tvb, offset+4+i*36, 16, ENC_ASCII);

       guint32 addr_size;
       proto_tree_add_item_ret_uint(port_tree, hf_time_sync_port_phys_addr_info_addr_size, tvb, offset+20+i*36, 2, ENC_LITTLE_ENDIAN, &addr_size);

       // Field is 16 bytes, but only highlight the actual size.
       proto_tree_add_item(port_tree, hf_time_sync_port_phys_addr_info_phys_addr, tvb, offset+22+i*36, addr_size, ENC_NA);
   }

   return 2+num_ports*36;
}

static int dissect_time_sync_port_proto_addr_info(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   guint16 i, num_ports;
   proto_tree* port_tree;

   if (total_len < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_proto_addr_info);
      return total_len;
   }

   num_ports = tvb_get_letohs( tvb, offset);
   proto_tree_add_item( tree, hf_time_sync_port_proto_addr_info_num_ports, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   if (2+num_ports*22 > total_len)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_port_proto_addr_info_ports);
      return total_len;
   }

   for (i = 0; i < num_ports; i++)
   {
       port_tree = proto_tree_add_subtree_format(tree, tvb, offset+2+i*22, 22, ett_time_sync_port_proto_addr_info, NULL, "Port #%d", i+1);
       proto_tree_add_item(port_tree, hf_time_sync_port_proto_addr_info_port_num, tvb, offset+2+i*22, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_proto_addr_info_network_proto, tvb, offset+4+i*22, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_proto_addr_info_addr_size, tvb, offset+6+i*22, 2, ENC_LITTLE_ENDIAN);
       proto_tree_add_item(port_tree, hf_time_sync_port_proto_addr_info_port_proto_addr, tvb, offset+8+i*22, 16, ENC_NA);
   }

   return 2+num_ports*22;
}

static int dissect_time_sync_sys_time_and_offset(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                             int offset, int total_len)
{
   if (total_len < 16)
   {
      expert_add_info(pinfo, item, &ei_mal_time_sync_sys_time_and_offset);
      return total_len;
   }

   dissect_cip_utime(tree, tvb, offset, hf_time_sync_sys_time_and_offset_time);
   proto_tree_add_item( tree, hf_time_sync_sys_time_and_offset_offset, tvb, offset+8, 8, ENC_LITTLE_ENDIAN);

   return 16;
}

int dissect_optional_attr_list(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
   int offset, int total_len)
{
   guint32 i;
   guint32 num_attr = 0;

   proto_tree_add_item_ret_uint(tree, hf_attr_class_opt_attr_num, tvb, offset, 2, ENC_LITTLE_ENDIAN, &num_attr);

   if (total_len < (int)(2 + num_attr * 2))
   {
      expert_add_info(pinfo, item, &ei_mal_opt_attr_list);
      return total_len;
   }

   // Look up the request data to get the CIP Class.
   cip_req_info_t *cip_req_info;
   cip_req_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);

   for (i = 0; i < num_attr; ++i)
   {
      proto_item* attr_item = proto_tree_add_item(tree, hf_attr_class_attr_num, tvb, offset + 2 + 2 * i, 2, ENC_LITTLE_ENDIAN);

      // Display attribute name.
      if (cip_req_info && cip_req_info->ciaData)
      {
          attribute_info_t* attr;
          attr = cip_get_attribute(cip_req_info->ciaData->iClass, 1, i);
          if (attr)
          {
              proto_item_append_text(attr_item, " (%s)", attr->text);
          }
      }
   }

   return 2 + num_attr * 2;
}

int dissect_optional_service_list(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
   int offset, int total_len)
{
   guint32 i;
   guint32 num_services = 0;

   proto_tree_add_item_ret_uint(tree, hf_attr_class_opt_service_num, tvb, offset, 2, ENC_LITTLE_ENDIAN, &num_services);

   if (total_len < (int)(2 + num_services * 2))
   {
      expert_add_info(pinfo, item, &ei_mal_opt_service_list);
      return total_len;
   }

   for (i = 0; i < num_services; ++i)
   {
      proto_tree_add_item(tree, hf_attr_class_service_code, tvb, offset + 2 + 2 * i, 2, ENC_LITTLE_ENDIAN);
   }

   return 2 + num_services * 2;
}

static int dissect_port_instance_info(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
   int offset, int total_len)
{
   int i;

   for (i = 0; i < total_len; i += 4)
   {
      proto_tree_add_item(tree, hf_port_type, tvb, offset + i, 2, ENC_LITTLE_ENDIAN);
      proto_tree_add_item(tree, hf_port_number, tvb, offset + i + 2, 2, ENC_LITTLE_ENDIAN);
   }

   return total_len;
}

static int dissect_port_associated_comm_objects(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
    int offset, int total_len _U_)
{
    guint32 num_entries;
    proto_tree_add_item_ret_uint(tree, hf_port_num_comm_object_entries, tvb, offset, 1, ENC_LITTLE_ENDIAN, &num_entries);

    int parsed_len = 1;
    for (guint32 i = 0; i < num_entries; ++i)
    {
        parsed_len += dissect_padded_epath_len_usint(pinfo, tree, item, tvb, offset + parsed_len,
            tvb_reported_length_remaining(tvb, offset + parsed_len));
    }

    return parsed_len;
}

static int dissect_padded_epath_len(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
   int offset, int total_len, gboolean one_byte_len)
{
   guint32     path_size;
   proto_tree *epath_tree;
   proto_item *path_item;

   guint32 path_size_len;
   int hf_path_len;
   if (one_byte_len == TRUE)
   {
      path_size_len = 1;
      hf_path_len = hf_path_len_usint;
   }
   else
   {
      path_size_len = 2;
      hf_path_len = hf_path_len_uint;
   }

   path_item = proto_tree_add_item_ret_uint(tree, hf_path_len, tvb, offset, path_size_len, ENC_LITTLE_ENDIAN, &path_size);

   if (total_len < (int)(path_size * 2 + path_size_len))
   {
      expert_add_info(pinfo, item, &ei_mal_padded_epath_size);
      return total_len;
   }

   epath_tree = proto_tree_add_subtree(tree, tvb, offset + path_size_len, path_size * 2, ett_path, &path_item, "Path: ");
   dissect_epath(tvb, pinfo, epath_tree, path_item, offset + path_size_len, path_size * 2, FALSE, FALSE, NULL, NULL, NO_DISPLAY, NULL, FALSE);

   return path_size * 2 + path_size_len;
}

/* Format: USINT (Length of EPATH in 16-bit words) + Padded EPATH */
int dissect_padded_epath_len_usint(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
   int offset, int total_len)
{
   return dissect_padded_epath_len(pinfo, tree, item, tvb, offset, total_len, TRUE);
}

/* Format: UINT (Length of EPATH in 16-bit words) + Padded EPATH */
int dissect_padded_epath_len_uint(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
   int offset, int total_len)
{
   return dissect_padded_epath_len(pinfo, tree, item, tvb, offset, total_len, FALSE);
}

static int dissect_single_segment_packed_attr(packet_info *pinfo, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
   int offset, int total_len _U_)
{
   proto_tree *subtree;
   proto_item *subitem;
   subtree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_port_path, &subitem, "Path: ");

   int parsed_len = dissect_cip_segment_single(pinfo, tvb, offset, subtree, subitem, FALSE, TRUE, NULL, NULL, NO_DISPLAY, NULL, FALSE);
   proto_item_set_len(subitem, parsed_len);

   return parsed_len;
}

static int dissect_single_segment_padded_attr(packet_info *pinfo, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
   int offset, int total_len _U_)
{
   proto_tree *subtree;
   proto_item *subitem;
   subtree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_port_path, &subitem, "Path: ");

   int parsed_len = dissect_cip_segment_single(pinfo, tvb, offset, subtree, subitem, FALSE, FALSE, NULL, NULL, NO_DISPLAY, NULL, FALSE);
   proto_item_set_len(subitem, parsed_len);

   return parsed_len;
}

static int dissect_port_link_object(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
   int offset, int total_len)
{
   return dissect_padded_epath_len_uint(pinfo, tree, item, tvb, offset, total_len);
}

static int dissect_port_node_range(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
   int offset, int total_len _U_)
{
   proto_tree_add_item(tree, hf_port_min_node_num, tvb, offset, 2, ENC_LITTLE_ENDIAN);
   proto_tree_add_item(tree, hf_port_max_node_num, tvb, offset + 2, 2, ENC_LITTLE_ENDIAN);

   return 4;
}


/// Identity - Services
static int dissect_identity_reset(packet_info *pinfo _U_, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb, int offset, gboolean request)
{
   int parsed_len = 0;

   if (request)
   {
      if (tvb_reported_length_remaining(tvb, offset) > 0)
      {
         proto_tree_add_item(tree, hf_cip_sc_reset_param, tvb, offset, 1, ENC_LITTLE_ENDIAN);
         parsed_len = 1;
      }
   }
   else
   {
      parsed_len = 0;
   }

   return parsed_len;
}

static attribute_info_t cip_attribute_vals[] = {
    /* Identity Object (class attributes) */
   {0x01, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
   {0x01, TRUE, 2, 1, CLASS_ATTRIBUTE_2_NAME, cip_uint, &hf_attr_class_max_instance, NULL },
   {0x01, TRUE, 3, -1, CLASS_ATTRIBUTE_3_NAME, cip_uint, &hf_attr_class_num_instance, NULL },
   {0x01, TRUE, 4, -1, CLASS_ATTRIBUTE_4_NAME, cip_dissector_func, NULL, dissect_optional_attr_list },
   {0x01, TRUE, 5, -1, CLASS_ATTRIBUTE_5_NAME, cip_dissector_func, NULL, dissect_optional_service_list },
   {0x01, TRUE, 6, 2, CLASS_ATTRIBUTE_6_NAME, cip_uint, &hf_attr_class_num_class_attr, NULL },
   {0x01, TRUE, 7, 3, CLASS_ATTRIBUTE_7_NAME, cip_uint, &hf_attr_class_num_inst_attr, NULL },

    /* Identity Object (instance attributes) */
   {0x01, FALSE, 1, 0, "Vendor ID", cip_uint, &hf_id_vendor_id, NULL},
   {0x01, FALSE, 2, 1, "Device Type", cip_uint, &hf_id_device_type, NULL},
   {0x01, FALSE, 3, 2, "Product Code", cip_uint, &hf_id_product_code, NULL},
   {0x01, FALSE, 4, 3, "Revision", cip_dissector_func, NULL, dissect_id_revision},
   {0x01, FALSE, 5, 4, "Status", cip_dissector_func, NULL, dissect_id_status},
   {0x01, FALSE, 6, 5, "Serial Number", cip_udint, &hf_id_serial_number, NULL},
   {0x01, FALSE, 7, 6, "Product Name", cip_short_string, &hf_id_product_name, NULL},
   {0x01, FALSE, 8, 7, "State", cip_usint, &hf_id_state, NULL},
   {0x01, FALSE, 9, 8, "Configuration Consistency Value", cip_uint, &hf_id_config_value, NULL},
   {0x01, FALSE, 10, 9, "Heartbeat Interval", cip_usint, &hf_id_heartbeat, NULL},

    /* Message Router Object (class attributes) */
   {0x02, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
   {0x02, TRUE, 2, -1, CLASS_ATTRIBUTE_2_NAME, cip_uint, &hf_attr_class_max_instance, NULL },
   {0x02, TRUE, 3, -1, CLASS_ATTRIBUTE_3_NAME, cip_uint, &hf_attr_class_num_instance, NULL },
   {0x02, TRUE, 4, 1, CLASS_ATTRIBUTE_4_NAME, cip_dissector_func, NULL, dissect_optional_attr_list },
   {0x02, TRUE, 5, 2, CLASS_ATTRIBUTE_5_NAME, cip_dissector_func, NULL, dissect_optional_service_list },
   {0x02, TRUE, 6, 3, CLASS_ATTRIBUTE_6_NAME, cip_uint, &hf_attr_class_num_class_attr, NULL },
   {0x02, TRUE, 7, 4, CLASS_ATTRIBUTE_7_NAME, cip_uint, &hf_attr_class_num_inst_attr, NULL },

    /* Message Router Object (instance attributes) */
   {0x02, FALSE, 1, 0, "Object List", cip_dissector_func, NULL, dissect_msg_rout_num_classes},
   {0x02, FALSE, 2, 1, "Number Available", cip_uint, &hf_msg_rout_num_available, NULL},
   {0x02, FALSE, 3, 2, "Number Active", cip_uint, &hf_msg_rout_num_active, NULL},
   {0x02, FALSE, 4, 3, "Active Connections", cip_uint_array, &hf_msg_rout_active_connections, NULL},

    /* Connection Manager Object (class attributes) */
   {0x06, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
   {0x06, TRUE, 2, 1, CLASS_ATTRIBUTE_2_NAME, cip_uint, &hf_attr_class_max_instance, NULL },
   {0x06, TRUE, 3, -1, CLASS_ATTRIBUTE_3_NAME, cip_uint, &hf_attr_class_num_instance, NULL },
   {0x06, TRUE, 4, -1, CLASS_ATTRIBUTE_4_NAME, cip_dissector_func, NULL, dissect_optional_attr_list },
   {0x06, TRUE, 5, -1, CLASS_ATTRIBUTE_5_NAME, cip_dissector_func, NULL, dissect_optional_service_list },
   {0x06, TRUE, 6, 2, CLASS_ATTRIBUTE_6_NAME, cip_uint, &hf_attr_class_num_class_attr, NULL },
   {0x06, TRUE, 7, 3, CLASS_ATTRIBUTE_7_NAME, cip_uint, &hf_attr_class_num_inst_attr, NULL },

    /* Connection Manager Object (instance attributes) */
   {0x06, FALSE, 1, 0, "Open Requests", cip_uint, &hf_conn_mgr_open_requests, NULL},
   {0x06, FALSE, 2, 1, "Open Format Rejects", cip_uint, &hf_conn_mgr_open_format_rejects, NULL},
   {0x06, FALSE, 3, 2, "Open Resource Rejects", cip_uint, &hf_conn_mgr_open_resource_rejects, NULL},
   {0x06, FALSE, 4, 3, "Other Open Rejects", cip_uint, &hf_conn_mgr_other_open_rejects, NULL},
   {0x06, FALSE, 5, 4, "Close Requests", cip_uint, &hf_conn_mgr_close_requests, NULL},
   {0x06, FALSE, 6, 5, "Close Format Requests", cip_uint, &hf_conn_close_format_requests, NULL},
   {0x06, FALSE, 7, 6, "Close Other Requests", cip_uint, &hf_conn_mgr_close_other_requests, NULL},
   {0x06, FALSE, 8, 7, "Connection Timeouts", cip_uint, &hf_conn_mgr_conn_timouts, NULL},
   {0x06, FALSE, 9, 8, "Connection Entry List", cip_dissector_func, NULL, dissect_cm_connection_entry_list },
   {0x06, FALSE, 11, 9, "CPU Utilization", cip_uint, &hf_conn_mgr_cpu_utilization, NULL },
   {0x06, FALSE, 12, 10, "Max Buff Size", cip_udint, &hf_conn_mgr_max_buff_size, NULL },
   {0x06, FALSE, 13, 11, "Buff Size Remaining", cip_udint, &hf_conn_mgr_buff_size_remaining, NULL },

    /* File Object (instance attributes) */
   {0x37, FALSE, 4, -1, "File Name", cip_stringi, &hf_file_filename, NULL },

    /* Time Sync Object (class attributes) */
   {0x43, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
   {0x43, TRUE, 2, 1, CLASS_ATTRIBUTE_2_NAME, cip_uint, &hf_attr_class_max_instance, NULL },
   {0x43, TRUE, 3, 2, CLASS_ATTRIBUTE_3_NAME, cip_uint, &hf_attr_class_num_instance, NULL },
   {0x43, TRUE, 4, 3, CLASS_ATTRIBUTE_4_NAME, cip_dissector_func, NULL, dissect_optional_attr_list },
   {0x43, TRUE, 5, 4, CLASS_ATTRIBUTE_5_NAME, cip_dissector_func, NULL, dissect_optional_service_list },
   {0x43, TRUE, 6, 5, CLASS_ATTRIBUTE_6_NAME, cip_uint, &hf_attr_class_num_class_attr, NULL },
   {0x43, TRUE, 7, 6, CLASS_ATTRIBUTE_7_NAME, cip_uint, &hf_attr_class_num_inst_attr, NULL },

    /* Time Sync Object (instance attributes) */
   {0x43, FALSE, 1, -1, "PTP Enable", cip_bool, &hf_time_sync_ptp_enable, NULL},
   {0x43, FALSE, 2, -1, "Is Synchronized", cip_bool, &hf_time_sync_is_synchronized, NULL},
   {0x43, FALSE, 3, -1, "System Time (Microseconds)", cip_utime, &hf_time_sync_sys_time_micro, NULL},
   {0x43, FALSE, 4, -1, "System Time (Nanoseconds)", cip_stime, &hf_time_sync_sys_time_nano, NULL},
   {0x43, FALSE, 5, -1, "Offset from Master", cip_ntime, &hf_time_sync_offset_from_master, NULL},
   {0x43, FALSE, 6, -1, "Max Offset from Master", cip_ulint, &hf_time_sync_max_offset_from_master, NULL},
   {0x43, FALSE, 7, -1, "Mean Path Delay To Master", cip_ntime, &hf_time_sync_mean_path_delay_to_master, NULL},
   {0x43, FALSE, 8, -1, "Grand Master Clock Info", cip_dissector_func, NULL, dissect_time_sync_grandmaster_clock},
   {0x43, FALSE, 9, -1, "Parent Clock Info", cip_dissector_func, NULL, dissect_time_sync_parent_clock},
   {0x43, FALSE, 10, -1, "Local Clock Info", cip_dissector_func, NULL, dissect_time_sync_local_clock},
   {0x43, FALSE, 11, -1, "Number of Ports", cip_uint, &hf_time_sync_num_ports, NULL},
   {0x43, FALSE, 12, -1, "Port State Info", cip_dissector_func, NULL, dissect_time_sync_port_state_info},
   {0x43, FALSE, 13, -1, "Port Enable Cfg", cip_dissector_func, NULL, dissect_time_sync_port_enable_cfg},
   {0x43, FALSE, 14, -1, "Port Log Announcement Interval Cfg", cip_dissector_func, NULL, dissect_time_sync_port_log_announce},
   {0x43, FALSE, 15, -1, "Port Log Sync Interval Cfg", cip_dissector_func, NULL, dissect_time_sync_port_log_sync},
   {0x43, FALSE, 16, -1, "Priority1", cip_usint, &hf_time_sync_priority1, NULL},
   {0x43, FALSE, 17, -1, "Priority2", cip_usint, &hf_time_sync_priority2, NULL},
   {0x43, FALSE, 18, -1, "Domain number", cip_usint, &hf_time_sync_domain_number, NULL},
   {0x43, FALSE, 19, -1, "Clock Type", cip_dissector_func, NULL, dissect_time_sync_clock_type},
   {0x43, FALSE, 20, -1, "Manufacture Identity", cip_dissector_func, NULL, dissect_time_sync_manufacture_id},
   {0x43, FALSE, 21, -1, "Product Description", cip_dissector_func, NULL, dissect_time_sync_prod_desc},
   {0x43, FALSE, 22, -1, "Revision Data", cip_dissector_func, NULL, dissect_time_sync_revision_data},
   {0x43, FALSE, 23, -1, "User Description", cip_dissector_func, NULL, dissect_time_sync_user_desc},
   {0x43, FALSE, 24, -1, "Port Profile Identity Info", cip_dissector_func, NULL, dissect_time_sync_port_profile_id_info},
   {0x43, FALSE, 25, -1, "Port Physical Address Info", cip_dissector_func, NULL, dissect_time_sync_port_phys_addr_info},
   {0x43, FALSE, 26, -1, "Port Protocol Address Info", cip_dissector_func, NULL, dissect_time_sync_port_proto_addr_info},
   {0x43, FALSE, 27, -1, "Steps Removed", cip_uint, &hf_time_sync_steps_removed, NULL},
   {0x43, FALSE, 28, -1, "System Time and Offset", cip_dissector_func, NULL, dissect_time_sync_sys_time_and_offset},


   /* Connection Configuration Object (class attributes) */
   /* Data sizes are different than common class attributes for some items. */
   { 0xF3, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
   { 0xF3, TRUE, 2, 1, CLASS_ATTRIBUTE_2_NAME, cip_udint, &hf_cip_class_max_inst32, NULL },
   { 0xF3, TRUE, 3, 2, CLASS_ATTRIBUTE_3_NAME, cip_udint, &hf_cip_class_num_inst32, NULL },
   { 0xF3, TRUE, 4, -1, CLASS_ATTRIBUTE_4_NAME, cip_dissector_func, NULL, dissect_optional_attr_list },
   { 0xF3, TRUE, 5, -1, CLASS_ATTRIBUTE_5_NAME, cip_dissector_func, NULL, dissect_optional_service_list },
   { 0xF3, TRUE, 6, -1, CLASS_ATTRIBUTE_6_NAME, cip_uint, &hf_attr_class_num_class_attr, NULL },
   { 0xF3, TRUE, 7, -1, CLASS_ATTRIBUTE_7_NAME, cip_uint, &hf_attr_class_num_inst_attr, NULL },
   { 0xF3, TRUE, 8, 3, "Format Number", cip_uint, &hf_cip_cco_format_number, NULL },
   { 0xF3, TRUE, 9, 4, "Edit Signature", cip_udint, &hf_cip_cco_edit_signature, NULL },

   /* Port Object (class attributes) */
   { 0xF4, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
   { 0xF4, TRUE, 2, 1, CLASS_ATTRIBUTE_2_NAME, cip_uint, &hf_attr_class_max_instance, NULL },
   { 0xF4, TRUE, 3, 2, CLASS_ATTRIBUTE_3_NAME, cip_uint, &hf_attr_class_num_instance, NULL },
   { 0xF4, TRUE, 4, -1, CLASS_ATTRIBUTE_4_NAME, cip_dissector_func, NULL, dissect_optional_attr_list },
   { 0xF4, TRUE, 5, -1, CLASS_ATTRIBUTE_5_NAME, cip_dissector_func, NULL, dissect_optional_service_list },
   { 0xF4, TRUE, 6, -1, CLASS_ATTRIBUTE_6_NAME, cip_uint, &hf_attr_class_num_class_attr, NULL },
   { 0xF4, TRUE, 7, -1, CLASS_ATTRIBUTE_7_NAME, cip_uint, &hf_attr_class_num_inst_attr, NULL },
   { 0xF4, TRUE, 8, 3, "Entry Port", cip_uint, &hf_port_entry_port, NULL },
   { 0xF4, TRUE, 9, 4, "Port Instance Info", cip_dissector_func, NULL, dissect_port_instance_info },

   /* Port Object (instance attributes) */
   { 0xF4, FALSE, 1, 0, "Port Type", cip_uint, &hf_port_type, NULL },
   { 0xF4, FALSE, 2, 1, "Port Number", cip_uint, &hf_port_number, NULL },
   { 0xF4, FALSE, 3, 2, "Link Object", cip_dissector_func, NULL, dissect_port_link_object },
   { 0xF4, FALSE, 4, 3, "Port Name", cip_short_string, &hf_port_name, NULL },
   { 0xF4, FALSE, 7, 4, "Port Number and Node Address", cip_dissector_func, NULL, dissect_single_segment_padded_attr },
   { 0xF4, FALSE, 8, -1, "Port Node Range", cip_dissector_func, NULL, dissect_port_node_range },
   { 0xF4, FALSE, 9, -1, "Chassis Identity", cip_dissector_func, NULL, dissect_single_segment_packed_attr },
   { 0xF4, FALSE, 11, -1, "Associated Communication Objects", cip_dissector_func, NULL, dissect_port_associated_comm_objects },
};

// Table of CIP services defined by this dissector.
static cip_service_info_t cip_obj_spec_service_table[] = {
    { 0x1, SC_RESET, "Reset", dissect_identity_reset },
};

// Look up a given CIP service from this dissector.
static cip_service_info_t* cip_get_service_cip(guint32 class_id, guint8 service_id)
{
   return cip_get_service_one_table(&cip_obj_spec_service_table[0],
      sizeof(cip_obj_spec_service_table) / sizeof(cip_service_info_t),
      class_id,
      service_id);
}

typedef struct attribute_val_array {
   size_t size;
   attribute_info_t* attrs;
} attribute_val_array_t;

/* Each entry in this table (eg: cip_attribute_vals) is a list of:
    Attribute information (class_id/class_instance/attribute) to attribute property

    Note: If more items are added to the individual tables, it may make sense
      to switch to a more efficient implementation (eg: hash table).
*/

static attribute_val_array_t all_attribute_vals[] = {
   {sizeof(cip_attribute_vals)/sizeof(attribute_info_t), cip_attribute_vals},
   {sizeof(enip_attribute_vals)/sizeof(attribute_info_t), enip_attribute_vals},
   {sizeof(cip_safety_attribute_vals)/sizeof(attribute_info_t), cip_safety_attribute_vals},
   {sizeof(cip_motion_attribute_vals)/sizeof(attribute_info_t), cip_motion_attribute_vals},
};

attribute_info_t* cip_get_attribute(guint class_id, guint instance, guint attribute)
{
   size_t i, j;
   attribute_val_array_t* att_array;
   attribute_info_t* pattr;

   static attribute_info_t class_attribute_vals[] = {
      { 0, TRUE, 1, -1, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
      { 0, TRUE, 2, -1, CLASS_ATTRIBUTE_2_NAME, cip_uint, &hf_attr_class_max_instance, NULL },
      { 0, TRUE, 3, -1, CLASS_ATTRIBUTE_3_NAME, cip_uint, &hf_attr_class_num_instance, NULL },
      { 0, TRUE, 4, -1, CLASS_ATTRIBUTE_4_NAME, cip_dissector_func, NULL, dissect_optional_attr_list },
      { 0, TRUE, 5, -1, CLASS_ATTRIBUTE_5_NAME, cip_dissector_func, NULL, dissect_optional_service_list },
      { 0, TRUE, 6, -1, CLASS_ATTRIBUTE_6_NAME, cip_uint, &hf_attr_class_num_class_attr, NULL },
      { 0, TRUE, 7, -1, CLASS_ATTRIBUTE_7_NAME, cip_uint, &hf_attr_class_num_inst_attr, NULL },
   };

   for (i = 0; i < sizeof(all_attribute_vals)/sizeof(attribute_val_array_t); i++)
   {
      att_array = &all_attribute_vals[i];
      for (j = 0; j < att_array->size; j++)
      {
         pattr = &att_array->attrs[j];
         if ((pattr->class_id == class_id) &&
             (instance != SEGMENT_VALUE_NOT_SET) &&
             (((instance == 0) && (pattr->class_instance == TRUE)) || ((instance != 0) && (pattr->class_instance == FALSE))) &&
             (pattr->attribute == attribute))
         {
            return pattr;
         }
      }
   }

   /* Check against common class attributes. */
   if (instance == 0)
   {
      for (i = 0; i < sizeof(class_attribute_vals) / sizeof(attribute_info_t); i++)
      {
         pattr = &class_attribute_vals[i];
         if (pattr->attribute == attribute)
         {
            return pattr;
         }
      }
   }

   return NULL;
}

// Look up a given CIP service from a table of cip_service_info_t.
cip_service_info_t* cip_get_service_one_table(cip_service_info_t* services, size_t size, guint32 class_id, guint8 service_id)
{
   for (guint32 i = 0; i < size; i++)
   {
      cip_service_info_t* entry = &services[i];
      if (entry->class_id == class_id && entry->service_id == (service_id & CIP_SC_MASK))
      {
         return entry;
      }
   }

   return NULL;
}

// Look through all CIP Service tables from different dissectors, to find a definition for a given CIP service.
static cip_service_info_t* cip_get_service(packet_info *pinfo, guint8 service_id)
{
   cip_req_info_t *cip_req_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
   if (!cip_req_info || !cip_req_info->ciaData)
   {
      return NULL;
   }

   cip_service_info_t* pService = cip_get_service_cip(cip_req_info->ciaData->iClass, service_id);
   if (pService)
   {
      return pService;
   }

   pService = cip_get_service_enip(cip_req_info->ciaData->iClass, service_id);
   if (pService)
   {
      return pService;
   }

   return NULL;
}

static const char *
segment_name_format(const char *segment_name, const char *fmt)
    G_GNUC_FORMAT(2);

static const char *
segment_name_format(const char *segment_name, const char *fmt)
{
   wmem_strbuf_t *strbuf;

   strbuf = wmem_strbuf_new(wmem_packet_scope(), segment_name);
   wmem_strbuf_append(strbuf, fmt);
   return wmem_strbuf_get_str(strbuf);
}

static int
dissect_cia(tvbuff_t *tvb, int offset, unsigned char segment_type,
            gboolean generate, gboolean packed, packet_info *pinfo, proto_item *epath_item,
            proto_tree *path_tree, proto_item *path_item, proto_item ** ret_item,
            const char* segment_name, const value_string* vals, int* value,
            int hf8, int hf16, int hf32)
{
   unsigned char logical_format;
   int segment_len;
   int temp_data;
   int value_offset;
   wmem_strbuf_t *strbuf;
   gboolean extended_logical = FALSE;
   guint8 logical_seg_type = segment_type & CI_LOGICAL_SEG_TYPE_MASK;

   /* Extended Logical Format is slightly different than other logical formats. An extra byte is
      inserted after the segment type. */
   if (logical_seg_type == CI_LOGICAL_SEG_EXT_LOGICAL)
   {
      extended_logical = TRUE;

      if (generate)
      {
         temp_data = tvb_get_guint8(tvb, offset + 1);
         *ret_item = proto_tree_add_uint(path_tree, hf_cip_ext_logical_type, tvb, 0, 0, temp_data);
         proto_item_set_generated(*ret_item);
      }
      else
      {
         *ret_item = proto_tree_add_item(path_tree, hf_cip_ext_logical_type, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
      }
   }

   logical_format = segment_type & CI_LOGICAL_SEG_FORMAT_MASK;
   switch (logical_format)
   {
   case CI_LOGICAL_SEG_8_BIT:
      value_offset = offset + 1;

      if (extended_logical == TRUE)
      {
         value_offset += 1;
      }

      temp_data = tvb_get_guint8(tvb, value_offset);

      if ( generate )
      {
         *ret_item = proto_tree_add_uint(path_tree, hf8, tvb, 0, 0, temp_data );
         proto_item_set_generated(*ret_item);
      }
      else
      {
         *ret_item = proto_tree_add_item(path_tree, hf8, tvb, value_offset, 1, ENC_LITTLE_ENDIAN);
      }

      if (vals == NULL)
      {
         if (logical_seg_type == CI_LOGICAL_SEG_ATTR_ID)
         {
            proto_item_append_text(epath_item, "%s: %d", segment_name, temp_data);
         }
         else
         {
            proto_item_append_text(epath_item, "%s: 0x%02X", segment_name, temp_data);
         }
      }
      else
      {
         proto_item_append_text( epath_item, "%s", val_to_str( temp_data, vals, segment_name_format( segment_name, ": 0x%02X" ) ) );
      }

      if (value != NULL)
         *value = temp_data;

      segment_len = 2;
      if (extended_logical == TRUE)
      {
         if (packed)
         {
            segment_len += 1;
         }
         else
         {
            segment_len += 2;
         }
      }
      break;
   case CI_LOGICAL_SEG_16_BIT:
      if (packed && extended_logical == FALSE)
      {
         value_offset = offset + 1;
         segment_len = 3;
      }
      else
      {
         value_offset = offset + 2;
         segment_len = 4;
      }

      temp_data = tvb_get_letohs(tvb, value_offset);

      if ( generate )
      {
         *ret_item = proto_tree_add_uint(path_tree, hf16, tvb, 0, 0, temp_data );
         proto_item_set_generated(*ret_item);
      }
      else
      {
         *ret_item = proto_tree_add_item(path_tree, hf16, tvb, value_offset, 2, ENC_LITTLE_ENDIAN);
      }

      if (vals == NULL)
      {
         if (logical_seg_type == CI_LOGICAL_SEG_ATTR_ID)
         {
            proto_item_append_text(epath_item, "%s: %d", segment_name, temp_data);
         }
         else
         {
            proto_item_append_text(epath_item, "%s: 0x%04X", segment_name, temp_data);
         }
      }
      else
      {
         strbuf = wmem_strbuf_new(wmem_packet_scope(), segment_name);
         wmem_strbuf_append(strbuf, ": 0x%04X");

         proto_item_append_text( epath_item, "%s", val_to_str( temp_data, vals, segment_name_format( segment_name, ": 0x%04X" ) ) );
      }

      if (value != NULL)
         *value = temp_data;

      break;
   case CI_LOGICAL_SEG_32_BIT:
      if (packed && extended_logical == FALSE)
      {
         value_offset = offset + 1;
         segment_len = 5;
      }
      else
      {
         value_offset = offset + 2;
         segment_len = 6;
      }
      temp_data = tvb_get_letohl(tvb, value_offset);

      if ( generate )
      {
         *ret_item = proto_tree_add_uint(path_tree, hf32, tvb, 0, 0, temp_data );
         proto_item_set_generated(*ret_item);
      }
      else
      {
         *ret_item = proto_tree_add_item(path_tree, hf32, tvb, value_offset, 4, ENC_LITTLE_ENDIAN);
      }

      if (vals == NULL)
      {
         if (logical_seg_type == CI_LOGICAL_SEG_ATTR_ID)
         {
            proto_item_append_text(epath_item, "%s: %d", segment_name, temp_data);
         }
         else
         {
            proto_item_append_text(epath_item, "%s: 0x%08X", segment_name, temp_data);
         }
      }
      else
      {
         strbuf = wmem_strbuf_new(wmem_packet_scope(), segment_name);
         wmem_strbuf_append(strbuf, ": 0x%08X");

         proto_item_append_text( epath_item, "%s", val_to_str( temp_data, vals, segment_name_format( segment_name, ": 0x%08X" ) ) );
      }

      if (value != NULL)
         *value = temp_data;

      break;
   default:
      expert_add_info(pinfo, epath_item, &ei_proto_log_seg_format);
      return 0;
   }

   if (generate == FALSE)
   {
      proto_item_set_len(path_item, segment_len);
   }

   return segment_len;
}

/* Dissect Device ID structure */
void
dissect_deviceid(tvbuff_t *tvb, int offset, proto_tree *tree,
                 int hf_vendor, int hf_devtype, int hf_prodcode,
                 int hf_compatibility, int hf_comp_bit, int hf_majrev, int hf_minrev,
                 gboolean generate, guint encoding)
{
   proto_item* vendor_id_item = proto_tree_add_item(tree, hf_vendor, tvb, offset, 2, encoding);
   proto_item* device_type_item = proto_tree_add_item(tree, hf_devtype, tvb, offset + 2, 2, encoding);
   proto_item* product_code_item = proto_tree_add_item(tree, hf_prodcode, tvb, offset + 4, 2, encoding);

   /* Major revision/Compatibility */
   guint8 compatibility = tvb_get_guint8(tvb, offset + 6);

   /* Add Major revision/Compatibility tree */
   proto_item* compatibility_item = proto_tree_add_uint_format_value(tree, hf_compatibility,
      tvb, offset + 6, 1, compatibility, "%s, Major Revision: %d",
      val_to_str_const((compatibility & 0x80) >> 7, cip_com_bit_vals, ""),
      compatibility & 0x7F);
   proto_tree* compatibility_tree = proto_item_add_subtree(compatibility_item, ett_mcsc);

   proto_item* comp_bit_item = proto_tree_add_item(compatibility_tree, hf_comp_bit, tvb, offset + 6, 1, encoding);
   proto_item* major_rev_item = proto_tree_add_item(compatibility_tree, hf_majrev, tvb, offset + 6, 1, encoding);
   proto_item* minor_rev_item = proto_tree_add_item(tree, hf_minrev, tvb, offset + 7, 1, encoding);

   if (generate)
   {
      proto_item_set_generated(vendor_id_item);
      proto_item_set_generated(device_type_item);
      proto_item_set_generated(product_code_item);
      proto_item_set_generated(compatibility_item);
      proto_item_set_generated(comp_bit_item);
      proto_item_set_generated(major_rev_item);
      proto_item_set_generated(minor_rev_item);
   }
}

static void
dissect_net_param16(tvbuff_t *tvb, int offset, proto_tree *tree,
                 int hf_net_param16, int hf_owner, int hf_type,
                 int hf_priority, int hf_fixed_var, int hf_con_size, gint ncp_ett, cip_connID_info_t* conn_info)
{
   proto_item *net_param_item;
   proto_tree *net_param_tree;

   net_param_item = proto_tree_add_item(tree, hf_net_param16, tvb, offset, 2, ENC_LITTLE_ENDIAN );
   net_param_tree = proto_item_add_subtree(net_param_item, ncp_ett);

   /* Add the data to the tree */
   proto_tree_add_item(net_param_tree, hf_owner, tvb, offset, 2, ENC_LITTLE_ENDIAN );
   proto_tree_add_item_ret_uint(net_param_tree, hf_type, tvb, offset, 2, ENC_LITTLE_ENDIAN, &conn_info->type);
   proto_tree_add_item(net_param_tree, hf_priority, tvb, offset, 2, ENC_LITTLE_ENDIAN );
   proto_tree_add_item(net_param_tree, hf_fixed_var, tvb, offset, 2, ENC_LITTLE_ENDIAN );
   proto_tree_add_item(net_param_tree, hf_con_size, tvb, offset, 2, ENC_LITTLE_ENDIAN );
}

static void
dissect_net_param32(tvbuff_t *tvb, int offset, proto_tree *tree,
                 int hf_net_param16, int hf_owner, int hf_type,
                 int hf_priority, int hf_fixed_var, int hf_con_size, gint ncp_ett, cip_connID_info_t* conn_info)
{
   proto_item *net_param_item;
   proto_tree *net_param_tree;

   net_param_item = proto_tree_add_item(tree, hf_net_param16, tvb, offset, 4, ENC_LITTLE_ENDIAN );
   net_param_tree = proto_item_add_subtree(net_param_item, ncp_ett);

   /* Add the data to the tree */
   proto_tree_add_item(net_param_tree, hf_owner, tvb, offset, 4, ENC_LITTLE_ENDIAN );
   proto_tree_add_item_ret_uint(net_param_tree, hf_type, tvb, offset, 4, ENC_LITTLE_ENDIAN, &conn_info->type);
   proto_tree_add_item(net_param_tree, hf_priority, tvb, offset, 4, ENC_LITTLE_ENDIAN );
   proto_tree_add_item(net_param_tree, hf_fixed_var, tvb, offset, 4, ENC_LITTLE_ENDIAN );
   proto_tree_add_item(net_param_tree, hf_con_size, tvb, offset, 4, ENC_LITTLE_ENDIAN );
}

static void
dissect_transport_type_trigger(tvbuff_t *tvb, int offset, proto_tree *tree,
                 int hf_ttt, int hf_direction, int hf_trigger, int hf_class, gint ett)
{
   int* const bits[] = {
      &hf_direction,
      &hf_trigger,
      &hf_class,
      NULL
   };

   proto_tree_add_bitmask(tree, tvb, offset, hf_ttt, ett, bits, ENC_LITTLE_ENDIAN);
}

static int dissect_segment_network_extended(packet_info *pinfo, proto_item *epath_item, tvbuff_t *tvb, int offset, gboolean generate, proto_tree *net_tree)
{
   int data_words;
   data_words = tvb_get_guint8(tvb, offset + 1);

   if (generate)
   {
      proto_item *it;
      guint16 temp_data;

      it = proto_tree_add_uint(net_tree, hf_cip_seg_network_size, tvb, 0, 0, data_words);
      proto_item_set_generated(it);

      temp_data = tvb_get_letohs(tvb, offset + 2);
      it = proto_tree_add_uint(net_tree, hf_cip_seg_network_subtype, tvb, 0, 0, temp_data);
      proto_item_set_generated(it);
   }
   else
   {
      proto_tree_add_item(net_tree, hf_cip_seg_network_size, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
      proto_tree_add_item(net_tree, hf_cip_seg_network_subtype, tvb, offset + 2, 2, ENC_LITTLE_ENDIAN);
   }

   // Extended Network Subtype is included in the Number of Data words, so we must have at least 1.
   if (data_words < 1)
   {
      expert_add_info(pinfo, epath_item, &ei_proto_ext_network);
      return 0;
   }

   if (generate == FALSE)
   {
      /* The first word of the data is the extended segment subtype, so
         don't include that in the displayed data block. */
      int net_seg_data_offset;
      int net_seg_data_len;
      net_seg_data_offset = offset + 4;
      net_seg_data_len = (data_words - 1) * 2;

      if (tvb_reported_length_remaining(tvb, net_seg_data_offset) < net_seg_data_len)
      {
          expert_add_info(pinfo, epath_item, &ei_proto_ext_network);
          return 0;
      }

      guint16 net_seg_subtype = tvb_get_letohs(tvb, offset + 2);

      int data_len_parsed = 0;
      switch (net_seg_subtype)
      {
      case CI_CONCURRENT_EXTENDED_NETWORK_SEG:
      {
         data_len_parsed = 4;
         data_len_parsed += dissect_concurrent_connection_network_segment(pinfo, tvb, offset + data_len_parsed, net_tree);
         break;
      }

      default:
      {
         break;
      }
      }

      if (net_seg_data_len - data_len_parsed > 0)
      {
         proto_tree_add_item(net_tree, hf_cip_data, tvb, net_seg_data_offset + data_len_parsed, net_seg_data_len - data_len_parsed, ENC_NA);
      }
   }

   return data_words * 2 + 2;
}

static int dissect_segment_network_production_inhibit_us(tvbuff_t *tvb, int offset, gboolean generate, proto_tree *net_tree)
{
   int data_words;
   guint32 inhibit_time;

   data_words = tvb_get_guint8(tvb, offset + 1);
   inhibit_time = tvb_get_letohl(tvb, offset + 2);

   if (generate == TRUE)
   {
      proto_item *it;
      it = proto_tree_add_uint(net_tree, hf_cip_seg_network_size, tvb, 0, 0, data_words);
      proto_item_set_generated(it);

      it = proto_tree_add_uint(net_tree, hf_cip_seg_prod_inhibit_time_us, tvb, 0, 0, inhibit_time);
      proto_item_set_generated(it);
   }
   else
   {
      proto_tree_add_item(net_tree, hf_cip_seg_network_size, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
      proto_tree_add_item(net_tree, hf_cip_seg_prod_inhibit_time_us,
         tvb, offset + 2, 4, ENC_LITTLE_ENDIAN);
   }

   return (data_words * 2) + 2;
}

static int dissect_segment_symbolic(tvbuff_t *tvb, proto_tree *path_seg_tree,
   proto_item *path_seg_item, proto_item *epath_item,
   int offset, gboolean generate)
{
   int seg_size;
   proto_item *it;
   guint8 symbol_size;

   symbol_size = tvb_get_guint8(tvb, offset) & 0x1F;
   if (generate)
   {
      it = proto_tree_add_uint(path_seg_tree, hf_cip_symbol_size, tvb, 0, 0, symbol_size);
      proto_item_set_generated(it);
   }
   else
   {
      proto_tree_add_item(path_seg_tree, hf_cip_symbol_size, tvb, offset, 1, ENC_LITTLE_ENDIAN);
   }

   if (symbol_size != 0)
   {
      gchar *symbol_name;
      symbol_name = tvb_format_text(wmem_packet_scope(), tvb, offset + 1, symbol_size);

      proto_item_append_text(path_seg_item, " (Symbolic Segment)");

      if (generate)
      {
         it = proto_tree_add_string(path_seg_tree, hf_cip_symbol_ascii, tvb, 0, 0, symbol_name);
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_symbol_ascii, tvb, offset + 1, symbol_size, ENC_ASCII | ENC_NA);
      }

      proto_item_append_text(epath_item, "%s", symbol_name);

      seg_size = symbol_size + 1;
   }
   else
   {
      /* Extended String */
      guint8 string_format;
      guint8 string_size;
      int data_size = 0;

      proto_item_append_text(path_seg_item, " (Extended String Symbolic Segment)");

      string_format = tvb_get_guint8(tvb, offset + 1) & CI_SYMBOL_SEG_FORMAT_MASK;
      string_size = tvb_get_guint8(tvb, offset + 1) & CI_SYMBOL_SEG_SIZE_MASK;

      if (generate)
      {
         it = proto_tree_add_uint(path_seg_tree, hf_cip_symbol_extended_format, tvb, 0, 0, string_format);
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_symbol_extended_format, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
      }

      switch (string_format)
      {
      case CI_SYMBOL_SEG_DOUBLE:
         data_size = string_size * 2;

         if (generate)
         {
            it = proto_tree_add_uint(path_seg_tree, hf_cip_symbol_double_size, tvb, 0, 0, string_size);
            proto_item_set_generated(it);
         }
         else
         {
            proto_tree_add_item(path_seg_tree, hf_cip_symbol_double_size, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(path_seg_tree, hf_cip_data, tvb, offset + 2, data_size, ENC_NA);
         }

         proto_item_append_text(epath_item, "[Data]");

         break;
      case CI_SYMBOL_SEG_TRIPLE:
         data_size = string_size * 3;

         if (generate)
         {
            it = proto_tree_add_uint(path_seg_tree, hf_cip_symbol_triple_size, tvb, 0, 0, string_size);
            proto_item_set_generated(it);
         }
         else
         {
            proto_tree_add_item(path_seg_tree, hf_cip_symbol_triple_size, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(path_seg_tree, hf_cip_data, tvb, offset + 2, data_size, ENC_NA);
         }

         proto_item_append_text(epath_item, "[Data]");

         break;
      case CI_SYMBOL_SEG_NUMERIC:
      {
         guint32 numeric_data;

         if (generate)
         {
            it = proto_tree_add_uint(path_seg_tree, hf_cip_symbol_numeric_format, tvb, 0, 0, string_size);
            proto_item_set_generated(it);
         }
         else
         {
            proto_tree_add_item(path_seg_tree, hf_cip_symbol_numeric_format, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
         }

         if (string_size == CI_SYMBOL_NUMERIC_USINT)
         {
            data_size = 1;
            numeric_data = tvb_get_guint8(tvb, offset + 2);

            if (generate)
            {
               it = proto_tree_add_uint(path_seg_tree, hf_cip_numeric_usint, tvb, 0, 0, numeric_data);
               proto_item_set_generated(it);
            }
            else
            {
               proto_tree_add_item(path_seg_tree, hf_cip_numeric_usint, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN);
            }
         }
         else if (string_size == CI_SYMBOL_NUMERIC_UINT)
         {
            data_size = 2;
            numeric_data = tvb_get_letohs(tvb, offset + 2);

            if (generate)
            {
               it = proto_tree_add_uint(path_seg_tree, hf_cip_numeric_uint, tvb, 0, 0, numeric_data);
               proto_item_set_generated(it);
            }
            else
            {
               proto_tree_add_item(path_seg_tree, hf_cip_numeric_uint, tvb, offset + 2, 2, ENC_LITTLE_ENDIAN);
            }
         }
         else if (string_size == CI_SYMBOL_NUMERIC_UDINT)
         {
            data_size = 4;
            numeric_data = tvb_get_letohl(tvb, offset + 2);

            if (generate)
            {
               it = proto_tree_add_uint(path_seg_tree, hf_cip_numeric_udint, tvb, 0, 0, numeric_data);
               proto_item_set_generated(it);
            }
            else
            {
               proto_tree_add_item(path_seg_tree, hf_cip_numeric_udint, tvb, offset + 2, 4, ENC_LITTLE_ENDIAN);
            }
         }
         else
         {
            /* Unknown Extended String Format. */
            return 0;
         }

         proto_item_append_text(epath_item, "0x%x", numeric_data);

         break;
      }
      default:
         /* Unknown Extended String Format. */
         return 0;
      }

      seg_size = 2 + data_size;
   }

   /* Add padding. */
   seg_size += seg_size % 2;

   return seg_size;
}

static int dissect_segment_port(tvbuff_t* tvb, int offset, gboolean generate,
   proto_tree* path_seg_tree, proto_item* path_seg_item, proto_item* epath_item)
{
   int segment_len = 0;
   gboolean extended_port = FALSE;
   int extended_port_offset = 0;
   guint8 segment_type = tvb_get_guint8(tvb, offset);

   /* Add Extended Link Address flag & Port Identifier*/
   if (generate)
   {
      proto_item* it = proto_tree_add_boolean(path_seg_tree, hf_cip_port_ex_link_addr, tvb, 0, 0, segment_type & CI_PORT_SEG_EX_LINK_ADDRESS);
      proto_item_set_generated(it);
      it = proto_tree_add_uint(path_seg_tree, hf_cip_port, tvb, 0, 0, (segment_type & CI_PORT_SEG_PORT_ID_MASK));
      proto_item_set_generated(it);
   }
   else
   {
      proto_tree_add_item(path_seg_tree, hf_cip_port_ex_link_addr, tvb, offset, 1, ENC_LITTLE_ENDIAN);
      proto_tree_add_item(path_seg_tree, hf_cip_port, tvb, offset, 1, ENC_LITTLE_ENDIAN);
   }

   guint8 port_id = segment_type & CI_PORT_SEG_PORT_ID_MASK;
   if (port_id == 0xF)
   {
      extended_port = TRUE;
   }

   proto_item_append_text(path_seg_item, " (Port Segment)");

   const gchar *port_name = try_val_to_str(port_id, cip_port_number_vals);
   if (port_name)
   {
      proto_item_append_text(epath_item, "Port: %s", port_name);
   }
   else
   {
      proto_item_append_text(epath_item, "Port: %d", port_id);
   }

   if (segment_type & CI_PORT_SEG_EX_LINK_ADDRESS)
   {
      int offset_link_address = 2;

      if (extended_port == TRUE)
      {
         offset_link_address += 2;
         extended_port_offset = offset + 2;
      }

      guint8 opt_link_size = tvb_get_guint8(tvb, offset + 1);

      if (generate)
      {
         /* Add size of extended link address */
         proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_link_address_size, tvb, 0, 0, opt_link_size);
         proto_item_set_generated(it);
         /* Add extended link address */
         it = proto_tree_add_string(path_seg_tree, hf_cip_link_address_string, tvb, 0, 0, tvb_format_text(wmem_packet_scope(), tvb, offset + offset_link_address, opt_link_size));
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_link_address_size, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(path_seg_tree, hf_cip_link_address_string, tvb, offset + offset_link_address, opt_link_size, ENC_ASCII | ENC_NA);
      }

      proto_item_append_text(epath_item, ", Address: %s", tvb_format_text(wmem_packet_scope(), tvb, offset + offset_link_address, opt_link_size));

      /* Pad byte */
      if (opt_link_size % 2)
      {
         segment_len = 1 + offset_link_address + opt_link_size;
      }
      else
      {
         segment_len = offset_link_address + opt_link_size;
      }
   }
   else
   {
      int offset_link_address = 1;

      segment_len = 2;

      if (extended_port == TRUE)
      {
         segment_len += 2;
         offset_link_address += 2;
         extended_port_offset = offset + 1;
      }

      /* Add Link Address */
      if (generate)
      {
         guint8 link_address_byte = tvb_get_guint8(tvb, offset + offset_link_address);
         proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_link_address_byte, tvb, 0, 0, link_address_byte);
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_link_address_byte, tvb, offset + offset_link_address, 1, ENC_LITTLE_ENDIAN);
      }

      proto_item_append_text(epath_item, ", Address: %d", tvb_get_guint8(tvb, offset + offset_link_address));
   }

   if (extended_port == TRUE)
   {
      if (generate)
      {
         guint16 port_extended = tvb_get_letohs(tvb, extended_port_offset);
         proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_port_extended, tvb, 0, 0, port_extended);
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_port_extended, tvb, extended_port_offset, 2, ENC_LITTLE_ENDIAN);
      }
   }

   if (generate == FALSE)
   {
      proto_item_set_len(path_seg_item, segment_len);
   }

   return segment_len;
}

static int dissect_segment_safety(packet_info* pinfo, tvbuff_t* tvb, int offset, gboolean generate,
   proto_tree* net_tree, cip_safety_epath_info_t* safety, cip_simple_request_info_t* req_data)
{
   guint16 seg_size = tvb_get_guint8(tvb, offset + 1) * 2;
   int segment_len = seg_size + 2;

   guint32 safety_format;
   if (generate)
   {
      safety_format = tvb_get_guint8(tvb, offset + 2);

      proto_item* it = proto_tree_add_uint(net_tree, hf_cip_seg_network_size, tvb, 0, 0, seg_size / 2);
      proto_item_set_generated(it);

      it = proto_tree_add_uint(net_tree, hf_cip_seg_safety_format, tvb, 0, 0, safety_format);
      proto_item_set_generated(it);

      /* Skip printing further information in response packets. */
      return segment_len;
   }
   else
   {
      proto_tree_add_item(net_tree, hf_cip_seg_network_size, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
      proto_tree_add_item_ret_uint(net_tree, hf_cip_seg_safety_format, tvb, offset + 2, 1, ENC_LITTLE_ENDIAN, &safety_format);
   }

   /* Safety Network Segment Format */
   if (safety_format < 3)
   {
      cip_connID_info_t ignore;
      proto_tree* safety_tree = proto_tree_add_subtree(net_tree, tvb, offset + 3, seg_size - 1,
         ett_network_seg_safety, NULL, val_to_str_const(safety_format, cip_safety_segment_format_type_vals, "Reserved"));

      gboolean has_scid = FALSE;
      guint32 ntem_value = 0;
      switch (safety_format)
      {
      case 0:
      {
         has_scid = TRUE;

         /* Target Format - Deprecated*/
         if (safety != NULL)
            safety->format = CIP_SAFETY_BASE_FORMAT;

         proto_tree_add_item(safety_tree, hf_cip_seg_safety_reserved, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_configuration_crc, tvb, offset + 4, 4, ENC_LITTLE_ENDIAN);
         dissect_cipsafety_snn(safety_tree, tvb, pinfo, offset + 8,
            hf_cip_seg_safety_configuration_timestamp, hf_cip_seg_safety_configuration_date, hf_cip_seg_safety_configuration_time);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_time_correction_epi, tvb, offset + 14, 4, ENC_LITTLE_ENDIAN);
         dissect_net_param16(tvb, offset + 18, safety_tree,
            hf_cip_seg_safety_time_correction_net_params, hf_cip_seg_safety_time_correction_own,
            hf_cip_seg_safety_time_correction_typ, hf_cip_seg_safety_time_correction_prio,
            hf_cip_seg_safety_time_correction_fixed_var, hf_cip_seg_safety_time_correction_con_size,
            ett_network_seg_safety_time_correction_net_params, &ignore);
         proto_item* it = proto_tree_add_item(safety_tree, hf_cip_seg_safety_tunid, tvb, offset + 20, 10, ENC_NA);
         dissect_unid(tvb, pinfo, offset + 20, it, "Target UNID SNN", hf_cip_seg_safety_tunid_snn_timestamp,
            hf_cip_seg_safety_tunid_snn_date, hf_cip_seg_safety_tunid_snn_time, hf_cip_seg_safety_tunid_nodeid,
            ett_cip_seg_safety_tunid, ett_cip_seg_safety_tunid_snn);
         it = proto_tree_add_item(safety_tree, hf_cip_seg_safety_ounid, tvb, offset + 30, 10, ENC_NA);
         dissect_unid(tvb, pinfo, offset + 30, it, "Originator UNID SNN", hf_cip_seg_safety_ounid_snn_timestamp,
            hf_cip_seg_safety_ounid_snn_date, hf_cip_seg_safety_ounid_snn_time, hf_cip_seg_safety_ounid_nodeid,
            ett_cip_seg_safety_ounid, ett_cip_seg_safety_ounid_snn);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_ping_epi_multiplier, tvb, offset + 40, 2, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_time_coord_msg_min_multiplier, tvb, offset + 42, 2, ENC_LITTLE_ENDIAN);
         proto_tree_add_item_ret_uint(safety_tree, hf_cip_seg_safety_network_time_expected_multiplier, tvb, offset + 44, 2, ENC_LITTLE_ENDIAN, &ntem_value);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_timeout_multiplier, tvb, offset + 46, 1, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_max_consumer_number, tvb, offset + 47, 1, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_conn_param_crc, tvb, offset + 48, 4, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_time_correction_conn_id, tvb, offset + 52, 4, ENC_LITTLE_ENDIAN);
         break;
      }
      case 1:
         /* Router Format */
         if (safety != NULL)
            safety->format = CIP_SAFETY_BASE_FORMAT;

         proto_tree_add_item(safety_tree, hf_cip_seg_safety_reserved, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_time_correction_conn_id, tvb, offset + 4, 4, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_time_correction_epi, tvb, offset + 8, 4, ENC_LITTLE_ENDIAN);
         dissect_net_param16(tvb, offset + 12, safety_tree,
            hf_cip_seg_safety_time_correction_net_params, hf_cip_seg_safety_time_correction_own,
            hf_cip_seg_safety_time_correction_typ, hf_cip_seg_safety_time_correction_prio,
            hf_cip_seg_safety_time_correction_fixed_var, hf_cip_seg_safety_time_correction_con_size,
            ett_network_seg_safety_time_correction_net_params, &ignore);
         break;
      case 2:
      {
         has_scid = TRUE;

         /* Extended Format */
         if (safety != NULL)
            safety->format = CIP_SAFETY_EXTENDED_FORMAT;

         proto_tree_add_item(safety_tree, hf_cip_seg_safety_reserved, tvb, offset + 3, 1, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_configuration_crc, tvb, offset + 4, 4, ENC_LITTLE_ENDIAN);
         dissect_cipsafety_snn(safety_tree, tvb, pinfo, offset + 8,
            hf_cip_seg_safety_configuration_timestamp, hf_cip_seg_safety_configuration_date, hf_cip_seg_safety_configuration_time);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_time_correction_epi, tvb, offset + 14, 4, ENC_LITTLE_ENDIAN);
         dissect_net_param16(tvb, offset + 18, safety_tree,
            hf_cip_seg_safety_time_correction_net_params, hf_cip_seg_safety_time_correction_own,
            hf_cip_seg_safety_time_correction_typ, hf_cip_seg_safety_time_correction_prio,
            hf_cip_seg_safety_time_correction_fixed_var, hf_cip_seg_safety_time_correction_con_size,
            ett_network_seg_safety_time_correction_net_params, &ignore);
         proto_item* it = proto_tree_add_item(safety_tree, hf_cip_seg_safety_tunid, tvb, offset + 20, 10, ENC_NA);
         dissect_unid(tvb, pinfo, offset + 20, it, "Target UNID SNN", hf_cip_seg_safety_tunid_snn_timestamp,
            hf_cip_seg_safety_tunid_snn_date, hf_cip_seg_safety_tunid_snn_time, hf_cip_seg_safety_tunid_nodeid,
            ett_cip_seg_safety_tunid, ett_cip_seg_safety_tunid_snn);
         it = proto_tree_add_item(safety_tree, hf_cip_seg_safety_ounid, tvb, offset + 30, 10, ENC_NA);
         dissect_unid(tvb, pinfo, offset + 30, it, "Originator UNID SNN", hf_cip_seg_safety_ounid_snn_timestamp,
            hf_cip_seg_safety_ounid_snn_date, hf_cip_seg_safety_ounid_snn_time, hf_cip_seg_safety_ounid_nodeid,
            ett_cip_seg_safety_ounid, ett_cip_seg_safety_ounid_snn);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_ping_epi_multiplier, tvb, offset + 40, 2, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_time_coord_msg_min_multiplier, tvb, offset + 42, 2, ENC_LITTLE_ENDIAN);
         proto_tree_add_item_ret_uint(safety_tree, hf_cip_seg_safety_network_time_expected_multiplier, tvb, offset + 44, 2, ENC_LITTLE_ENDIAN, &ntem_value);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_timeout_multiplier, tvb, offset + 46, 1, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_max_consumer_number, tvb, offset + 47, 1, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_max_fault_number, tvb, offset + 48, 2, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_conn_param_crc, tvb, offset + 50, 4, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_time_correction_conn_id, tvb, offset + 54, 4, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_init_timestamp, tvb, offset + 58, 2, ENC_LITTLE_ENDIAN);
         proto_tree_add_item(safety_tree, hf_cip_seg_safety_init_rollover, tvb, offset + 60, 2, ENC_LITTLE_ENDIAN);
         break;
      }
      }  // END switch

      if (safety && req_data && has_scid)
      {
         // Check if the SCID (SCCRC + SCTS) is all zeros.
         guint32 sccrc_value = tvb_get_letohl(tvb, offset + 4);
         guint64 scts_value = tvb_get_letoh48(tvb, offset + 8);
         gboolean scid_zero = (sccrc_value == 0) && (scts_value == 0);

         if (req_data->hasSimpleData)
         {
            safety->safety_open_type = CIP_SAFETY_OPEN_TYPE1;
         }
         else if (scid_zero)
         {
            safety->safety_open_type = CIP_SAFETY_OPEN_TYPE2B;
         }
         else
         {
            safety->safety_open_type = CIP_SAFETY_OPEN_TYPE2A;
         }
      }

      if (safety)
      {
         safety->nte_value_ms = ntem_value * 0.128f;
      }
   }
   else
   {
      proto_tree_add_item(net_tree, hf_cip_seg_safety_data, tvb, offset + 3, seg_size - 1, ENC_NA);
   }

   if (safety != NULL)
   {
      safety->safety_seg = TRUE;
   }

   return segment_len;
}

static int dissect_segment_data_simple(packet_info* pinfo, tvbuff_t* tvb, int offset, gboolean generate,
   proto_tree* path_seg_tree, proto_item* path_seg_item, cip_simple_request_info_t* req_data)
{
   if (req_data)
   {
      req_data->hasSimpleData = TRUE;
   }

   guint16 seg_size = tvb_get_guint8(tvb, offset + 1) * 2;
   int segment_len = seg_size + 2;

   if (generate)
   {
      proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_data_seg_size_simple, tvb, 0, 0, seg_size / 2);
      proto_item_set_generated(it);
   }
   else
   {
      proto_tree_add_item(path_seg_tree, hf_cip_data_seg_size_simple, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
   }

   if (generate)
   {
      return segment_len;
   }

   /* Segment data  */
   if (seg_size != 0)
   {
      int parsed_data_len = 0;
      if (req_data && req_data->iClass == CI_CLS_MOTION
         && req_data->iConnPointA != SEGMENT_VALUE_NOT_SET
         && req_data->iConnPoint != SEGMENT_VALUE_NOT_SET)
      {
         parsed_data_len += dissect_motion_configuration_block(tvb, pinfo, path_seg_tree, path_seg_item, offset + 2);
      }

      int remaining_data_len = seg_size - parsed_data_len;
      if (remaining_data_len > 0)
      {
         proto_tree_add_item(path_seg_tree, hf_cip_data_seg_item, tvb, offset + 2 + parsed_data_len, remaining_data_len, ENC_NA);
      }
   }

   proto_item_set_len(path_seg_item, segment_len);

   return segment_len;
}

static int dissect_segment_ansi_extended_symbol(packet_info* pinfo, tvbuff_t* tvb, int offset,
   gboolean generate, proto_tree* path_seg_tree, proto_item* path_seg_item,
   proto_item* epath_item, int display_type,
   gboolean is_msp_item, proto_item* msp_item)
{
   /* Segment size */
   guint16 seg_size = tvb_get_guint8(tvb, offset + 1);
   if (generate)
   {
      proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_data_seg_size_extended, tvb, 0, 0, seg_size);
      proto_item_set_generated(it);
   }
   else
      proto_tree_add_item(path_seg_tree, hf_cip_data_seg_size_extended, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);

   /* Segment data  */
   if (seg_size != 0)
   {
      gchar* symbol_name = tvb_format_text(pinfo->pool, tvb, offset + 2, seg_size);

      if (generate)
      {
         proto_item* it = proto_tree_add_string(path_seg_tree, hf_cip_symbol, tvb, 0, 0, symbol_name);
         proto_item_set_generated(it);
      }
      else
         proto_tree_add_item(path_seg_tree, hf_cip_symbol, tvb, offset + 2, seg_size, ENC_ASCII | ENC_NA);

      proto_item_append_text(epath_item, "%s", symbol_name);

      if (cip_enhanced_info_column == TRUE && is_msp_item == FALSE)
      {
         add_cip_symbol_to_info_column(pinfo, symbol_name, display_type);
      }

      if (msp_item != NULL)
      {
         proto_item_append_text(msp_item, "'%s' - ", symbol_name);
      }
   }

   /* Check for pad byte */
   if (seg_size % 2)
      seg_size++;

   if (!generate)
   {
      proto_item_set_len(path_seg_item, 2 + seg_size);
   }

   return 2 + seg_size;
}

// offset - Starts with the 'Key Data' section of the Electronic Key Segment Format.
int dissect_electronic_key_format(tvbuff_t* tvb, int offset, proto_tree* tree, gboolean generate, guint8 key_format, guint encoding)
{
   int key_len;
   if (key_format == CI_E_KEY_FORMAT_VAL)
   {
      key_len = 8;
   }
   else  // CI_E_SERIAL_NUMBER_KEY_FORMAT_VAL
   {
      key_len = 12;
   }

   if (generate)
   {
      dissect_deviceid(tvb, offset, tree,
         hf_cip_ekey_vendor, hf_cip_ekey_devtype, hf_cip_ekey_prodcode,
         hf_cip_ekey_compatibility, hf_cip_ekey_comp_bit, hf_cip_ekey_majorrev, hf_cip_ekey_minorrev, TRUE, encoding);
   }
   else
   {
      dissect_deviceid(tvb, offset, tree,
         hf_cip_ekey_vendor, hf_cip_ekey_devtype, hf_cip_ekey_prodcode,
         hf_cip_ekey_compatibility, hf_cip_ekey_comp_bit, hf_cip_ekey_majorrev, hf_cip_ekey_minorrev, FALSE, encoding);

      if (key_format == CI_E_SERIAL_NUMBER_KEY_FORMAT_VAL)
      {
         proto_tree_add_item(tree, hf_cip_ekey_serial_number, tvb, offset + 8, 4, encoding);
      }
   }

   return key_len;
}

static int dissect_segment_logical_special(packet_info* pinfo, tvbuff_t* tvb, int offset,
   gboolean generate, proto_tree* path_seg_tree,
   proto_item* path_seg_item, proto_item* epath_item)
{
   int segment_len = 0;

   guint8 segment_type = tvb_get_guint8(tvb, offset);

   /* Logical Special ID, the only logical format specified is electronic key */
   if ((segment_type & CI_LOGICAL_SEG_FORMAT_MASK) == CI_LOGICAL_SEG_E_KEY)
   {
      guint8 key_format = tvb_get_guint8(tvb, offset + 1);
      if (key_format == CI_E_KEY_FORMAT_VAL || key_format == CI_E_SERIAL_NUMBER_KEY_FORMAT_VAL)
      {
         if (generate)
         {
            proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_ekey_format, tvb, 0, 0, key_format);
            proto_item_set_generated(it);
         }
         else
         {
            proto_tree_add_item(path_seg_tree, hf_cip_ekey_format, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
         }
         segment_len = 2;

         segment_len += dissect_electronic_key_format(tvb, offset + 2, path_seg_tree, generate, key_format, ENC_LITTLE_ENDIAN);

         proto_item_set_len(path_seg_item, segment_len);

         /* Add "summary" information to parent item */
         guint16 vendor_id = tvb_get_letohs(tvb, offset + 2);
         proto_item_append_text(path_seg_tree, " (VendorID: 0x%04X", vendor_id);

         guint16 device_type = tvb_get_letohs(tvb, offset + 4);
         proto_item_append_text(path_seg_tree, ", DevTyp: 0x%04X", device_type);

         guint8 major_rev = tvb_get_guint8(tvb, offset + 8);
         guint8 minor_rev = tvb_get_guint8(tvb, offset + 9);

         proto_item_append_text(path_seg_tree, ", %d.%d)", (major_rev & 0x7F), minor_rev);
         proto_item_append_text(epath_item, "[Key]");
      }
      else
      {
         expert_add_info(pinfo, epath_item, &ei_proto_electronic_key_format);
      }
   }
   else
   {
      expert_add_info(pinfo, epath_item, &ei_proto_special_segment_format);
   }

   return segment_len;
}

static int dissect_segment_network(packet_info* pinfo, tvbuff_t* tvb, int offset,
   gboolean generate, proto_tree* path_seg_tree, proto_item* path_seg_item,
   proto_item* epath_item, int display_type, cip_safety_epath_info_t* safety,
   cip_simple_request_info_t* req_data)
{
   int segment_len = 0;

   guint8 segment_type = tvb_get_guint8(tvb, offset);

   /* Network segment -Determine the segment sub-type */
   if (generate)
   {
      proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_network_seg_type, tvb, 0, 0, segment_type & CI_NETWORK_SEG_TYPE_MASK);
      proto_item_set_generated(it);
   }
   else
   {
      proto_tree_add_item(path_seg_tree, hf_cip_network_seg_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
   }

   proto_item_append_text(path_seg_item, " (%s)", val_to_str_const((segment_type & CI_NETWORK_SEG_TYPE_MASK), cip_network_segment_type_vals, "Reserved"));

   switch (segment_type & CI_NETWORK_SEG_TYPE_MASK)
   {
   case CI_NETWORK_SEG_SCHEDULE:
      if (generate)
      {
         guint8 schedule = tvb_get_guint8(tvb, offset + 1);
         proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_seg_schedule, tvb, 0, 0, schedule);
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_seg_schedule, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
      }

      segment_len = 2;
      break;

   case CI_NETWORK_SEG_FIXED_TAG:
      if (generate)
      {
         guint8 fixed_tag = tvb_get_guint8(tvb, offset + 1);
         proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_seg_fixed_tag, tvb, 0, 0, fixed_tag);
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_seg_fixed_tag, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
      }

      segment_len = 2;
      break;

   case CI_NETWORK_SEG_PROD_INHI:
      if (generate)
      {
         guint8 inhibit_time = tvb_get_guint8(tvb, offset + 1);
         proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_seg_prod_inhibit_time, tvb, 0, 0, inhibit_time);
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_seg_prod_inhibit_time, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
      }

      segment_len = 2;
      break;

   case CI_NETWORK_SEG_PROD_INHI_US:
      segment_len = dissect_segment_network_production_inhibit_us(tvb, offset, generate, path_seg_tree);
      break;

   case CI_NETWORK_SEG_EXTENDED:
      segment_len = dissect_segment_network_extended(pinfo, epath_item, tvb, offset, generate, path_seg_tree);
      proto_item_append_text(epath_item, "[Network]");
      break;

   case CI_NETWORK_SEG_SAFETY:
      proto_item_append_text(epath_item, "[Safety]");

      if (display_type == DISPLAY_CONNECTION_PATH)
      {
         col_append_str(pinfo->cinfo, COL_INFO, " [Safety]");
      }

      segment_len = dissect_segment_safety(pinfo, tvb, offset, generate, path_seg_tree, safety, req_data);
      break;

   default:
      expert_add_info(pinfo, epath_item, &ei_proto_log_sub_seg_type);
      segment_len = 0;
      break;
   } /* End of switch sub-type */

   if (generate == FALSE)
   {
      proto_item_set_len(path_seg_item, segment_len);
   }

   return segment_len;
}

static int dissect_segment_logical_service_id(packet_info* pinfo, tvbuff_t* tvb, int offset,
   gboolean generate, proto_tree* path_seg_tree, proto_item* path_seg_item, proto_item* epath_item)
{
   int segment_len = 0;

   guint8 segment_type = tvb_get_guint8(tvb, offset);

   /* Logical Service ID - the only logical format specified is 8-bit Service ID */
   if ((segment_type & CI_LOGICAL_SEG_FORMAT_MASK) == CI_LOGICAL_SEG_8_BIT)
   {
      guint8 service_id = tvb_get_guint8(tvb, offset + 1);

      if (generate)
      {
         proto_item* it = proto_tree_add_uint(path_seg_tree, hf_cip_serviceid8, tvb, 0, 0, service_id);
         proto_item_set_generated(it);
      }
      else
      {
         proto_tree_add_item(path_seg_tree, hf_cip_serviceid8, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);

         proto_item_set_len(path_seg_item, 2);
      }

      proto_item_append_text(epath_item, "Service ID: 0x%x", service_id);

      segment_len = 2;
   }
   else
   {
      expert_add_info(pinfo, epath_item, &ei_proto_log_seg_type);
   }

   return segment_len;
}

int dissect_cip_segment_single(packet_info *pinfo, tvbuff_t *tvb, int offset, proto_tree *path_tree, proto_item *epath_item,
                    gboolean generate, gboolean packed, cip_simple_request_info_t* req_data, cip_safety_epath_info_t* safety,
                    int display_type, proto_item *msp_item,
                    gboolean is_msp_item)
{
   int segment_len = 0;
   unsigned char segment_type;
   proto_tree *path_seg_tree;
   proto_item *it, *cia_ret_item;
   proto_item *path_seg_item;

   {
      if (tvb_reported_length_remaining(tvb, offset) <= 0)
      {
         expert_add_info(pinfo, epath_item, &ei_mal_incomplete_epath);
         return 0;
      }

      /* Get segment type */
      segment_type = tvb_get_guint8( tvb, offset );

      if ( generate )
      {
         path_seg_item = proto_tree_add_uint(path_tree, hf_cip_path_segment, tvb, 0, 0, segment_type );
         proto_item_set_generated(path_seg_item);
         path_seg_tree = proto_item_add_subtree( path_seg_item, ett_path_seg );
         it = proto_tree_add_uint(path_seg_tree, hf_cip_path_segment_type, tvb, 0, 0, segment_type&CI_SEGMENT_TYPE_MASK);
         proto_item_set_generated(it);
      }
      else
      {
         path_seg_item = proto_tree_add_item(path_tree, hf_cip_path_segment, tvb, offset, 1, ENC_LITTLE_ENDIAN);
         path_seg_tree = proto_item_add_subtree( path_seg_item, ett_path_seg );
         proto_tree_add_item(path_seg_tree, hf_cip_path_segment_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
      }

      /* Determine the segment type */

      switch( segment_type & CI_SEGMENT_TYPE_MASK )
      {
         case CI_PORT_SEGMENT:
         {
            segment_len = dissect_segment_port(tvb, offset, generate, path_seg_tree, path_seg_item, epath_item);
            break;
         }

         case CI_LOGICAL_SEGMENT:
         {
            guint8 logical_seg_type;
            logical_seg_type = segment_type & CI_LOGICAL_SEG_TYPE_MASK;

            /* Logical segment, determine the logical type */
            if ( generate )
            {
               it = proto_tree_add_uint(path_seg_tree, hf_cip_logical_seg_type, tvb, 0, 0, logical_seg_type);
               proto_item_set_generated(it);
               if (logical_seg_type != CI_LOGICAL_SEG_SPECIAL && logical_seg_type != CI_LOGICAL_SEG_SERV_ID)
               {
                  it = proto_tree_add_uint(path_seg_tree, hf_cip_logical_seg_format, tvb, 0, 0, segment_type & CI_LOGICAL_SEG_FORMAT_MASK);
                  proto_item_set_generated(it);
               }
            }
            else
            {
               proto_tree_add_item(path_seg_tree, hf_cip_logical_seg_type, tvb, offset, 1, ENC_LITTLE_ENDIAN );
               if (logical_seg_type != CI_LOGICAL_SEG_SPECIAL && logical_seg_type != CI_LOGICAL_SEG_SERV_ID)
                  proto_tree_add_item(path_seg_tree, hf_cip_logical_seg_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
            }

            proto_item_append_text( path_seg_item, " (%s)", val_to_str_const( ((segment_type & (CI_LOGICAL_SEG_TYPE_MASK|CI_LOGICAL_SEG_FORMAT_MASK))), cip_logical_seg_vals, "Reserved"));

            switch (logical_seg_type)
            {
               case CI_LOGICAL_SEG_CLASS_ID:
               {
                  guint32 ClassID;
                  segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo,
                       epath_item, path_seg_tree, path_seg_item, &cia_ret_item,
                       "Class", cip_class_names_vals, &ClassID,
                       hf_cip_class8, hf_cip_class16, hf_cip_class32);
                  if (segment_len == 0)
                  {
                     return 0;
                  }

                  if (req_data)
                  {
                     req_data->iClass = ClassID;

                     // Save the first ClassID separately.
                     if (req_data->iClassA == SEGMENT_VALUE_NOT_SET)
                     {
                        req_data->iClassA = ClassID;
                     }
                  }

                  if (req_data != NULL)
                  {
                     if (cip_enhanced_info_column == TRUE && is_msp_item == FALSE)
                     {
                        add_cip_class_to_info_column(pinfo, req_data->iClass, display_type);
                     }

                     if (msp_item != NULL)
                     {
                        proto_item_append_text(msp_item, "%s - ", val_to_str(req_data->iClass, cip_class_names_vals, "Class (0x%02x)"));
                     }
                  }

                  break;
               }

               case CI_LOGICAL_SEG_INST_ID:
               {
                  guint32 InstanceID;
                  segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo,
                       epath_item, path_seg_tree, path_seg_item, &cia_ret_item,
                       "Instance", NULL, &InstanceID,
                       hf_cip_instance8, hf_cip_instance16, hf_cip_instance32);
                  if (segment_len == 0)
                  {
                     return 0;
                  }

                  if (req_data)
                  {
                     req_data->iInstance = InstanceID;

                     // Save the first InstanceID separately.
                     if (req_data->iInstanceA == SEGMENT_VALUE_NOT_SET)
                     {
                        req_data->iInstanceA = InstanceID;
                     }
                  }

                  break;
               }

               case CI_LOGICAL_SEG_MBR_ID:
                  segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo,
                       epath_item, path_seg_tree, path_seg_item, &cia_ret_item,
                       "Member", NULL, (req_data == NULL) ? NULL : &req_data->iMember,
                       hf_cip_member8, hf_cip_member16, hf_cip_member32);
                  break;

               case CI_LOGICAL_SEG_ATTR_ID:
                  segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo,
                       epath_item, path_seg_tree, path_seg_item, &cia_ret_item,
                       "Attribute", NULL, (req_data == NULL) ? NULL : &req_data->iAttribute,
                       hf_cip_attribute8, hf_cip_attribute16, hf_cip_attribute32);
                  if (segment_len == 0)
                  {
                     return 0;
                  }

                  if (req_data != NULL)
                  {
                     attribute_info_t* att_info = cip_get_attribute(req_data->iClass, req_data->iInstance,
                                                  req_data->iAttribute);
                     if (att_info != NULL)
                     {
                        proto_item_append_text(cia_ret_item, " (%s)", att_info->text);
                        proto_item_append_text(epath_item, " (%s)", att_info->text);
                     }
                  }
                  break;

               case CI_LOGICAL_SEG_CON_POINT:
               {
                  guint32 ConnPoint;
                  segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo,
                     epath_item, path_seg_tree, path_seg_item, &cia_ret_item,
                     "Connection Point", NULL, &ConnPoint,
                     hf_cip_conpoint8, hf_cip_conpoint16, hf_cip_conpoint32);
                  if (segment_len == 0)
                  {
                     return 0;
                  }

                  if (req_data)
                  {
                     req_data->iConnPoint = ConnPoint;

                     // Save the first ConnPoint separately.
                     if (req_data->iConnPointA == SEGMENT_VALUE_NOT_SET)
                     {
                        req_data->iConnPointA = ConnPoint;
                     }
                  }

                  break;
               }

               case CI_LOGICAL_SEG_SPECIAL:
                   segment_len = dissect_segment_logical_special(pinfo, tvb, offset, generate,
                      path_seg_tree, path_seg_item, epath_item);
                   break;

               case CI_LOGICAL_SEG_SERV_ID:
                   segment_len = dissect_segment_logical_service_id(pinfo, tvb, offset, generate,
                      path_seg_tree, path_seg_item, epath_item);
                   break;

               case CI_LOGICAL_SEG_EXT_LOGICAL:
                   segment_len = dissect_cia(tvb, offset, segment_type, generate, packed, pinfo,
                       epath_item, path_seg_tree, path_seg_item, &cia_ret_item,
                       "Extended Logical", NULL, NULL,
                       hf_cip_ext_logical8, hf_cip_ext_logical16, hf_cip_ext_logical32);
                   break;

               default:
                  expert_add_info(pinfo, epath_item, &ei_proto_log_seg_type);
                  return 0;

            } /* end of switch( logical_seg_type ) */
            break;
         }

         case CI_DATA_SEGMENT:
         {
            /* Data segment, determine the logical type */
            if ( generate )
            {
               it = proto_tree_add_uint(path_seg_tree, hf_cip_data_seg_type, tvb, 0, 0, segment_type & CI_DATA_SEG_TYPE_MASK);
               proto_item_set_generated(it);
            }
            else
            {
               proto_tree_add_item(path_seg_tree, hf_cip_data_seg_type, tvb, offset, 1, ENC_LITTLE_ENDIAN );
            }

            proto_item_append_text( path_seg_item, " (%s)", val_to_str_const( (segment_type & CI_DATA_SEG_TYPE_MASK), cip_data_segment_type_vals, "Reserved"));

            switch( segment_type & CI_DATA_SEG_TYPE_MASK)
            {
               case CI_DATA_SEG_SIMPLE:
                  segment_len = dissect_segment_data_simple(pinfo, tvb, offset, generate, path_seg_tree, path_seg_item, req_data);
                  proto_item_append_text(epath_item, "[Data]" );
                  break;

               case CI_DATA_SEG_SYMBOL:
                  segment_len = dissect_segment_ansi_extended_symbol(pinfo, tvb, offset, generate,
                     path_seg_tree, path_seg_item, epath_item, display_type, is_msp_item, msp_item);
                  break;

               default:
                  expert_add_info(pinfo, epath_item, &ei_proto_log_sub_seg_type);
                  return 0;

            } /* End of switch sub-type */

            break;
         }

         case CI_NETWORK_SEGMENT:
            segment_len = dissect_segment_network(pinfo, tvb, offset, generate, path_seg_tree, path_seg_item, epath_item, display_type, safety, req_data);
            break;

         case CI_SYMBOLIC_SEGMENT:
         {
             segment_len = dissect_segment_symbolic(tvb, path_seg_tree,
                 path_seg_item, epath_item,
                 offset, generate);

             if (segment_len == 0)
             {
                 expert_add_info(pinfo, epath_item, &ei_proto_ext_string_format);
                 return 0;
             }

             if (generate == FALSE)
             {
                 proto_item_set_len(path_seg_item, segment_len);
             }

             break;
         }

         default:
            expert_add_info(pinfo, epath_item, &ei_proto_seg_type);
            return 0;

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

   return segment_len;
}

void reset_cip_request_info(cip_simple_request_info_t* req_data)
{
   req_data->iClass = SEGMENT_VALUE_NOT_SET;
   req_data->iClassA = SEGMENT_VALUE_NOT_SET;

   req_data->iInstance = SEGMENT_VALUE_NOT_SET;
   req_data->iInstanceA = SEGMENT_VALUE_NOT_SET;

   req_data->iAttribute = SEGMENT_VALUE_NOT_SET;
   req_data->iMember = SEGMENT_VALUE_NOT_SET;

   req_data->iConnPoint = SEGMENT_VALUE_NOT_SET;
   req_data->iConnPointA = SEGMENT_VALUE_NOT_SET;

   req_data->hasSimpleData = FALSE;
}

void dissect_epath(tvbuff_t *tvb, packet_info *pinfo, proto_tree *path_tree, proto_item *epath_item, int offset, int path_length,
                    gboolean generate, gboolean packed, cip_simple_request_info_t* req_data, cip_safety_epath_info_t* safety,
                    int display_type, proto_item *msp_item,
                    gboolean is_msp_item)
{
   int pathpos = 0;
   proto_item *hidden_item;

   if (req_data != NULL)
   {
      reset_cip_request_info(req_data);
   }

   if (safety != NULL)
      safety->safety_seg = FALSE;

   if ( !generate )
   {
      hidden_item = proto_tree_add_item(path_tree, hf_cip_epath,
                                        tvb, offset, path_length, ENC_NA );
      proto_item_set_hidden(hidden_item);
   }

  while( pathpos < path_length )
  {
      int segment_len;
      segment_len = dissect_cip_segment_single(pinfo, tvb, offset + pathpos, path_tree, epath_item, generate, packed, req_data, safety, display_type, msp_item, is_msp_item);
      if (segment_len == 0)
      {
          break;
      }

      pathpos += segment_len;

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

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

} /* end of dissect_epath() */

#define NUM_SECONDS_PER_DAY ((guint64)(60 * 60 * 24))

/* Number of seconds between Jan 1, 1970 00:00:00 epoch and CIP's epoch time of Jan 1, 1972 00:00:00 */
#define CIP_TIMEBASE ((guint64)(NUM_SECONDS_PER_DAY * 365 * 2))

void dissect_cip_date_and_time(proto_tree *tree, tvbuff_t *tvb, int offset, int hf_datetime)
{
   nstime_t computed_time;
   guint16 num_days_since_1972;
   guint32 num_ms_today;

   num_days_since_1972 = tvb_get_letohs(tvb, offset+4);
   num_ms_today = tvb_get_letohl(tvb, offset);

   if ((num_days_since_1972 != 0) || (num_ms_today != 0))
   {
      computed_time.secs = CIP_TIMEBASE + (guint64)num_days_since_1972 * NUM_SECONDS_PER_DAY;
      computed_time.secs += num_ms_today/1000;
      computed_time.nsecs = (num_ms_today%1000)*1000000;
   }
   else
   {
      computed_time.secs = 0;
      computed_time.nsecs = 0;
   }

   proto_tree_add_time(tree, hf_datetime, tvb, offset, 6, &computed_time);
}

static int dissect_cip_date(proto_tree *tree, tvbuff_t *tvb, int offset, int hf_date)
{
   char date_str[20];

   guint16 num_days_since_1972 = tvb_get_letohs(tvb, offset);
   /* Convert to nstime epoch */
   time_t computed_time = CIP_TIMEBASE + (guint64)num_days_since_1972 * NUM_SECONDS_PER_DAY;
   struct tm* date = gmtime(&computed_time);

   if (date != NULL)
      strftime(date_str, 20, "%Y-%m-%d", date);
   else
      (void) g_strlcpy(date_str, "Not representable", sizeof date_str);
   proto_tree_add_uint_format_value(tree, hf_date, tvb, offset, 2, num_days_since_1972, "%s", date_str);

   return 2;
}

// CIP Type - STIME (nanoseconds)
static int dissect_cip_stime(proto_tree* tree, tvbuff_t* tvb, int offset, int hf_datetime)
{
   nstime_t ts_nstime = { 0 };
   guint64 timestamp = tvb_get_letoh64(tvb, offset);
   ts_nstime.secs = timestamp / 1000000000;
   ts_nstime.nsecs = timestamp % 1000000000;

   proto_tree_add_time(tree, hf_datetime, tvb, offset, 8, &ts_nstime);

   return 8;
}

// CIP Type - UTIME (microseconds)
int dissect_cip_utime(proto_tree* tree, tvbuff_t* tvb, int offset, int hf_datetime)
{
   nstime_t ts_nstime = { 0 };
   guint64 timestamp = tvb_get_letoh64(tvb, offset);
   ts_nstime.secs = timestamp / 1000000;
   ts_nstime.nsecs = (timestamp % 1000000) * 1000;

   proto_tree_add_time(tree, hf_datetime, tvb, offset, 8, &ts_nstime);

   return 8;
}

int dissect_cip_string_type(packet_info *pinfo, proto_tree *tree, proto_item *item,
    tvbuff_t *tvb, int offset, int hf_type, int string_type)
{
    guint32 string_size_field_len;
    guint32 string_size;
    guint string_encoding;
    int parsed_len;
    int total_len;

    total_len = tvb_reported_length_remaining(tvb, offset);

    switch (string_type)
    {
    case CIP_SHORT_STRING_TYPE:
        string_size = tvb_get_guint8(tvb, offset);
        string_encoding = ENC_ASCII | ENC_NA;
        string_size_field_len = 1;
        break;

    case CIP_STRING_TYPE:
        string_size = tvb_get_letohs(tvb, offset);
        string_encoding = ENC_ASCII | ENC_NA;
        string_size_field_len = 2;
        break;

    case CIP_STRING2_TYPE:
        string_size = tvb_get_letohs(tvb, offset) * 2;
        string_encoding = ENC_UCS_2 | ENC_LITTLE_ENDIAN;
        string_size_field_len = 2;
        break;

    default:
        // Unsupported.
        return total_len;
        break;
    }

    if (total_len < (int)(string_size + string_size_field_len))
    {
        expert_add_info(pinfo, item, &ei_mal_missing_string_data);
        parsed_len = total_len;
    }
    else
    {
        proto_tree_add_item(tree, hf_type, tvb, offset + string_size_field_len, string_size, string_encoding);
        parsed_len = string_size + string_size_field_len;
    }

    return parsed_len;
}

static int dissect_cip_stringi(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb, int offset)
{
    int parsed_len = 1;
    guint32 num_char = 0;
    proto_tree_add_item_ret_uint(tree, hf_stringi_number_char, tvb, offset, 1, ENC_LITTLE_ENDIAN, &num_char);

    for (guint32 i = 0; i < num_char; ++i)
    {
        proto_tree_add_item(tree, hf_stringi_language_char, tvb, offset + 1, 3, ENC_ASCII | ENC_NA);

        guint32 char_string_type = 0;
        proto_tree_add_item_ret_uint(tree, hf_stringi_char_string_struct, tvb, offset + 4, 1, ENC_LITTLE_ENDIAN, &char_string_type);
        proto_tree_add_item(tree, hf_stringi_char_set, tvb, offset + 5, 2, ENC_LITTLE_ENDIAN);
        parsed_len += 6;

        if (char_string_type != CIP_STRING_TYPE
            && char_string_type != CIP_SHORT_STRING_TYPE
            && char_string_type != CIP_STRING2_TYPE)
        {
            // Unsupported type.
            break;
        }

        parsed_len += dissect_cip_string_type(pinfo, tree, item, tvb, offset + parsed_len, hf_stringi_international_string, char_string_type);
    }

    return parsed_len;
}

int dissect_cip_attribute(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
                         attribute_info_t* attr, int offset, int total_len)
{
   int i, temp_data, temp_time, hour, min, sec, ms,
      consumed = 0;

   /* sanity check */
   if (((attr->datatype == cip_dissector_func) && (attr->pdissect == NULL)) ||
       ((attr->datatype != cip_dissector_func) && (attr->phf == NULL)))
   {
      DISSECTOR_ASSERT(0);
      return total_len;
   }

   switch (attr->datatype)
   {
   case cip_bool:
   case cip_usint:
   case cip_sint:
   case cip_byte:
      proto_tree_add_item(tree, *(attr->phf), tvb, offset, 1, ENC_LITTLE_ENDIAN);
      consumed = 1;
      break;
   case cip_uint:
   case cip_int:
   case cip_word:
   case cip_itime:
      proto_tree_add_item(tree, *(attr->phf), tvb, offset, 2, ENC_LITTLE_ENDIAN);
      consumed = 2;
      break;
   case cip_usint_array:
      for (i = 0; i < total_len; i++)
         proto_tree_add_item(tree, *(attr->phf), tvb, offset, total_len, ENC_NA);
      consumed = total_len;
      break;
   case cip_uint_array:
      for (i = 0; i < total_len; i+=2)
         proto_tree_add_item(tree, *(attr->phf), tvb, offset+i, 2, ENC_LITTLE_ENDIAN);
      consumed = i;
      break;
   case cip_udint:
   case cip_dint:
   case cip_dword:
   case cip_real:
   case cip_time:
   case cip_ftime:
      proto_tree_add_item(tree, *(attr->phf), tvb, offset, 4, ENC_LITTLE_ENDIAN);
      consumed = 4;
      break;
   case cip_ulint:
   case cip_lint:
   case cip_lword:
   case cip_lreal:
   case cip_ltime:
   case cip_ntime:
      proto_tree_add_item(tree, *(attr->phf), tvb, offset, 8, ENC_LITTLE_ENDIAN);
      consumed = 8;
      break;
   case cip_short_string:
      consumed = dissect_cip_string_type(pinfo, tree, item, tvb, offset, *(attr->phf), CIP_SHORT_STRING_TYPE);
      break;
   case cip_string:
      consumed = dissect_cip_string_type(pinfo, tree, item, tvb, offset, *(attr->phf), CIP_STRING_TYPE);
      break;
   case cip_dissector_func:
      consumed = attr->pdissect(pinfo, tree, item, tvb, offset, total_len);
      if (consumed == 0)
      {
         consumed = total_len;
      }

      break;
   case cip_date_and_time:
      dissect_cip_date_and_time(tree, tvb, offset, *(attr->phf));
      consumed = 6;
      break;
   case cip_stime:
      consumed = dissect_cip_stime(tree, tvb, offset, *(attr->phf));
      break;
   case cip_utime:
      consumed = dissect_cip_utime(tree, tvb, offset, *(attr->phf));
      break;
   case cip_date:
      consumed = dissect_cip_date(tree, tvb, offset, *(attr->phf));
      break;
   case cip_time_of_day:
      temp_time = temp_data = tvb_get_letohl( tvb, offset);
      hour = temp_time/(60*60*1000);
      temp_time %= (60*60*1000);
      min = temp_time/(60*1000);
      temp_time %= (60*1000);
      sec = temp_time/1000;
      ms = temp_time%1000;
      proto_tree_add_uint_format_value(tree, *(attr->phf), tvb, offset, 4, temp_data, "%02d:%02d:%02d.%03d", hour, min, sec, ms);
      consumed = 4;
      break;
   case cip_string2:
      consumed = dissect_cip_string_type(pinfo, tree, item, tvb, offset, *(attr->phf), CIP_STRING2_TYPE);
      break;
   case cip_stringi:
      consumed = dissect_cip_stringi(pinfo, tree, item, tvb, offset);
      break;
   case cip_stringN:
      /* CURRENTLY NOT SUPPORTED */
      expert_add_info(pinfo, item, &ei_proto_unsupported_datatype);
      consumed = total_len;
      break;
   }

   return consumed;
}

static int dissect_cip_service(packet_info *pinfo, tvbuff_t *tvb, int offset,
   proto_item *ti, proto_tree *item_tree, cip_service_info_t *service_entry, guint8 service)
{
   int parsed_len = 0;

   if (service_entry != NULL && service_entry->pdissect)
   {
      gboolean request = !(service & CIP_SC_RESPONSE_MASK);
      parsed_len = service_entry->pdissect(pinfo, item_tree, ti, tvb, offset, request);
   }

   return parsed_len;
}

static int dissect_cip_object_specific_service(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item* msp_item, cip_service_info_t *service_entry)
{
   DISSECTOR_ASSERT(service_entry != NULL);

   int offset = 0;
   guint8 service = tvb_get_guint8(tvb, offset);
   guint8 gen_status = 0;

   // Skip over the Request/Response header to get to the actual data.
   if (service & CIP_SC_RESPONSE_MASK)
   {
      gen_status = tvb_get_guint8(tvb, offset + 2);

      guint16 add_stat_size = tvb_get_guint8(tvb, offset + 3) * 2;
      offset = 4 + add_stat_size;
   }
   else
   {
      guint16 req_path_size = tvb_get_guint8(tvb, offset + 1) * 2;
      offset = 2 + req_path_size;
   }

   // Display the service name, even if there is no payload data.
   if (service_entry->service_name)
   {
      col_append_str(pinfo->cinfo, COL_INFO, service_entry->service_name);
      col_set_fence(pinfo->cinfo, COL_INFO);

      proto_item_append_text(msp_item, "%s", service_entry->service_name);
   }

   // Only dissect responses with specific response statuses.
   if ((service & CIP_SC_RESPONSE_MASK)
      && (should_dissect_cip_response(tvb, offset, gen_status) == FALSE))
   {
      return 0;
   }

   proto_item *payload_item;
   proto_tree *payload_tree = proto_tree_add_subtree(tree, tvb, offset, tvb_reported_length_remaining(tvb, offset), ett_cmd_data, &payload_item, "");

   // Add the service info to the tree item.
   proto_item_append_text(payload_item, "%s", service_entry->service_name);

   if (service & CIP_SC_RESPONSE_MASK)
   {
      proto_item_append_text(payload_item, " (Response)");
   }
   else
   {
      proto_item_append_text(payload_item, " (Request)");
   }

   // Process any known command-specific data.
   offset += dissect_cip_service(pinfo, tvb, offset, payload_item, payload_tree, service_entry, service);

   // Add any remaining data.
   int len_remain = tvb_reported_length_remaining(tvb, offset);
   if (len_remain > 0)
   {
      proto_tree_add_item(payload_tree, hf_cip_data, tvb, offset, len_remain, ENC_NA);
   }

   return tvb_reported_length(tvb);
}

/************************************************
 *
 * Dissector for generic CIP object
 *
 ************************************************/

static void
dissect_cip_generic_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo, proto_item *ti )
{
   proto_tree *cmd_data_tree;
   int req_path_size;
   unsigned char add_stat_size;
   int cmd_data_len;
   int cmd_data_offset;
   guint8 service = tvb_get_guint8( tvb, offset );

   if (service & CIP_SC_RESPONSE_MASK)
   {
      /* Response message */
      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
      cmd_data_len = item_length - 4 - add_stat_size;
      cmd_data_offset = offset + 4 + add_stat_size;
   }
   else
   {
      /* Request message */
      req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
      cmd_data_len = item_length - req_path_size - 2;
      cmd_data_offset = offset + 2 + req_path_size;
   }

   /* If there is any command specific data create a sub-tree for it */
   if (cmd_data_len > 0)
   {
      cmd_data_tree = proto_tree_add_subtree(item_tree, tvb, cmd_data_offset, cmd_data_len,
         ett_cmd_data, NULL, "Command Specific Data");
      proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, cmd_data_offset, cmd_data_len, ENC_NA);
   }
   else
   {
      proto_item_set_hidden(ti);
   }

   add_cip_service_to_info_column(pinfo, service, cip_sc_vals);
} /* End of dissect_cip_generic_data() */

static int
dissect_cip_class_generic(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
   proto_item *ti;
   proto_tree *class_tree;

   /* Create display subtree for the protocol */
   ti = proto_tree_add_item(tree, proto_cip_class_generic, tvb, 0, -1, ENC_NA);
   class_tree = proto_item_add_subtree( ti, ett_cip_class_generic );

   dissect_cip_generic_data( class_tree, tvb, 0, tvb_reported_length(tvb), pinfo, ti );

   return tvb_reported_length(tvb);
}

static int
dissect_cip_set_attribute_single_req(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item * item,
                                  int offset, cip_simple_request_info_t* req_data)
{
   int parsed_len = 0;
   attribute_info_t* attr;

   attr = cip_get_attribute(req_data->iClass, req_data->iInstance, req_data->iAttribute);
   if (attr != NULL)
   {
      parsed_len = dissect_cip_attribute(pinfo, tree, item, tvb, attr, offset, tvb_reported_length_remaining(tvb, offset));
   }

   return parsed_len;
}

int dissect_cip_get_attribute_list_req(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item * item,
                                  int offset, cip_simple_request_info_t* req_data)
{
   int i, att_count, att_value;
   attribute_info_t* pattribute;
   proto_item *att_list, *att_item;
   proto_tree* att_tree;

   /* Get attribute list request */
   if (tvb_reported_length_remaining(tvb, offset) < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_serv_gal);
      return 0;
   }

   /* Add number of attributes */
   att_count = tvb_get_letohs( tvb, offset);
   proto_tree_add_item(tree, hf_cip_sc_get_attr_list_attr_count, tvb, offset, 2, ENC_LITTLE_ENDIAN);
   offset += 2;

   /* Add Attribute List */
   att_tree = proto_tree_add_subtree(tree, tvb, offset, att_count*2, ett_cip_get_attribute_list, &att_list, "Attribute List" );

   for( i=0; i < att_count; i++ )
   {
      att_value = tvb_get_letohs( tvb, offset);
      att_item = proto_tree_add_item(att_tree, hf_cip_attribute16, tvb, offset, 2, ENC_LITTLE_ENDIAN);
      pattribute = cip_get_attribute(req_data->iClass, req_data->iInstance, att_value);
      if (pattribute != NULL)
         proto_item_append_text(att_item, " (%s)", pattribute->text);

      offset += 2;
      if ((tvb_reported_length_remaining(tvb, offset) < 2) && (i < att_count-1))
      {
         expert_add_info(pinfo, att_list, &ei_mal_serv_gal_count);
         break;
      }
   }

   return 2 + att_count * 2;
}

int
dissect_cip_set_attribute_list_req(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item * item,
                                  int offset, cip_simple_request_info_t* req_data)
{
   int i, start_offset, att_count,
       att_value, att_size;
   attribute_info_t* attr;
   proto_item *att_list, *att_item;
   proto_tree *att_tree, *att_list_tree;

   /* Get attribute list request */
   if (tvb_reported_length_remaining(tvb, offset) < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_serv_sal);
      return 0;
   }

   /* Add number of attributes */
   att_count = tvb_get_letohs( tvb, offset);
   proto_tree_add_item(tree, hf_cip_sc_set_attr_list_attr_count, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   /* Add Attribute List */
   att_list_tree = proto_tree_add_subtree(tree, tvb, offset+2, att_count*2, ett_cip_set_attribute_list, &att_list, "Attribute List" );
   offset += 2;
   start_offset = offset;

   for( i=0; i < att_count; i++ )
   {
      att_value = tvb_get_letohs( tvb, offset);
      att_item = proto_tree_add_item(att_list_tree, hf_cip_attribute16, tvb, offset, 2, ENC_LITTLE_ENDIAN);
      att_tree = proto_item_add_subtree( att_item, ett_cip_set_attribute_list_item);
      offset += 2;

      attr = cip_get_attribute(req_data->iClass, req_data->iInstance, att_value);
      if (attr != NULL)
      {
         proto_item_append_text(att_item, " (%s)", attr->text);
         /* provide attribute data */
         att_size = dissect_cip_attribute(pinfo, att_tree, att_item, tvb, attr, offset, tvb_reported_length_remaining(tvb, offset));
         offset += att_size;
         proto_item_set_len(att_item, att_size+2);
      }
      else
      {
         /* Can't find the attribute. */
         break;
      }

      if ((tvb_reported_length_remaining(tvb, offset) < 2) && (i < att_count-1))
      {
         expert_add_info(pinfo, att_list, &ei_mal_serv_sal_count);
         break;
      }
   }

   proto_item_set_len(att_list, offset-start_offset );

   return 2 + (offset - start_offset);
}

// NOLINTNEXTLINE(misc-no-recursion)
int dissect_cip_multiple_service_packet(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item * item, int offset, gboolean request)
{
   proto_tree *mult_serv_tree, *offset_tree;
   int i, num_services, serv_offset, prev_offset = 0;
   int parsed_len;
   cip_req_info_t *cip_req_info, *mr_single_req_info;
   mr_mult_req_info_t *mr_mult_req_info = NULL;

   if (tvb_reported_length_remaining(tvb, offset) < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_msp_missing_services);
      return 0;
   }

   num_services = tvb_get_letohs( tvb, offset);
   proto_tree_add_item(tree, hf_cip_sc_mult_serv_pack_num_services, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   /* Ensure a rough sanity check */
   if (num_services*2 > tvb_reported_length_remaining(tvb, offset+2))
   {
      expert_add_info(pinfo, item, &ei_mal_msp_services);
   }

   offset_tree = proto_tree_add_subtree(tree, tvb, offset + 2, num_services * 2, ett_cip_msp_offset, NULL, "Offset List");
   for (i = 0; i < num_services; i++)
   {
      proto_tree_add_item(offset_tree, hf_cip_sc_mult_serv_pack_offset, tvb, offset + 2 + i * 2, 2, ENC_LITTLE_ENDIAN);
   }

   cip_req_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0 );
   if ( cip_req_info )
   {
      /* Only allocate memory for requests. */
      if (cip_req_info->pData == NULL && request == TRUE)
      {
         mr_mult_req_info = wmem_new(wmem_file_scope(), mr_mult_req_info_t);
         mr_mult_req_info->service = SC_MULT_SERV_PACK;
         mr_mult_req_info->num_services = num_services;
         mr_mult_req_info->requests = (cip_req_info_t *)wmem_alloc0(wmem_file_scope(), sizeof(cip_req_info_t)*num_services);
         cip_req_info->pData = mr_mult_req_info;
      }

      mr_mult_req_info = (mr_mult_req_info_t*)cip_req_info->pData;

      if (mr_mult_req_info
         && (mr_mult_req_info->service != SC_MULT_SERV_PACK
         || mr_mult_req_info->num_services != num_services))
      {
         mr_mult_req_info = NULL;
      }
   }

   col_append_str(pinfo->cinfo, COL_INFO, ": ");

   parsed_len = 2 + num_services * 2;
   for( i=0; i < num_services; i++ )
   {
      proto_item *mult_serv_item;
      int serv_length;
      tvbuff_t *next_tvb;

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

      if (tvb_reported_length_remaining(tvb, serv_offset) <= 0)
      {
         expert_add_info(pinfo, item, &ei_mal_msp_inv_offset);
         continue;
      }

      if( i == (num_services-1) )
      {
         /* Last service to add */
         serv_length = tvb_reported_length_remaining(tvb, offset)-serv_offset;
      }
      else
      {
         serv_length = tvb_get_letohs( tvb, offset+2+((i+1)*2) ) - serv_offset;
      }

      mult_serv_tree = proto_tree_add_subtree_format(tree, tvb, offset+serv_offset, serv_length,
                    ett_cip_mult_service_packet, &mult_serv_item, "Service Packet #%d: ", i+1 );
      proto_tree_add_item(mult_serv_tree, hf_cip_sc_mult_serv_pack_offset, tvb, offset+2+(i*2) , 2, ENC_LITTLE_ENDIAN);

      /* Make sure the offset is valid */
      if ((tvb_reported_length_remaining(tvb, serv_offset) <= 0) ||
          (tvb_reported_length_remaining(tvb, serv_offset+serv_length) <= 0) ||
          (serv_length <= 0) ||
          (prev_offset >= serv_offset))
      {
         expert_add_info(pinfo, mult_serv_item, &ei_mal_msp_inv_offset);
         prev_offset = serv_offset;
         continue;
      }
      prev_offset = serv_offset;

      /*
      ** We call ourselves again to dissect embedded packet
      */

      next_tvb = tvb_new_subset_length(tvb, offset+serv_offset, serv_length);

      if ( mr_mult_req_info )
      {
         mr_single_req_info = mr_mult_req_info->requests + i;
         dissect_cip_data(mult_serv_tree, next_tvb, 0, pinfo, mr_single_req_info, mult_serv_item, TRUE);
      }
      else
      {
         dissect_cip_data(mult_serv_tree, next_tvb, 0, pinfo, NULL, mult_serv_item, TRUE);
      }

      /* Add the embedded CIP service to the item. */
      if (mult_serv_item != NULL)
      {
         guint8 service = tvb_get_guint8(next_tvb, 0);
         proto_item_append_text(mult_serv_item, "%s", val_to_str(service & CIP_SC_MASK, cip_sc_vals, "Service (0x%02x)"));
      }

      if (i != num_services - 1)
      {
          col_append_str(pinfo->cinfo, COL_INFO, ", ");
      }

      parsed_len += serv_length;
   }

   return parsed_len;
}

static int
// NOLINTNEXTLINE(misc-no-recursion)
dissect_cip_generic_service_req(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, cip_simple_request_info_t* req_data)
{
   proto_item *cmd_data_item;
   int req_path_size,
       offset = 0;
   proto_tree *cmd_data_tree;
   guint8 service = tvb_get_guint8( tvb, offset ) & CIP_SC_MASK;

   add_cip_service_to_info_column(pinfo, service, cip_sc_vals);

   req_path_size = tvb_get_guint8(tvb, offset + 1);
   offset += ((req_path_size * 2) + 2);

   /* Create service tree */
   cmd_data_tree = proto_tree_add_subtree(tree, tvb, offset, 0, ett_cmd_data, &cmd_data_item,
                        val_to_str(service, cip_sc_vals , "Unknown Service (0x%02x)"));
   proto_item_append_text(cmd_data_item, " (Request)");

   int parsed_len = 0;

   switch(service)
   {
   case SC_GET_ATT_LIST:
      parsed_len = dissect_cip_get_attribute_list_req(tvb, pinfo, cmd_data_tree, cmd_data_item, offset, req_data);
      break;
   case SC_SET_ATT_LIST:
      parsed_len = dissect_cip_set_attribute_list_req(tvb, pinfo, cmd_data_tree, cmd_data_item, offset, req_data);
      break;
   case SC_MULT_SERV_PACK:
      parsed_len = dissect_cip_multiple_service_packet(tvb, pinfo, cmd_data_tree, cmd_data_item, offset, TRUE);
      break;
   case SC_SET_ATT_SINGLE:
      parsed_len = dissect_cip_set_attribute_single_req(tvb, pinfo, cmd_data_tree, cmd_data_item, offset, req_data);
      break;
   case SC_FIND_NEXT_OBJ_INST:
      proto_tree_add_item(cmd_data_tree, hf_cip_find_next_object_max_instance, tvb, offset, 1, ENC_LITTLE_ENDIAN);
      parsed_len = 1;
      break;
   default:
      // No specific handling for other services.
      break;
   }

   // Display any remaining unparsed data.
   int remain_len = tvb_reported_length_remaining(tvb, offset + parsed_len);
   if (remain_len > 0)
   {
       proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset + parsed_len, remain_len, ENC_NA);
   }

   proto_item_set_len(cmd_data_item, parsed_len + remain_len);

   return tvb_reported_length(tvb);
}

typedef struct cip_gaa_key {
   guint32 cip_class;
   gboolean class_instance;
} cip_gaa_key_t;

typedef struct cip_gaa_val {
   wmem_list_t *attributes;
} cip_gaa_val_t;

static wmem_map_t *cip_gaa_hashtable = NULL;

static guint
cip_gaa_hash (gconstpointer v)
{
   const cip_gaa_key_t *key = (const cip_gaa_key_t *)v;
   guint val;

   val = (guint)((key->cip_class << 1) & 0xFFFFFFFE);
   val |= (key->class_instance & 1);

   return val;
}

static gint
cip_gaa_equal(gconstpointer v, gconstpointer w)
{
   const cip_gaa_key_t *v1 = (const cip_gaa_key_t *)v;
   const cip_gaa_key_t *v2 = (const cip_gaa_key_t *)w;

   if ((v1->cip_class == v2->cip_class) &&
       (v1->class_instance == v2->class_instance))
       return 1;

   return 0;
}

static void build_get_attr_all_table(void)
{
   size_t i, j;
   attribute_val_array_t* att_array;
   attribute_info_t* pattr;
   cip_gaa_key_t key;
   cip_gaa_key_t* new_key;
   cip_gaa_val_t *gaa_val;
   int last_attribute_index = -1;

   cip_gaa_hashtable = wmem_map_new(wmem_epan_scope(), cip_gaa_hash, cip_gaa_equal);

   for (i = 0; i < sizeof(all_attribute_vals)/sizeof(attribute_val_array_t); i++)
   {
      att_array = &all_attribute_vals[i];
      for (j = 0; j < att_array->size; j++)
      {
         pattr = &att_array->attrs[j];
         key.cip_class = pattr->class_id;
         key.class_instance = pattr->class_instance;

         gaa_val = (cip_gaa_val_t *)wmem_map_lookup( cip_gaa_hashtable, &key );
         if (gaa_val == NULL)
         {
            new_key = (cip_gaa_key_t*)wmem_memdup(wmem_epan_scope(), &key, sizeof(cip_gaa_key_t));
            gaa_val = wmem_new0(wmem_epan_scope(), cip_gaa_val_t);
            gaa_val->attributes = wmem_list_new(wmem_epan_scope());

            wmem_map_insert(cip_gaa_hashtable, new_key, gaa_val );
            last_attribute_index = -1;
         }

         if ((pattr->gaa_index >= 0) && (pattr->gaa_index > last_attribute_index))
         {
             wmem_list_append(gaa_val->attributes, pattr);
             last_attribute_index = pattr->gaa_index;
         }
      }
   }
}

int dissect_cip_get_attribute_all_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
    int offset, cip_simple_request_info_t* req_data)
{
   int att_size;
   gint len_remain;
   attribute_info_t* attr;
   proto_item *att_item;
   proto_tree *att_tree;
   cip_gaa_key_t key;
   cip_gaa_val_t *gaa_val;
   wmem_list_frame_t* attribute_list;
   int parsed_len = 0;

   key.cip_class = req_data->iClass;
   key.class_instance = (req_data->iInstance == 0);

   gaa_val = (cip_gaa_val_t *)wmem_map_lookup( cip_gaa_hashtable, &key );
   if (gaa_val == NULL)
   {
      return 0;
   }

   for (attribute_list = wmem_list_head(gaa_val->attributes);
       (attribute_list != NULL);
        attribute_list = wmem_list_frame_next(attribute_list))
   {
      attr = (attribute_info_t *)wmem_list_frame_data(attribute_list);
      len_remain = tvb_reported_length_remaining(tvb, offset);

      /* If there are no more attributes defined or there is no data left. */
      if (attr == NULL || len_remain <= 0)
         break;

      att_item = proto_tree_add_uint_format_value(tree, hf_cip_attribute16, tvb, offset, 0, attr->attribute, "%d (%s)", attr->attribute, attr->text);
      att_tree = proto_item_add_subtree(att_item, ett_cip_get_attributes_all_item);

      att_size = dissect_cip_attribute(pinfo, att_tree, att_item, tvb, attr, offset, len_remain);
      proto_item_set_len(att_item, att_size);

      offset += att_size;
      parsed_len += att_size;
   }

   return parsed_len;
}

static int
dissect_cip_get_attribute_list_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item * item,
                                  int offset, cip_simple_request_info_t* req_data)
{
   int i, start_offset, att_count,
       att_value, att_status;
   guint att_size;
   attribute_info_t* attr;
   proto_item *att_list, *att_item;
   proto_tree *att_tree, *att_list_tree;

   /* Get attribute list response */
   if (tvb_reported_length_remaining(tvb, offset) < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_serv_gal);
      return 0;
   }

   /* Add number of attributes */
   att_count = tvb_get_letohs( tvb, offset);
   proto_tree_add_item(tree, hf_cip_sc_get_attr_list_attr_count, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   /* Add Attribute List */
   att_list_tree = proto_tree_add_subtree(tree, tvb, offset+2, att_count*4, ett_cip_get_attribute_list, &att_list, "Attribute List" );
   offset += 2;
   start_offset = offset;

   for( i=0; i < att_count; i++ )
   {
      att_value = tvb_get_letohs( tvb, offset);
      att_item = proto_tree_add_item(att_list_tree, hf_cip_attribute16, tvb, offset, 2, ENC_LITTLE_ENDIAN);
      att_tree = proto_item_add_subtree( att_item, ett_cip_get_attribute_list_item);

      att_status = tvb_get_letohs( tvb, offset+2);
      proto_tree_add_item(att_tree, hf_cip_sc_get_attr_list_attr_status, tvb, offset+2, 1, ENC_LITTLE_ENDIAN);

      attr = cip_get_attribute(req_data->iClass, req_data->iInstance, att_value);
      if (attr != NULL)
         proto_item_append_text(att_item, " (%s)", attr->text);

      offset += 4;
      if (att_status == 0)
      {
         if (attr != NULL)
         {
            /* provide attribute data */
            att_size = dissect_cip_attribute(pinfo, att_tree, att_item, tvb, attr, offset, tvb_reported_length_remaining(tvb, offset));
            offset += att_size;
            proto_item_set_len(att_item, att_size+4);
         }
         else
         {
            /* Can't find the attribute */
            break;
         }
      }

      if ((tvb_reported_length_remaining(tvb, offset) < 4) && (i < att_count-1))
      {
         expert_add_info(pinfo, att_list, &ei_mal_serv_gal_count);
         break;
      }
   }

   proto_item_set_len(att_list, offset-start_offset );
   return 2 + (offset - start_offset);
}

int
dissect_cip_set_attribute_list_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item * item,
                                  int offset, cip_simple_request_info_t* req_data)
{
   int i, start_offset, att_count, att_value;
   attribute_info_t* attr;
   proto_item *att_list, *att_item;
   proto_tree *att_tree, *att_list_tree;

   /* Get attribute list request */
   if (tvb_reported_length_remaining(tvb, offset) < 2)
   {
      expert_add_info(pinfo, item, &ei_mal_serv_sal);
      return 0;
   }

   /* Add number of attributes */
   att_count = tvb_get_letohs( tvb, offset);
   proto_tree_add_item(tree, hf_cip_sc_set_attr_list_attr_count, tvb, offset, 2, ENC_LITTLE_ENDIAN);

   /* Add Attribute List */
   att_list_tree = proto_tree_add_subtree(tree, tvb, offset+2, att_count*4, ett_cip_get_attribute_list, &att_list, "Attribute List" );
   offset += 2;
   start_offset = offset;

   for( i=0; i < att_count; i++ )
   {
      att_value = tvb_get_letohs( tvb, offset);
      att_item = proto_tree_add_item(att_list_tree, hf_cip_attribute16, tvb, offset, 2, ENC_LITTLE_ENDIAN);
      att_tree = proto_item_add_subtree( att_item, ett_cip_set_attribute_list_item);

      proto_tree_add_item(att_tree, hf_cip_sc_set_attr_list_attr_status, tvb, offset+2, 1, ENC_LITTLE_ENDIAN);

      attr = cip_get_attribute(req_data->iClass, req_data->iInstance, att_value);
      if (attr != NULL)
         proto_item_append_text(att_item, " (%s)", attr->text);

      offset += 4;
      if ((tvb_reported_length_remaining(tvb, offset) < 4) && (i < att_count-1))
      {
         expert_add_info(pinfo, att_list, &ei_mal_serv_sal_count);
         break;
      }
   }

   proto_item_set_len(att_list, offset-start_offset );
   return 2 + (offset - start_offset);
}

static int
dissect_cip_get_attribute_single_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item * item,
                                  int offset, cip_simple_request_info_t* req_data)
{
   int parsed_len = 0;
   int total_len;
   attribute_info_t* attr;

   total_len = tvb_reported_length_remaining(tvb, offset);
   attr = cip_get_attribute(req_data->iClass, req_data->iInstance, req_data->iAttribute);
   if (attr != NULL)
   {
      proto_item_append_text(item, " (%s)", attr->text);
      parsed_len = dissect_cip_attribute(pinfo, tree, item, tvb, attr, offset, total_len);
   }

   return parsed_len;
}

static int
dissect_cip_find_next_object_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, proto_item * item, int offset)
{
   guint32 i, num_instances;

   if (tvb_reported_length_remaining(tvb, offset) < 1)
   {
      expert_add_info(pinfo, item, &ei_mal_serv_find_next_object);
      return 0;
   }

   proto_tree_add_item_ret_uint(tree, hf_cip_find_next_object_num_instances, tvb, offset, 1, ENC_LITTLE_ENDIAN, &num_instances);
   offset += 1;

   for (i = 0; i < num_instances; i++)
   {
      proto_tree_add_item(tree, hf_cip_find_next_object_instance_item, tvb, offset, 1, ENC_LITTLE_ENDIAN);
      offset += 1;

      if ((tvb_reported_length_remaining(tvb, offset) < 2) && (i < num_instances-1))
      {
         expert_add_info(pinfo, item, &ei_mal_serv_find_next_object_count);
         break;
      }
   }

   return 1 + num_instances * 2;
}

void load_cip_request_data(packet_info *pinfo, cip_simple_request_info_t *req_data)
{
    cip_req_info_t* preq_info;
    preq_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);

    if ((preq_info != NULL) &&
        (preq_info->ciaData != NULL))
    {
        memcpy(req_data, preq_info->ciaData, sizeof(cip_simple_request_info_t));
    }
    else
    {
        reset_cip_request_info(req_data);
    }
}

gboolean should_dissect_cip_response(tvbuff_t *tvb, int offset, guint8 gen_status)
{
    // Only parse the response if there is data left or it has a response status that allows additional data
    //   to be returned.
    if ((tvb_reported_length_remaining(tvb, offset) == 0)
        && gen_status != CI_GRC_SUCCESS
        && gen_status != CI_GRC_ATTR_LIST_ERROR
        && gen_status != CI_GRC_SERVICE_ERROR
        && gen_status != CI_GRC_INVALID_LIST_STATUS)
    {
        return FALSE;
    }

    return TRUE;
}

int
// NOLINTNEXTLINE(misc-no-recursion)
dissect_cip_generic_service_rsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
   proto_item *cmd_data_item;
   proto_tree *cmd_data_tree;
   cip_simple_request_info_t req_data;
   int offset = 0;
   guint8 gen_status = tvb_get_guint8(tvb, offset + 2);
   guint8 service = tvb_get_guint8(tvb, offset) & CIP_SC_MASK;
   guint16 add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;

   offset = 4 + add_stat_size;

   add_cip_service_to_info_column(pinfo, service, cip_sc_vals);

   cmd_data_tree = proto_tree_add_subtree(tree, tvb, offset, 0,
       ett_cmd_data, &cmd_data_item, val_to_str(service, cip_sc_vals, "Unknown Service (0x%02x)"));
   proto_item_append_text(cmd_data_item, " (Response)");

   load_cip_request_data(pinfo, &req_data);

   if (should_dissect_cip_response(tvb, offset, gen_status) == FALSE)
   {
      return 0;
   }

   int parsed_len = 0;

   switch(service)
   {
   case SC_GET_ATT_ALL:
      parsed_len = dissect_cip_get_attribute_all_rsp(tvb, pinfo, cmd_data_tree, offset, &req_data);
      break;
   case SC_GET_ATT_LIST:
      parsed_len = dissect_cip_get_attribute_list_rsp(tvb, pinfo, cmd_data_tree, cmd_data_item, offset, &req_data);
      break;
   case SC_SET_ATT_LIST:
      parsed_len = dissect_cip_set_attribute_list_rsp(tvb, pinfo, cmd_data_tree, cmd_data_item, offset, &req_data);
      break;
   case SC_CREATE:
      proto_tree_add_item(cmd_data_tree, hf_cip_sc_create_instance, tvb, offset, 2, ENC_LITTLE_ENDIAN);
      parsed_len = 2;
      break;
   case SC_MULT_SERV_PACK:
      parsed_len = dissect_cip_multiple_service_packet(tvb, pinfo, cmd_data_tree, cmd_data_item, offset, FALSE);
      break;
   case SC_GET_ATT_SINGLE:
      parsed_len = dissect_cip_get_attribute_single_rsp(tvb, pinfo, cmd_data_tree, cmd_data_item, offset, &req_data);
      break;
   case SC_FIND_NEXT_OBJ_INST:
      parsed_len = dissect_cip_find_next_object_rsp(tvb, pinfo, cmd_data_tree, cmd_data_item, offset);
      break;
   case SC_GROUP_SYNC:
      proto_tree_add_item(cmd_data_tree, hf_cip_sc_group_sync_is_sync, tvb, offset, 1, ENC_LITTLE_ENDIAN);
      parsed_len = 1;
      break;
   default:
      // No specific handling for other services.
      break;
   }

   // Display any remaining unparsed data.
   int remain_len = tvb_reported_length_remaining(tvb, offset + parsed_len);
   if (remain_len > 0)
   {
       proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset + parsed_len, remain_len, ENC_NA);
   }

   proto_item_set_len(cmd_data_item, parsed_len + remain_len);

   return tvb_reported_length(tvb);
}

/************************************************
 *
 * Dissector for CIP Connection Manager
 *
 ************************************************/

static void
dissect_cip_cm_timeout(proto_tree *cmd_tree, tvbuff_t *tvb, int offset)
{
   guint8 tick, timeout_tick;
   int timeout;

   /* Display the priority/tick timer */
   tick = tvb_get_guint8( tvb, offset) & 0x0F;
   proto_tree_add_item( cmd_tree, hf_cip_cm_priority, tvb, offset, 1, ENC_LITTLE_ENDIAN);
   proto_tree_add_item( cmd_tree, hf_cip_cm_tick_time, tvb, offset, 1, ENC_LITTLE_ENDIAN);

   /* Display the time-out ticks */
   timeout_tick = tvb_get_guint8( tvb, offset+1 );
   proto_tree_add_item( cmd_tree, hf_cip_cm_timeout_tick, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);

   /* Display the actual time out */
   timeout = ( 1 << tick ) * timeout_tick;
   proto_tree_add_uint(cmd_tree, hf_cip_cm_timeout, tvb, offset, 2, timeout);
}

static void dissect_connection_triad(tvbuff_t *tvb, int offset, proto_tree *tree,
   int hf_conn_serial, int hf_vendor, int hf_orig_serial,
   cip_connection_triad_t *triad)
{
   guint32 ConnSerialNumber;
   guint32 VendorID;
   guint32 DeviceSerialNumber;

   proto_tree_add_item_ret_uint(tree, hf_conn_serial, tvb, offset, 2, ENC_LITTLE_ENDIAN, &ConnSerialNumber);
   proto_tree_add_item_ret_uint(tree, hf_vendor, tvb, offset + 2, 2, ENC_LITTLE_ENDIAN, &VendorID);
   proto_tree_add_item_ret_uint(tree, hf_orig_serial, tvb, offset + 4, 4, ENC_LITTLE_ENDIAN, &DeviceSerialNumber);

   if (triad)
   {
      triad->ConnSerialNumber = ConnSerialNumber;
      triad->VendorID = VendorID;
      triad->DeviceSerialNumber = DeviceSerialNumber;
   }
}

// Mark this message as belonging to a specific CIP connection index.
static void mark_cip_connection(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree)
{
    cip_conn_info_t* conn_val = (cip_conn_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_CONNECTION_INFO);
    if (conn_val)
    {
        proto_item* pi = proto_tree_add_uint(tree, hf_cip_connection, tvb, 0, 0, conn_val->connid);
        proto_item_set_generated(pi);
    }
}

// Save the Route or Connection Path for use in the response packet.
static void save_route_connection_path(packet_info* pinfo, tvbuff_t* tvb, int offset, guint path_size_bytes)
{
   if (pinfo->fd->visited)
   {
      return;
   }

   cip_req_info_t* preq_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
   if (preq_info)
   {
      preq_info->pRouteConnectionPath = wmem_alloc(wmem_file_scope(), path_size_bytes);
      preq_info->RouteConnectionPathLen = path_size_bytes / 2;
      tvb_memcpy(tvb, preq_info->pRouteConnectionPath, offset, path_size_bytes);
   }
}

static int get_connection_timeout_multiplier(guint32 timeout_value)
{
   guint32 timeout_multiplier;
   switch (timeout_value)
   {
   case 0:
      timeout_multiplier = 4;
      break;
   case 1:
      timeout_multiplier = 8;
      break;
   case 2:
      timeout_multiplier = 16;
      break;
   case 3:
      timeout_multiplier = 32;
      break;
   case 4:
      timeout_multiplier = 64;
      break;
   case 5:
      timeout_multiplier = 128;
      break;
   case 6:
      timeout_multiplier = 256;
      break;
   case 7:
      timeout_multiplier = 512;
      break;
   default:
      // Invalid
      timeout_multiplier = 0;
      break;
   }

   return timeout_multiplier;
}

static void fwd_open_analysis_safety_open(packet_info* pinfo, proto_item* cmd_item, cip_safety_epath_info_t* safety_fwdopen)
{
   if (safety_fwdopen->safety_seg == FALSE)
   {
      return;
   }

   if (safety_fwdopen->safety_open_type == CIP_SAFETY_OPEN_TYPE1)
   {
      expert_add_info(pinfo, cmd_item, &ei_cip_safety_open_type1);
   }
   else if (safety_fwdopen->safety_open_type == CIP_SAFETY_OPEN_TYPE2A)
   {
      expert_add_info(pinfo, cmd_item, &ei_cip_safety_open_type2a);
   }
   else if (safety_fwdopen->safety_open_type == CIP_SAFETY_OPEN_TYPE2B)
   {
      expert_add_info(pinfo, cmd_item, &ei_cip_safety_open_type2b);
   }
}

static void display_previous_route_connection_path(cip_req_info_t* preq_info, proto_tree* item_tree, tvbuff_t* tvb, packet_info* pinfo, int hf_path, int display_type);

// Display all Connection Information and Analysis.
static void display_connection_information_fwd_open_req(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree)
{
   cip_conn_info_t* conn_info = (cip_conn_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_CONNECTION_INFO);
   if (!conn_info)
   {
      return;
   }

   proto_item* conn_info_item = NULL;
   proto_tree* conn_info_tree = proto_tree_add_subtree(tree, tvb, 0, 0, ett_connection_info, &conn_info_item, "Connection Information");
   proto_item_set_generated(conn_info_item);

   mark_cip_connection(pinfo, tvb, conn_info_tree);

   proto_item* pi = proto_tree_add_float(conn_info_tree, hf_cip_cm_ot_timeout, tvb, 0, 0, (conn_info->O2T.rpi / 1000.0f) * conn_info->timeout_multiplier);
   proto_item_set_generated(pi);

   pi = proto_tree_add_float(conn_info_tree, hf_cip_cm_to_timeout, tvb, 0, 0, (conn_info->T2O.rpi / 1000.0f) * conn_info->timeout_multiplier);
   proto_item_set_generated(pi);

   if (conn_info->safety.safety_seg)
   {
      pi = proto_tree_add_float(conn_info_tree, hf_cip_safety_nte_ms, tvb, 0, 0, conn_info->safety.nte_value_ms);
      proto_item_set_generated(pi);
   }

   if (conn_info->close_frame != 0)
   {
      pi = proto_tree_add_uint(conn_info_tree, hf_cip_fwd_close_in, tvb, 0, 0, conn_info->close_frame);
      proto_item_set_generated(pi);
   }
   else
   {
      expert_add_info(pinfo, conn_info_item, &ei_cip_no_fwd_close);
   }

   fwd_open_analysis_safety_open(pinfo, conn_info_item, &conn_info->safety);
}

static void display_connection_information_fwd_open_rsp(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree, cip_req_info_t* preq_info)
{
   proto_item* conn_info_item = NULL;
   proto_tree* conn_info_tree = proto_tree_add_subtree(tree, tvb, 0, 0, ett_connection_info, &conn_info_item, "Connection Information");
   proto_item_set_generated(conn_info_item);

   mark_cip_connection(pinfo, tvb, conn_info_tree);

   display_previous_route_connection_path(preq_info, conn_info_tree, tvb, pinfo, hf_cip_cm_conn_path_size, DISPLAY_CONNECTION_PATH);
}

static void display_connection_information_fwd_close_req(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree)
{
   cip_conn_info_t* conn_info = (cip_conn_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_CONNECTION_INFO);
   if (!conn_info)
   {
      return;
   }

   proto_item* conn_info_item = NULL;
   proto_tree* conn_info_tree = proto_tree_add_subtree(tree, tvb, 0, 0, ett_connection_info, &conn_info_item, "Connection Information");
   proto_item_set_generated(conn_info_item);

   mark_cip_connection(pinfo, tvb, conn_info_tree);

   display_fwd_open_connection_path(conn_info, conn_info_tree, tvb, pinfo);

   proto_item *pi = proto_tree_add_uint(conn_info_tree, hf_cip_fwd_open_in, tvb, 0, 0, conn_info->open_req_frame);
   proto_item_set_generated(pi);

   // Show the API values
   pi = proto_tree_add_uint(conn_info_tree, hf_cip_cm_ot_api, tvb, 0, 0, conn_info->O2T.api);
   proto_item_set_generated(pi);

   pi = proto_tree_add_uint(conn_info_tree, hf_cip_cm_to_api, tvb, 0, 0, conn_info->T2O.api);
   proto_item_set_generated(pi);

   // Connection timeout values
   float ot_timeout_ms = (conn_info->O2T.rpi / 1000.0f) * conn_info->timeout_multiplier;
   float to_timeout_ms = (conn_info->T2O.rpi / 1000.0f) * conn_info->timeout_multiplier;
   proto_item* ot_timeout_item = proto_tree_add_float(conn_info_tree, hf_cip_cm_ot_timeout, tvb, 0, 0, ot_timeout_ms);
   proto_item_set_generated(ot_timeout_item);

   proto_item* to_timeout_item = proto_tree_add_float(conn_info_tree, hf_cip_cm_to_timeout, tvb, 0, 0, to_timeout_ms);
   proto_item_set_generated(to_timeout_item);

   if (conn_info->safety.safety_seg)
   {
      pi = proto_tree_add_float(conn_info_tree, hf_cip_safety_nte_ms, tvb, 0, 0, conn_info->safety.nte_value_ms);
      proto_item_set_generated(pi);
   }

   if (conn_info->safety.safety_seg)
   {
      // Make it obvious that the FwdClose is Safety, to match how the FwdOpen looks.
      col_append_str(pinfo->cinfo, COL_INFO, " [Safety]");
   }

}

static void display_connection_information_fwd_close_rsp(packet_info* pinfo, tvbuff_t* tvb, proto_tree* tree)
{
   cip_conn_info_t* conn_val = (cip_conn_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_CONNECTION_INFO);
   if (!conn_val)
   {
      return;
   }

   proto_item* conn_info_item = NULL;
   proto_tree* conn_info_tree = proto_tree_add_subtree(tree, tvb, 0, 0, ett_connection_info, &conn_info_item, "Connection Information");
   proto_item_set_generated(conn_info_item);

   mark_cip_connection(pinfo, tvb, conn_info_tree);

   cip_req_info_t* preq_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
   display_previous_route_connection_path(preq_info, conn_info_tree, tvb, pinfo, hf_cip_cm_conn_path_size, DISPLAY_CONNECTION_PATH);

   if (conn_val->safety.safety_seg)
   {
      // Make it obvious that the FwdClose is Safety, to match how the FwdOpen looks.
      col_append_str(pinfo->cinfo, COL_INFO, " [Safety]");
   }
}

//// Concurrent Connections
static int dissect_cip_cc_hop(packet_info* pinfo, tvbuff_t* tvb, int offset, proto_tree* hops_tree, guint8 hop_number)
{
    int parsed = 0;

    proto_item* item_hop = proto_tree_add_uint(hops_tree, hf_ext_net_seg_hop, tvb, offset, 0, hop_number);

    proto_tree* hop_tree = proto_item_add_subtree(item_hop, ett_cc_hop);
    proto_tree_add_item(hop_tree, hf_ext_net_seg_hop_egress_cip_port, tvb, offset + parsed, 1, ENC_LITTLE_ENDIAN);
    parsed++;

    proto_tree_add_item(hop_tree, hf_ext_net_seg_hop_link_adr_type, tvb, offset + parsed, 1, ENC_LITTLE_ENDIAN);
    proto_tree_add_item(hop_tree, hf_ext_net_seg_hop_number_of_linkadr, tvb, offset + parsed, 1, ENC_LITTLE_ENDIAN);
    parsed++;

    guint8 link_type = tvb_get_guint8(tvb, offset + 1) >> 4;
    guint8 number_of_links = tvb_get_guint8(tvb, offset + 1) & 0x0F;

    for (guint8 i = 0; i < number_of_links; i++)
    {
        switch (link_type)
        {
        case 0: // Link addresses
        {
            proto_tree_add_item(hop_tree, hf_ext_net_seg_link_address, tvb, offset + parsed, 1, ENC_LITTLE_ENDIAN);
            parsed++;
            break;
        }

        case 1: // IPv4 addresses encoding
            proto_tree_add_item(hop_tree, hf_ext_net_seg_link_ipv4, tvb, offset + parsed, 4, ENC_LITTLE_ENDIAN);
            parsed += 4;
            break;

        case 2: // Host Name addresses encoding
            parsed += dissect_cip_string_type(pinfo, hop_tree, item_hop, tvb, offset + parsed, hf_ext_net_seg_link_hostname, CIP_STRING_TYPE);

            // Add pad byte when string length is odd
            if (parsed % 2)
            {
                parsed++;
            }

            break;

        default:
            break;
        }
    }

    proto_item_set_len(item_hop, parsed);

    return parsed;
}

#define CC_PACKET_TYPE_MASK (0x001F)
int dissect_concurrent_connection_packet(packet_info* pinfo, tvbuff_t* tvb, int offset, proto_tree* tree)
{
    proto_item* type_item = proto_tree_add_item(tree, proto_cc, tvb, offset, -1, ENC_NA);
    proto_tree* CC_tree = proto_item_add_subtree(type_item, ett_cc_header);

    guint16 header_type = tvb_get_letohs(tvb, offset) & CC_PACKET_TYPE_MASK;

    int parsed_len = 0;
    if (header_type == 1)
    {
        static int* const options[] = {
           &hf_cip_cc_packet_type,
           &hf_cip_cc_packet_keepalive,
           &hf_cip_cc_packet_keepalive_hop_count,
           &hf_cip_cc_packet_reserved,
           NULL
        };

        proto_tree_add_bitmask(CC_tree, tvb, offset + parsed_len, hf_cip_cc_packet_options, ett_cc_header, options, ENC_LITTLE_ENDIAN);
        parsed_len += 2;

        guint32 CC_frame_length;
        proto_tree_add_item_ret_uint(CC_tree, hf_cip_cc_packet_length, tvb, offset + parsed_len, 2, ENC_LITTLE_ENDIAN, &CC_frame_length);
        parsed_len += 2;

        guint32 ccSeq;
        proto_tree_add_item_ret_uint(CC_tree, hf_cip_cc_packet_seq_number, tvb, offset + parsed_len, 4, ENC_LITTLE_ENDIAN, &ccSeq);
        col_append_fstr(pinfo->cinfo, COL_INFO, ", CC_SEQ=%010u", ccSeq);
        parsed_len += 4;

        proto_tree_add_item(CC_tree, hf_cip_cc_crc, tvb, offset + CC_frame_length, CC_CRC_LENGTH, ENC_LITTLE_ENDIAN);
        proto_tree_set_appendix(CC_tree, tvb, offset + CC_frame_length, CC_CRC_LENGTH);
    }
    else
    {
        expert_add_info(pinfo, type_item, &ei_cc_invalid_header_type);
    }

    proto_item_set_len(type_item, parsed_len);

    return parsed_len;
}

void proto_register_cc(void)
{
    static hf_register_info hf_cc[] =
    {
        /// Concurrent Connections
        { &hf_cip_cm_cc_version, { "Concurrent Connections Protocol Version", "cip.cm.cc_version", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},

        // Concurrent Connection Packet
        { &hf_cip_cc_packet_length,{ "Packet Length", "cip.cc.packet.length", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
        { &hf_cip_cc_packet_type,{ "Packet Type", "cip.cc.packet_type", FT_UINT16, BASE_DEC, VALS(cc_packet_type_vals), CC_PACKET_TYPE_MASK, NULL, HFILL } },
        { &hf_cip_cc_packet_options, { "Packet Type and Keep-alive", "cip.cc.packet.type_and_keepalive", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
        { &hf_cip_cc_packet_keepalive,{ "Keep-alive Flag", "cip.cc.packet.keep_alive_flag", FT_UINT16, BASE_HEX, NULL, 0x0020, NULL, HFILL } },
        { &hf_cip_cc_packet_keepalive_hop_count,{ "Keep-alive Hop Count", "cip.cc.packet.keep_alive_count", FT_UINT16, BASE_DEC, NULL, 0x01C0, NULL, HFILL } },
        { &hf_cip_cc_packet_reserved,{ "Reserved", "cip.cc.packet.reserved", FT_UINT16, BASE_HEX, NULL, 0xFE00, NULL, HFILL } },
        { &hf_cip_cc_packet_seq_number,{ "Concurrent Connection Sequence Count", "cip.cc.packet.sequence_count", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
        { &hf_cip_cc_crc,{ "CRC", "cip.cc.crc", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },

        // Concurrent Connection Path
        { &hf_ext_net_seg_hops_count,{ "Hops Count", "cip.cc.netsegment.hopsCount", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
        { &hf_ext_net_seg_length,{ "Length of Concurrent Connection Path", "cip.cc.netsegment.length", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },

        // Concurrent Connection Hops
        { &hf_ext_net_seg_hop,{ "CC Hop", "cip.cc.netsegment.hop", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
        { &hf_ext_net_seg_hop_egress_cip_port,{ "Egress Port", "cip.cc.netsegment.HopEgreeCipPort", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
        { &hf_ext_net_seg_hop_link_adr_type,{ "Link Address Type", "cip.cc.netsegment.HopLnkAdrType", FT_UINT8, BASE_DEC, VALS(cc_link_adr_type), 0xF0, NULL, HFILL } },
        { &hf_ext_net_seg_hop_number_of_linkadr,{ "Number of link addresses", "cip.cc.netsegment.HopNumberOfLnkAdr", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL } },
        { &hf_ext_net_seg_link_address,{ "Link address", "cip.cc.netsegment.link", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
        { &hf_ext_net_seg_link_ipv4,{ "IPv4 address", "cip.cc.netsegment.ip", FT_IPv4, BASE_NONE, NULL, 0, NULL, HFILL } },
        { &hf_ext_net_seg_link_hostname, { "Hostname", "cip.cc.netsegment.hostname", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
    };

    static ei_register_info ei_cc[] = {
        { &ei_cc_invalid_header_type, { "cip.cc.invalid_packet_type", PI_MALFORMED, PI_ERROR, "Invalid Concurrent Connections Packet Type", EXPFILL }},
    };

    proto_cc = proto_register_protocol("Concurrent Connection Packet",
        "CIPCC",
        "cipcc");

    proto_register_field_array(proto_cc, hf_cc, array_length(hf_cc));
    proto_register_subtree_array(ett_cc, array_length(ett_cc));

    expert_module_t* expert_cc = expert_register_protocol(proto_cc);
    expert_register_field_array(expert_cc, ei_cc, array_length(ei_cc));
}

// Offset - Starts after the Extended Network Segment Subtype
int dissect_concurrent_connection_network_segment(packet_info* pinfo, tvbuff_t* tvb, int offset, proto_tree* tree)
{
    guint32 hops_count;
    proto_tree_add_item_ret_uint(tree, hf_ext_net_seg_hops_count, tvb, offset, 1, ENC_LITTLE_ENDIAN, &hops_count);

    proto_tree_add_item(tree, hf_ext_net_seg_length, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
    int parsed_len = 2;

    for (guint32 i = 0; i < hops_count; i++)
    {
        parsed_len += dissect_cip_cc_hop(pinfo, tvb, offset + parsed_len, tree, i + 1);
    }

    // Add padding when the Network segment length is odd.
    if (parsed_len & 0x0001)
    {
        parsed_len++;
    }

    return parsed_len;
}

static void
dissect_cip_cm_fwd_open_req(cip_req_info_t *preq_info, proto_tree *cmd_tree, tvbuff_t *tvb, int offset,
   gboolean large_fwd_open, packet_info *pinfo, gboolean concurrent_connection)
{
   proto_item *pi;
   proto_tree *epath_tree;
   int conn_path_size, net_param_offset = 0;
   guint8 TransportClass_trigger;
   cip_simple_request_info_t connection_path;
   cip_safety_epath_info_t safety_fwdopen = {0};

   cip_connID_info_t O2T_info = {0};
   cip_connID_info_t T2O_info = {0};

   dissect_cip_cm_timeout(cmd_tree, tvb, offset);
   proto_tree_add_item_ret_uint( cmd_tree, hf_cip_cm_ot_connid, tvb, offset+2, 4, ENC_LITTLE_ENDIAN, &O2T_info.connID);
   proto_tree_add_item_ret_uint( cmd_tree, hf_cip_cm_to_connid, tvb, offset+6, 4, ENC_LITTLE_ENDIAN, &T2O_info.connID);

   // Add Connection IDs as hidden items so that it's easy to find all Connection IDs in different fields.
   pi = proto_tree_add_item(cmd_tree, hf_cip_connid, tvb, offset + 2, 4, ENC_LITTLE_ENDIAN);
   proto_item_set_hidden(pi);
   pi = proto_tree_add_item(cmd_tree, hf_cip_connid, tvb, offset + 6, 4, ENC_LITTLE_ENDIAN);
   proto_item_set_hidden(pi);

   cip_connection_triad_t conn_triad;
   dissect_connection_triad(tvb, offset + 10, cmd_tree,
      hf_cip_cm_conn_serial_num, hf_cip_cm_vendor, hf_cip_cm_orig_serial_num,
      &conn_triad);

   if (concurrent_connection)
   {
      // For CC there is additional 1 byte containing CC Version and one reserved byte after.
      proto_tree_add_item(cmd_tree, hf_cip_cm_cc_version, tvb, offset + 18, 1, ENC_LITTLE_ENDIAN);
      offset += 2;
   }

   guint32 timeout_value;
   proto_tree_add_item_ret_uint(cmd_tree, hf_cip_cm_timeout_multiplier, tvb, offset+18, 1, ENC_LITTLE_ENDIAN, &timeout_value);
   guint32 timeout_multiplier = get_connection_timeout_multiplier(timeout_value);

   proto_tree_add_item(cmd_tree, hf_cip_reserved24, tvb, offset+19, 3, ENC_LITTLE_ENDIAN);

   // O->T parameters
   proto_tree_add_item_ret_uint(cmd_tree, hf_cip_cm_ot_rpi, tvb, offset + 22, 4, ENC_LITTLE_ENDIAN, &O2T_info.rpi);
   if (large_fwd_open)
   {
      dissect_net_param32(tvb, offset+26, cmd_tree,
                 hf_cip_cm_ot_net_params32, hf_cip_cm_lfwo_own, hf_cip_cm_lfwo_typ,
                 hf_cip_cm_lfwo_prio, hf_cip_cm_lfwo_fixed_var, hf_cip_cm_lfwo_con_size, ett_cm_ncp, &O2T_info);
      net_param_offset = 4;
   }
   else
   {
      dissect_net_param16(tvb, offset+26, cmd_tree,
                 hf_cip_cm_ot_net_params16, hf_cip_cm_fwo_own, hf_cip_cm_fwo_typ,
                 hf_cip_cm_fwo_prio, hf_cip_cm_fwo_fixed_var, hf_cip_cm_fwo_con_size, ett_cm_ncp, &O2T_info);
      net_param_offset = 2;
   }

   // T->O parameters
   proto_tree_add_item_ret_uint(cmd_tree, hf_cip_cm_to_rpi, tvb, offset + 26 + net_param_offset, 4, ENC_LITTLE_ENDIAN, &T2O_info.rpi);
   if (large_fwd_open)
   {
      dissect_net_param32(tvb, offset+26+net_param_offset+4, cmd_tree,
                 hf_cip_cm_to_net_params32, hf_cip_cm_lfwo_own, hf_cip_cm_lfwo_typ,
                 hf_cip_cm_lfwo_prio, hf_cip_cm_lfwo_fixed_var, hf_cip_cm_lfwo_con_size, ett_cm_ncp, &T2O_info);
      net_param_offset += 4;
   }
   else
   {
      dissect_net_param16(tvb, offset+26+net_param_offset+4, cmd_tree,
                 hf_cip_cm_to_net_params16, hf_cip_cm_fwo_own, hf_cip_cm_fwo_typ,
                 hf_cip_cm_fwo_prio, hf_cip_cm_fwo_fixed_var, hf_cip_cm_fwo_con_size, ett_cm_ncp, &T2O_info);
      net_param_offset += 2;
   }

   TransportClass_trigger = tvb_get_guint8( tvb, offset+26+net_param_offset+4);
   dissect_transport_type_trigger(tvb, offset+26+net_param_offset+4, cmd_tree, hf_cip_cm_transport_type_trigger,
                                  hf_cip_cm_fwo_dir, hf_cip_cm_fwo_trigg, hf_cip_cm_fwo_class, ett_cm_ttt);

   /* Add path size */
   conn_path_size = tvb_get_guint8( tvb, offset+26+net_param_offset+5 )*2;
   proto_tree_add_item(cmd_tree, hf_cip_cm_conn_path_size, tvb, offset+26+net_param_offset+5, 1, ENC_LITTLE_ENDIAN);

   /* Add the epath */
   epath_tree = proto_tree_add_subtree(cmd_tree, tvb, offset+26+net_param_offset+6, conn_path_size, ett_path, &pi, "Connection Path: ");
   dissect_epath( tvb, pinfo, epath_tree, pi, offset+26+net_param_offset+6, conn_path_size, FALSE, FALSE, &connection_path, &safety_fwdopen, DISPLAY_CONNECTION_PATH, NULL, FALSE);
   save_route_connection_path(pinfo, tvb, offset + 26 + net_param_offset + 6, conn_path_size);

   if (pinfo->fd->visited)
   {
       /* "Connection" is created during ForwardOpen reply (which will be after ForwardOpen request),
          so ForwardOpen request can only be marked after the first pass */
       enip_mark_connection_triad(pinfo, &conn_triad);
   }
   else
   {
      if (preq_info != NULL)
      {
         DISSECTOR_ASSERT(preq_info->connInfo == NULL);
         preq_info->connInfo = wmem_new0(wmem_file_scope(), cip_conn_info_t);

         preq_info->connInfo->triad = conn_triad;
         preq_info->connInfo->open_req_frame = pinfo->num;

         preq_info->connInfo->O2T = O2T_info;
         preq_info->connInfo->T2O = T2O_info;

         preq_info->connInfo->TransportClass_trigger = TransportClass_trigger;
         preq_info->connInfo->timeout_multiplier = timeout_multiplier;
         preq_info->connInfo->safety = safety_fwdopen;
         preq_info->connInfo->connection_path = connection_path;

         preq_info->connInfo->FwdOpenPathLenBytes = conn_path_size;
         preq_info->connInfo->pFwdOpenPathData = wmem_alloc(wmem_file_scope(), conn_path_size);
         tvb_memcpy(tvb, preq_info->connInfo->pFwdOpenPathData, offset + 26 + net_param_offset + 6, conn_path_size);
      }
   }

   display_connection_information_fwd_open_req(pinfo, tvb, cmd_tree);
}

static void display_previous_route_connection_path(cip_req_info_t *preq_info, proto_tree *item_tree, tvbuff_t *tvb, packet_info *pinfo, int hf_path, int display_type)
{
   if (preq_info && preq_info->RouteConnectionPathLen && preq_info->pRouteConnectionPath)
   {
      tvbuff_t* tvbIOI = tvb_new_real_data((const guint8 *)preq_info->pRouteConnectionPath, preq_info->RouteConnectionPathLen * 2, preq_info->RouteConnectionPathLen * 2);
      if (!tvbIOI)
      {
         return;
      }

      proto_item* pi = proto_tree_add_uint(item_tree, hf_path, tvb, 0, 0, preq_info->RouteConnectionPathLen);
      proto_item_set_generated(pi);

      proto_tree* epath_tree = proto_tree_add_subtree(item_tree, tvb, 0, 0, ett_path, &pi, "Route/Connection Path: ");
      proto_item_set_generated(pi);

      cip_simple_request_info_t route_conn_path;
      dissect_epath(tvbIOI, pinfo, epath_tree, pi, 0, preq_info->RouteConnectionPathLen * 2, TRUE, FALSE, &route_conn_path, NULL, display_type, NULL, FALSE);
      tvb_free(tvbIOI);
   }
}

gboolean cip_connection_triad_match(const cip_connection_triad_t* left, const cip_connection_triad_t* right)
{
   return (left->ConnSerialNumber == right->ConnSerialNumber) &&
      (left->VendorID == right->VendorID) &&
      (left->DeviceSerialNumber == right->DeviceSerialNumber);
}

static int
dissect_cip_cm_fwd_open_rsp_success(cip_req_info_t *preq_info, proto_tree *tree, tvbuff_t *tvb, int offset, packet_info *pinfo)
{
   int parsed_len = 26;

   guint16 init_rollover_value = 0, init_timestamp_value = 0;
   proto_tree *pid_tree, *safety_tree;
   cip_connection_triad_t target_triad = {0};

   /* Display originator to target connection ID */
   guint32 O2TConnID;
   proto_tree_add_item_ret_uint(tree, hf_cip_cm_ot_connid, tvb, offset, 4, ENC_LITTLE_ENDIAN, &O2TConnID);

   /* Display target to originator connection ID */
   guint32 T2OConnID;
   proto_tree_add_item_ret_uint(tree, hf_cip_cm_to_connid, tvb, offset+4, 4, ENC_LITTLE_ENDIAN, &T2OConnID);

   // Add Connection IDs as hidden items so that it's easy to find all Connection IDs in different fields.
   proto_item* pi = proto_tree_add_item(tree, hf_cip_connid, tvb, offset, 4, ENC_LITTLE_ENDIAN);
   proto_item_set_hidden(pi);
   pi = proto_tree_add_item(tree, hf_cip_connid, tvb, offset + 4, 4, ENC_LITTLE_ENDIAN);
   proto_item_set_hidden(pi);

   cip_connection_triad_t conn_triad;
   dissect_connection_triad(tvb, offset + 8, tree,
      hf_cip_cm_conn_serial_num, hf_cip_cm_vendor, hf_cip_cm_orig_serial_num,
      &conn_triad);

   /* Display originator to target actual packet interval */
   guint32 O2TAPI;
   proto_tree_add_item_ret_uint(tree, hf_cip_cm_ot_api, tvb, offset + 16, 4, ENC_LITTLE_ENDIAN, &O2TAPI);

   /* Display originator to target actual packet interval */
   guint32 T2OAPI;
   proto_tree_add_item_ret_uint(tree, hf_cip_cm_to_api, tvb, offset + 20, 4, ENC_LITTLE_ENDIAN, &T2OAPI);

   /* Display the application reply size */
   guint16 app_rep_size = tvb_get_guint8( tvb, offset+24 ) * 2;
   proto_tree_add_item(tree, hf_cip_cm_app_reply_size, tvb, offset+24, 1, ENC_LITTLE_ENDIAN);

   /* Display the Reserved byte */
   proto_tree_add_item(tree, hf_cip_reserved8, tvb, offset+25, 1, ENC_LITTLE_ENDIAN );

   // Handle the Application Reply Data.
   if (app_rep_size > 0)
   {
      if ((preq_info == NULL) || (preq_info->connInfo == NULL) ||
          (preq_info->connInfo->safety.safety_seg == FALSE))
      {
         proto_tree_add_item(tree, hf_cip_cm_app_reply_data, tvb, offset+26, app_rep_size, ENC_NA );
      }
      else if (preq_info->connInfo->safety.format == CIP_SAFETY_BASE_FORMAT)
      {
         safety_tree = proto_tree_add_subtree( tree, tvb, offset+26, 10, ett_cip_cm_safety, NULL, "Safety Application Reply Data");
         proto_tree_add_item( safety_tree, hf_cip_cm_consumer_number, tvb, offset+26, 2, ENC_LITTLE_ENDIAN);
         pid_tree = proto_tree_add_subtree( safety_tree, tvb, offset+28, 8, ett_cip_cm_pid, NULL, "PID/CID");
         proto_tree_add_item( pid_tree, hf_cip_cm_targ_vendor_id, tvb, offset+28, 2, ENC_LITTLE_ENDIAN);
         target_triad.VendorID = tvb_get_letohs(tvb, offset+28);

         proto_tree_add_item_ret_uint( pid_tree, hf_cip_cm_targ_dev_serial_num, tvb, offset+30, 4, ENC_LITTLE_ENDIAN, &target_triad.DeviceSerialNumber);

         proto_tree_add_item( pid_tree, hf_cip_cm_targ_conn_serial_num, tvb, offset+34, 2, ENC_LITTLE_ENDIAN);
         target_triad.ConnSerialNumber = tvb_get_letohs(tvb, offset+34);

         if (app_rep_size > 10)
            proto_tree_add_item(tree, hf_cip_cm_app_reply_data, tvb, offset+36, app_rep_size-10, ENC_NA );
      }
      else if (preq_info->connInfo->safety.format == CIP_SAFETY_EXTENDED_FORMAT)
      {
         safety_tree = proto_tree_add_subtree( tree, tvb, offset+26, 14, ett_cip_cm_safety, NULL, "Safety Application Reply Data");
         proto_tree_add_item( safety_tree, hf_cip_cm_consumer_number, tvb, offset+26, 2, ENC_LITTLE_ENDIAN);
         pid_tree = proto_tree_add_subtree( safety_tree, tvb, offset+28, 12, ett_cip_cm_pid, NULL, "PID/CID");
         proto_tree_add_item( pid_tree, hf_cip_cm_targ_vendor_id, tvb, offset+28, 2, ENC_LITTLE_ENDIAN);
         target_triad.VendorID = tvb_get_letohs(tvb, offset+28);

         proto_tree_add_item_ret_uint( pid_tree, hf_cip_cm_targ_dev_serial_num, tvb, offset+30, 4, ENC_LITTLE_ENDIAN, &target_triad.DeviceSerialNumber);

         proto_tree_add_item( pid_tree, hf_cip_cm_targ_conn_serial_num, tvb, offset+34, 2, ENC_LITTLE_ENDIAN);
         target_triad.ConnSerialNumber = tvb_get_letohs(tvb, offset+34);

         proto_tree_add_item( pid_tree, hf_cip_cm_initial_timestamp, tvb, offset+36, 2, ENC_LITTLE_ENDIAN);
         init_timestamp_value = tvb_get_letohs(tvb, offset+36);
         proto_tree_add_item( pid_tree, hf_cip_cm_initial_rollover, tvb, offset+38, 2, ENC_LITTLE_ENDIAN);
         init_rollover_value = tvb_get_letohs(tvb, offset+38);

         if (app_rep_size > 14)
            proto_tree_add_item(tree, hf_cip_cm_app_reply_data, tvb, offset+40, app_rep_size-14, ENC_NA );
      }
   }

   display_connection_information_fwd_open_rsp(pinfo, tvb, tree, preq_info);

   /* See if we've captured the ForwardOpen request.  If so some of the conversation data has already been
      populated and we just need to update it. */
   if (pinfo->fd->visited)
      return parsed_len + app_rep_size;

   if ((preq_info != NULL) && (preq_info->connInfo != NULL))
   {
      /* Ensure the connection triad matches before updating the connection IDs */
      if (cip_connection_triad_match(&(preq_info->connInfo->triad), &conn_triad))
      {
         /* Update the connection IDs as ForwardOpen reply is allowed to update them from
            the ForwardOpen request */
         preq_info->connInfo->O2T.connID = O2TConnID;
         preq_info->connInfo->T2O.connID = T2OConnID;

         preq_info->connInfo->O2T.api = O2TAPI;
         preq_info->connInfo->T2O.api = T2OAPI;
         if (preq_info->connInfo->safety.safety_seg == TRUE)
         {
             preq_info->connInfo->safety.running_rollover_value = init_rollover_value;
             preq_info->connInfo->safety.running_timestamp_value = init_timestamp_value;
             preq_info->connInfo->safety.target_triad = target_triad;
             preq_info->connInfo->safety.seen_non_zero_timestamp = FALSE;
         }
      }
   }

   return parsed_len + app_rep_size;
}

static void dissect_cip_cm_unconnected_send_req(proto_tree* cmd_data_tree, tvbuff_t* tvb, int offset, packet_info* pinfo)
{
   /* Display timeout fields */
   dissect_cip_cm_timeout(cmd_data_tree, tvb, offset);

   /* Message request size */
   guint16 msg_req_siz = tvb_get_letohs(tvb, offset + 2);
   proto_tree_add_item(cmd_data_tree, hf_cip_cm_msg_req_size, tvb, offset + 2, 2, ENC_LITTLE_ENDIAN);

   /* Message Request */
   proto_tree* temp_tree = proto_tree_add_subtree(cmd_data_tree, tvb, offset + 4, msg_req_siz, ett_cm_mes_req, NULL, "CIP Embedded Message Request");

   /*
   ** We call ourselves again to dissect embedded packet
   */

   col_append_str(pinfo->cinfo, COL_INFO, ": ");

   tvbuff_t* next_tvb = tvb_new_subset_length(tvb, offset + 4, msg_req_siz);
   cip_req_info_t* preq_info = (cip_req_info_t *)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
   cip_req_info_t* pembedded_req_info = NULL;
   if (preq_info)
   {
      if (preq_info->pData == NULL)
      {
         pembedded_req_info = wmem_new0(wmem_file_scope(), cip_req_info_t);
         preq_info->pData = pembedded_req_info;
      }
      else
      {
         pembedded_req_info = (cip_req_info_t*)preq_info->pData;
      }
   }

   dissect_cip_data(temp_tree, next_tvb, 0, pinfo, pembedded_req_info, NULL, FALSE);

   if (msg_req_siz % 2)
   {
      /* Pad byte */
      proto_tree_add_item(cmd_data_tree, hf_cip_pad8, tvb, offset + 4 + msg_req_siz, 1, ENC_LITTLE_ENDIAN);
      msg_req_siz++;  /* include the padding */
   }

   /* Route Path Size */
   guint16 route_path_size = tvb_get_guint8(tvb, offset + 4 + msg_req_siz) * 2;
   proto_tree_add_item(cmd_data_tree, hf_cip_cm_route_path_size, tvb, offset + 4 + msg_req_siz, 1, ENC_LITTLE_ENDIAN);

   /* Display the Reserved byte */
   proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset + 5 + msg_req_siz, 1, ENC_LITTLE_ENDIAN);

   /* Route Path */
   proto_item* epath_item;
   proto_tree* epath_tree = proto_tree_add_subtree(cmd_data_tree, tvb, offset + 6 + msg_req_siz, route_path_size, ett_path, &epath_item, "Route Path: ");
   dissect_epath(tvb, pinfo, epath_tree, epath_item, offset + 6 + msg_req_siz, route_path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY, NULL, FALSE);

   save_route_connection_path(pinfo, tvb, offset + 6 + msg_req_siz, route_path_size);
}

static void dissect_cip_cm_fwd_close_req(proto_tree* cmd_data_tree, tvbuff_t* tvb, int offset, packet_info* pinfo)
{
   cip_simple_request_info_t conn_path;

   dissect_cip_cm_timeout(cmd_data_tree, tvb, offset);

   cip_connection_triad_t conn_triad;
   dissect_connection_triad(tvb, offset + 2, cmd_data_tree,
      hf_cip_cm_conn_serial_num, hf_cip_cm_vendor, hf_cip_cm_orig_serial_num,
      &conn_triad);

   /* Add the path size */
   guint16 conn_path_size = tvb_get_guint8(tvb, offset + 10) * 2;
   proto_tree_add_item(cmd_data_tree, hf_cip_cm_conn_path_size, tvb, offset + 10, 1, ENC_LITTLE_ENDIAN);

   /* Display the Reserved byte */
   proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset + 11, 1, ENC_LITTLE_ENDIAN);

   /* Add the EPATH */
   proto_item *pi;
   proto_tree* epath_tree = proto_tree_add_subtree(cmd_data_tree, tvb, offset + 12, conn_path_size, ett_path, &pi, "Connection Path: ");
   dissect_epath(tvb, pinfo, epath_tree, pi, offset + 12, conn_path_size, FALSE, FALSE, &conn_path, NULL, DISPLAY_CONNECTION_PATH, NULL, FALSE);
   save_route_connection_path(pinfo, tvb, offset + 12, conn_path_size);

   enip_close_cip_connection(pinfo, &conn_triad);
   display_connection_information_fwd_close_req(pinfo, tvb, cmd_data_tree);
}

static int dissect_cip_cm_fwd_close_rsp_success(proto_tree* cmd_data_tree, tvbuff_t* tvb, int offset, packet_info* pinfo, proto_item* cmd_item)
{
   cip_connection_triad_t conn_triad;
   dissect_connection_triad(tvb, offset, cmd_data_tree,
      hf_cip_cm_conn_serial_num, hf_cip_cm_vendor, hf_cip_cm_orig_serial_num,
      &conn_triad);

   /* Display the application reply size */
   guint16 app_rep_size = tvb_get_guint8(tvb, offset + 8) * 2;
   proto_tree_add_item(cmd_data_tree, hf_cip_cm_app_reply_size, tvb, offset + 8, 1, ENC_LITTLE_ENDIAN);

   /* Display the Reserved byte */
   proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset + 9, 1, ENC_LITTLE_ENDIAN);
   if (app_rep_size > 0)
   {
      if (tvb_reported_length_remaining(tvb, offset + 10) < app_rep_size)
      {
         expert_add_info(pinfo, cmd_item, &ei_mal_fwd_close_missing_data);
         return 0;
      }
      proto_tree_add_item(cmd_data_tree, hf_cip_cm_app_reply_data, tvb, offset + 10, app_rep_size, ENC_NA);
   }

   if (!pinfo->fd->visited)
      enip_mark_connection_triad(pinfo, &conn_triad);

   display_connection_information_fwd_close_rsp(pinfo, tvb, cmd_data_tree);

   return 10 + app_rep_size;
}

static void display_previous_request_path(cip_req_info_t *preq_info, proto_tree *item_tree, tvbuff_t *tvb, packet_info *pinfo, proto_item* msp_item, gboolean is_msp_item)
{
   if (preq_info && preq_info->IOILen && preq_info->pIOI)
   {
      proto_item *pi;
      proto_tree *epath_tree;
      tvbuff_t* tvbIOI;

      tvbIOI = tvb_new_real_data((const guint8 *)preq_info->pIOI, preq_info->IOILen * 2, preq_info->IOILen * 2);
      if (tvbIOI)
      {
         pi = proto_tree_add_uint(item_tree, hf_cip_request_path_size, tvb, 0, 0, preq_info->IOILen);
         proto_item_set_generated(pi);

         /* Add the epath */
         epath_tree = proto_tree_add_subtree(item_tree, tvb, 0, 0, ett_path, &pi, "Request Path: ");
         proto_item_set_generated(pi);

         if (preq_info->ciaData == NULL)
         {
            preq_info->ciaData = wmem_new(wmem_file_scope(), cip_simple_request_info_t);
         }

         dissect_epath(tvbIOI, pinfo, epath_tree, pi, 0, preq_info->IOILen * 2, TRUE, FALSE, preq_info->ciaData, NULL, DISPLAY_REQUEST_PATH, msp_item, is_msp_item);
         tvb_free(tvbIOI);
      }
   }
}

static void
dissect_cip_cm_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
{
   proto_item *rrsc_item, *status_item;
   proto_tree *rrsc_tree, *cmd_data_tree;
   int req_path_size;
   unsigned char service, gen_status, add_stat_size;
   unsigned short add_status;
   int i;
   cip_req_info_t *preq_info;

   service = tvb_get_guint8( tvb, offset );

   /* Special handling for Unconnected send response. If successful, embedded service code is sent.
    * If failed, it can be either an Unconnected send response or the embedded service code response. */
   preq_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0 );
   if (  preq_info != NULL && ( service & CIP_SC_RESPONSE_MASK )
      && preq_info->bService == SC_CM_UNCON_SEND
      )
   {
      gen_status = tvb_get_guint8( tvb, offset+2 );
      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;
      if ( add_stat_size == 2 )
         add_status = tvb_get_letohs( tvb, offset + 4 );
      else
         add_status = 0;

      if(   gen_status == CI_GRC_SUCCESS
         || ( ( service & CIP_SC_MASK ) != SC_CM_UNCON_SEND )
         || !(  ( gen_status == CI_GRC_FAILURE && (add_status == CM_ES_UNCONNECTED_REQUEST_TIMED_OUT ||
                                                   add_status == CM_ES_PORT_NOT_AVAILABLE ||
                                                   add_status == CM_ES_LINK_ADDRESS_NOT_VALID ||
                                                   add_status == CM_ES_INVALID_SEGMENT_IN_CONN_PATH ||
                                                   add_status == CM_ES_LINK_ADDRESS_TO_SELF_INVALID))
             || gen_status == CI_GRC_NO_RESOURCE
             || gen_status == CI_GRC_BAD_PATH
             )
         )
      {
         cip_req_info_t* pembedded_req_info = (cip_req_info_t*)preq_info->pData;

         if ( pembedded_req_info )
         {
            tvbuff_t *next_tvb;
            void *p_save_proto_data;
            gint service_index;
            heur_dtbl_entry_t *hdtbl_entry;

            p_save_proto_data = p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0 );
            p_remove_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
            p_add_proto_data(wmem_file_scope(), pinfo, proto_cip, 0, pembedded_req_info );

            proto_item_set_generated(proto_tree_add_uint_format( item_tree, hf_cip_cm_sc, tvb, 0, 0, SC_CM_UNCON_SEND|CIP_SC_RESPONSE_MASK, "Service: Unconnected Send (Response)" ));
            next_tvb = tvb_new_subset_length(tvb, offset, item_length);

            display_previous_request_path(pembedded_req_info, item_tree, tvb, pinfo, NULL, FALSE);
            display_previous_route_connection_path(preq_info, item_tree, tvb, pinfo, hf_cip_cm_route_path_size, NO_DISPLAY);

            /* Check to see if service is 'generic' */
            try_val_to_str_idx((service & CIP_SC_MASK), cip_sc_vals, &service_index);

            cip_service_info_t* service_entry = cip_get_service(pinfo, service);
            if ( pembedded_req_info && pembedded_req_info->dissector )
            {
               call_dissector(pembedded_req_info->dissector, next_tvb, pinfo, item_tree );
            }
            else if (service_index >= 0 && !service_entry)
            {
               /* See if object dissector wants to override generic service handling */
               if (!dissector_try_heuristic(heur_subdissector_service, tvb, pinfo, item_tree, &hdtbl_entry, NULL))
               {
                   dissect_cip_generic_service_rsp(tvb, pinfo, item_tree);
               }
            }
            else if (service_entry)
            {
               dissect_cip_object_specific_service(tvb, pinfo, item_tree, NULL, service_entry);
            }
            else
            {
               call_dissector( cip_class_generic_handle, next_tvb, pinfo, item_tree );
            }

            p_remove_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
            p_add_proto_data(wmem_file_scope(), pinfo, proto_cip, 0, p_save_proto_data);

            /* Return early because the response was only the embedded message response. */
            return;
         }
      }
   }

   col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP CM");

   /* Add Service code & Request/Response tree */
   rrsc_tree = proto_tree_add_subtree( item_tree, tvb, offset, 1, ett_cm_rrsc, &rrsc_item, "Service: " );

   /* Add Request/Response */
   proto_tree_add_item( rrsc_tree, hf_cip_reqrsp, tvb, offset, 1, ENC_LITTLE_ENDIAN );

   /* watch for service collisions */
   proto_item_append_text( rrsc_item, "%s (%s)",
               val_to_str( ( service & CIP_SC_MASK ),
                  cip_sc_vals_cm , "Unknown Service (0x%02x)"),
               val_to_str_const( ( service & CIP_SC_RESPONSE_MASK )>>7,
                  cip_sc_rr, "") );

   /* Add Service code */
   proto_tree_add_item(rrsc_tree, hf_cip_cm_sc, tvb, offset, 1, ENC_LITTLE_ENDIAN );
   add_cip_service_to_info_column(pinfo, service, cip_sc_vals_cm);

   if( service & CIP_SC_RESPONSE_MASK )
   {
      /* Response message */
      gen_status = tvb_get_guint8( tvb, offset+2 );
      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;

      if (gen_status == CI_GRC_FAILURE)
      {
         /* Dissect object specific error codes */
         proto_tree* status_tree = proto_tree_add_subtree(item_tree, tvb, offset+2, 1, ett_status_item, &status_item, "Status: " );

         /* Add general status */
         proto_tree_add_item(status_tree, hf_cip_cm_genstat, tvb, offset+2, 1, ENC_LITTLE_ENDIAN );
         proto_item_append_text( status_item, "%s", val_to_str_ext( gen_status,
                        &cip_gs_vals_ext , "Unknown Response (%x)")   );

         /* Add additional status size */
         proto_tree_add_item(status_tree, hf_cip_cm_addstat_size, tvb, offset+3, 1, ENC_LITTLE_ENDIAN);

         if( add_stat_size )
         {
            add_status = tvb_get_letohs( tvb, offset + 4 );
            proto_tree_add_item(status_tree, hf_cip_cm_ext_status, tvb, offset+4, 2, ENC_LITTLE_ENDIAN );
            proto_item_append_text(status_item, ", Extended: %s", val_to_str_ext(add_status, &cip_cm_ext_st_vals_ext, "Reserved (0x%04x)"));

            switch(add_status)
            {
            case CM_ES_RPI_NOT_ACCEPTABLE:
               if (add_stat_size < 3)
               {
                  expert_add_info(pinfo, status_item, &ei_mal_rpi_no_data);
               }
               else
               {
                  proto_tree_add_item(status_tree, hf_cip_cm_ext112_ot_rpi_type, tvb, offset+6, 1, ENC_LITTLE_ENDIAN );
                  proto_tree_add_item(status_tree, hf_cip_cm_ext112_to_rpi_type, tvb, offset+7, 1, ENC_LITTLE_ENDIAN );
                  proto_tree_add_item(status_tree, hf_cip_cm_ext112_ot_rpi, tvb, offset + 8, 4, ENC_LITTLE_ENDIAN);
                  proto_tree_add_item(status_tree, hf_cip_cm_ext112_to_rpi, tvb, offset + 12, 4, ENC_LITTLE_ENDIAN);
               }
               break;
            case CM_ES_INVALID_CONFIGURATION_SIZE:
               proto_tree_add_item(status_tree, hf_cip_cm_ext126_size, tvb, offset+6, 2, ENC_LITTLE_ENDIAN );
               break;
            case CM_ES_INVALID_OT_SIZE:
               proto_tree_add_item(status_tree, hf_cip_cm_ext127_size, tvb, offset+6, 2, ENC_LITTLE_ENDIAN );
               break;
            case CM_ES_INVALID_TO_SIZE:
               proto_tree_add_item(status_tree, hf_cip_cm_ext128_size, tvb, offset+6, 2, ENC_LITTLE_ENDIAN );
               break;
            default:
               /* Add additional status */
               if (add_stat_size > 1)
               {
                  proto_tree* add_status_tree = proto_tree_add_subtree( status_tree, tvb, offset+4, add_stat_size, ett_cm_add_status_item, NULL, "Additional Status" );

                  for( i=0; i < add_stat_size-2; i += 2 )
                     proto_tree_add_item(add_status_tree, hf_cip_cm_add_status, tvb, offset+4+i, 2, ENC_LITTLE_ENDIAN );
               }
            }
         }
      }

      /* If there is any command specific data create a sub-tree for it */
      int data_len = item_length - 4 - add_stat_size;
      if (data_len > 0)
      {
         int parsed_len = 0;
         offset += (4 + add_stat_size);

         proto_item *cmd_item;
         cmd_data_tree = proto_tree_add_subtree( item_tree, tvb, offset, data_len,
                                                 ett_cm_cmd_data, &cmd_item, "Command Specific Data" );

         if( gen_status == CI_GRC_SUCCESS )
         {
           /* Success responses */
           switch (service & CIP_SC_MASK)
           {
           case SC_CM_FWD_OPEN:
           case SC_CM_LARGE_FWD_OPEN:
           case SC_CM_CONCURRENT_FWD_OPEN:
              parsed_len = dissect_cip_cm_fwd_open_rsp_success(preq_info, cmd_data_tree, tvb, offset, pinfo);
              break;
           case SC_CM_FWD_CLOSE:
           case SC_CM_CONCURRENT_FWD_CLOSE:
              parsed_len = dissect_cip_cm_fwd_close_rsp_success(cmd_data_tree, tvb, offset, pinfo, cmd_item);
              break;
            case SC_CM_GET_CONN_OWNER:
            {
               /* Get Connection owner response (Success) */
               proto_tree_add_item( cmd_data_tree, hf_cip_cm_gco_conn, tvb, offset, 1, ENC_LITTLE_ENDIAN);
               proto_tree_add_item( cmd_data_tree, hf_cip_cm_gco_coo_conn, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
               proto_tree_add_item( cmd_data_tree, hf_cip_cm_gco_roo_conn, tvb, offset+2, 1, ENC_LITTLE_ENDIAN);
               proto_tree_add_item( cmd_data_tree, hf_cip_cm_gco_last_action, tvb, offset+3, 1, ENC_LITTLE_ENDIAN);

               dissect_connection_triad(tvb, offset + 4, cmd_data_tree,
                  hf_cip_cm_conn_serial_num, hf_cip_cm_vendor, hf_cip_cm_orig_serial_num,
                  NULL);

               parsed_len = 12;
            }
            break;
            case SC_CM_UNCON_SEND:  // Unconnected send response (Success)
            default:
               parsed_len = 0;
               break;
            }
         }
         else
         {
            /* Error responses */
            switch (service & CIP_SC_MASK)
            {
            case SC_CM_FWD_OPEN:
            case SC_CM_LARGE_FWD_OPEN:
            case SC_CM_FWD_CLOSE:
            case SC_CM_CONCURRENT_FWD_OPEN:
            case SC_CM_CONCURRENT_FWD_CLOSE:
            {
               /* Forward open and forward close error response look the same */
               cip_connection_triad_t conn_triad;
               dissect_connection_triad(tvb, offset, cmd_data_tree,
                  hf_cip_cm_conn_serial_num, hf_cip_cm_vendor, hf_cip_cm_orig_serial_num,
                  &conn_triad);

               proto_tree_add_item(cmd_data_tree, hf_cip_cm_remain_path_size, tvb, offset+8, 1, ENC_LITTLE_ENDIAN);
               proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+9, 1, ENC_LITTLE_ENDIAN);

               /* With an error reply the connection will either never be established or it has since already closed
                  That means the conversation should end too */
               enip_close_cip_connection(pinfo, &conn_triad);
               if (preq_info != NULL)
               {
                  /* Remove any connection information */
                  preq_info->connInfo = NULL;
               }


               display_previous_route_connection_path(preq_info, cmd_data_tree, tvb, pinfo, hf_cip_cm_conn_path_size, DISPLAY_CONNECTION_PATH);

               parsed_len = 10;
               break;
            }
            case SC_CM_UNCON_SEND:
               /* Unconnected send response (Unsuccess) */
               proto_tree_add_item(cmd_data_tree, hf_cip_cm_remain_path_size, tvb, offset, 1, ENC_LITTLE_ENDIAN);
               proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
               display_previous_route_connection_path(preq_info, item_tree, tvb, pinfo, hf_cip_cm_route_path_size, NO_DISPLAY);
               parsed_len = 2;
               break;
            default:
               parsed_len = 0;
               break;
            }
         } /* end of if-else( CI_CRC_SUCCESS ) */

         int remain_len = tvb_reported_length_remaining(tvb, offset + parsed_len);
         if (remain_len > 0)
         {
            proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset + parsed_len, remain_len, ENC_NA);
         }
      } /* End of if command-specific data present */

   } /* End of if reply */
   else
   {
      /* Request message */

      req_path_size = tvb_get_guint8( tvb, offset+1 )*2;

      /* If there is any command specific data creat a sub-tree for it */
      if( (item_length-req_path_size-2) != 0 )
      {

         cmd_data_tree = proto_tree_add_subtree( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2,
                                                 ett_cm_cmd_data, NULL, "Command Specific Data" );

         /* Check what service code that received */
         switch (service)
         {
         case SC_CM_FWD_OPEN:
            /* Forward open Request*/
            dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, tvb, offset+2+req_path_size, FALSE, pinfo, FALSE);
            break;
         case SC_CM_CONCURRENT_FWD_OPEN:
            dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, tvb, offset+2+req_path_size, FALSE, pinfo, TRUE);
            break;
         case SC_CM_LARGE_FWD_OPEN:
            /* Large Forward open Request*/
            dissect_cip_cm_fwd_open_req(preq_info, cmd_data_tree, tvb, offset+2+req_path_size, TRUE, pinfo, FALSE);
            break;
         case SC_CM_FWD_CLOSE:
         case SC_CM_CONCURRENT_FWD_CLOSE:
            dissect_cip_cm_fwd_close_req(cmd_data_tree, tvb, offset + 2 + req_path_size, pinfo);
            break;
         case SC_CM_UNCON_SEND:
            dissect_cip_cm_unconnected_send_req(cmd_data_tree, tvb, offset + 2 + req_path_size, pinfo);
            break;
         case SC_CM_GET_CONN_OWNER:
         {
            /* Get Connection Owner Request */

            /* Display the Reserved byte */
            proto_tree_add_item(cmd_data_tree, hf_cip_reserved8, tvb, offset+2+req_path_size, 1, ENC_LITTLE_ENDIAN);

            /* Add path size */
            guint16 conn_path_size = tvb_get_guint8( tvb, offset+2+req_path_size+1 )*2;
            proto_tree_add_item(cmd_data_tree, hf_cip_cm_conn_path_size, tvb, offset+2+req_path_size+1, 1, ENC_LITTLE_ENDIAN);

            /* Add the epath */
            proto_item* pi;
            proto_tree* epath_tree = proto_tree_add_subtree(cmd_data_tree, tvb, offset+2+req_path_size+2, conn_path_size, ett_path, &pi, "Connection Path: ");
            dissect_epath(tvb, pinfo, epath_tree, pi, offset+2+req_path_size+2, conn_path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY, NULL, FALSE);
            break;
         }
         default:
            /* Add data */
            proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+2+req_path_size, item_length-req_path_size-2, ENC_NA);
         }

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

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

} /* End of dissect_cip_cm_data() */

static int
dissect_cip_class_cm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
   proto_item *ti;
   proto_tree *class_tree;

   /* Create display subtree for the protocol */
   ti = proto_tree_add_item(tree, proto_cip_class_cm, tvb, 0, -1, ENC_NA);
   class_tree = proto_item_add_subtree( ti, ett_cip_class_cm );

   dissect_cip_cm_data( class_tree, tvb, 0, tvb_reported_length(tvb), pinfo );

   return tvb_reported_length(tvb);
}

/************************************************
 *
 * Dissector for CIP PCCC Object
 *
 ************************************************/
static void
dissect_cip_pccc_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
{
   proto_item *rrsc_item;
   proto_tree *rrsc_tree, *req_id_tree, *pccc_cmd_tree, *cmd_data_tree;
   int req_path_size;
   unsigned char service;
   int add_status;

   service = tvb_get_guint8( tvb, offset );

   col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP PCCC");

   /* Add Service code & Request/Response tree */
   rrsc_tree = proto_tree_add_subtree( item_tree, tvb, offset, 1, ett_pccc_rrsc, &rrsc_item, "Service: " );

   /* Add Request/Response */
   proto_tree_add_item( rrsc_tree, hf_cip_reqrsp, tvb, offset, 1, ENC_LITTLE_ENDIAN );

   /* watch for service collisions */
   proto_item_append_text( rrsc_item, "%s (%s)",
               val_to_str( ( service & CIP_SC_MASK ),
                  cip_sc_vals_pccc , "Unknown Service (0x%02x)"),
               val_to_str_const( ( service & CIP_SC_RESPONSE_MASK )>>7,
                  cip_sc_rr, "") );

   /* Add Service code */
   proto_tree_add_item(rrsc_tree, hf_cip_pccc_sc, tvb, offset, 1, ENC_LITTLE_ENDIAN );
   add_cip_service_to_info_column (pinfo, service, cip_sc_vals_pccc);

   /* There is a minimum of two bytes different between the request and response request path */
   /* Response message */
   if ( service & CIP_SC_RESPONSE_MASK )
   {
       req_path_size = 2 + tvb_get_guint8( tvb, offset+2 )*2;
   }
   /* Request message */
   else
   {
       req_path_size = tvb_get_guint8( tvb, offset+1 )*2;
   }

   int req_id_offset = offset+req_path_size+2;
   int req_id_size = tvb_get_guint8( tvb, req_id_offset );
   int pccc_cmd_offset = req_id_offset+req_id_size;

   /* Add Requestor ID tree */
   req_id_tree = proto_tree_add_subtree( item_tree, tvb, req_id_offset, req_id_size, ett_pccc_req_id, NULL, "Requestor ID" );
   /* Add Length of Requestor ID code */
   proto_tree_add_item(req_id_tree, hf_cip_pccc_req_id_len, tvb, req_id_offset, 1, ENC_LITTLE_ENDIAN );
   /* Add CIP Vendor ID */
   proto_tree_add_item(req_id_tree, hf_cip_pccc_cip_vend_id, tvb, req_id_offset+1, 2, ENC_LITTLE_ENDIAN );
   /* Add CIP Serial Number */
   proto_tree_add_item(req_id_tree, hf_cip_pccc_cip_serial_num, tvb, req_id_offset+3, 4, ENC_LITTLE_ENDIAN );

   if( service & CIP_SC_RESPONSE_MASK )
   {
        /* Add PCCC Response Data tree */
         pccc_cmd_tree = proto_tree_add_subtree( item_tree, tvb, pccc_cmd_offset, item_length-req_path_size-2-req_id_size, ett_pccc_req_id, NULL, "PCCC Response Data" );

         /* Add Command Code */
         proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_resp_code, tvb, pccc_cmd_offset, 1, ENC_LITTLE_ENDIAN );
         /* Add Status Code */
         proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_sts_code, tvb, pccc_cmd_offset+1, 1, ENC_LITTLE_ENDIAN );
         /* Add Transaction Code */
         proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_tns_code, tvb, pccc_cmd_offset+2, 2, ENC_LITTLE_ENDIAN );

         /* Check the status byte for the EXT_STS signifier - 0xF0 */
         add_status = tvb_get_guint8( tvb, pccc_cmd_offset+1 );
         // TODO: still need to test this
         if ( add_status == PCCC_GS_USE_EXTSTS )
         {
             proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_ext_sts_code, tvb, pccc_cmd_offset+4, 1, ENC_LITTLE_ENDIAN );
         }
         // handle cases where data is returned in the response
         else if (item_length-req_path_size-2-req_id_size-4 != 0)
         {
            /* Add the data tree */
            cmd_data_tree = proto_tree_add_subtree( pccc_cmd_tree, tvb, pccc_cmd_offset+4, item_length-req_path_size-2-req_id_size-4, ett_pccc_cmd_data, NULL, "Function Specific Response Data" );
            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_data, tvb, pccc_cmd_offset+4, item_length-req_path_size-2-req_id_size-4, ENC_NA);
         }

   } /* end of if reply */

  /* Request message */
   else
   {
      /* If there is any command specific data create a sub-tree for it */
      if( (item_length-req_path_size-2) != 0 )
      {
         guint32 cmd_code, fnc_code;

         /* Add PCCC CMD Data tree */
         pccc_cmd_tree = proto_tree_add_subtree( item_tree, tvb, pccc_cmd_offset, item_length-req_path_size-2-req_id_size, ett_pccc_req_id, NULL, "PCCC Command Data" );

         /* Add Command Code */
         proto_tree_add_item_ret_uint(pccc_cmd_tree, hf_cip_pccc_cmd_code, tvb, pccc_cmd_offset, 1, ENC_LITTLE_ENDIAN, &cmd_code);
         /* Add Status Code */
         proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_sts_code, tvb, pccc_cmd_offset+1, 1, ENC_LITTLE_ENDIAN );
         /* Add Transaction Code */
         proto_tree_add_item(pccc_cmd_tree, hf_cip_pccc_tns_code, tvb, pccc_cmd_offset+2, 2, ENC_LITTLE_ENDIAN );
         /* Add Function Code */
         switch(cmd_code)
         {
             case PCCC_CMD_06:
                 proto_tree_add_item_ret_uint(pccc_cmd_tree, hf_cip_pccc_fnc_code_06, tvb, pccc_cmd_offset+4, 1, ENC_LITTLE_ENDIAN, &fnc_code);
                 add_cip_pccc_function_to_info_column(pinfo, fnc_code, cip_pccc_fnc_06_vals);
             break;

             case PCCC_CMD_07:
                 proto_tree_add_item_ret_uint(pccc_cmd_tree, hf_cip_pccc_fnc_code_07, tvb, pccc_cmd_offset+4, 1, ENC_LITTLE_ENDIAN, &fnc_code);
                 add_cip_pccc_function_to_info_column(pinfo, fnc_code, cip_pccc_fnc_07_vals);
             break;

             case PCCC_CMD_0F:
                 proto_tree_add_item_ret_uint(pccc_cmd_tree, hf_cip_pccc_fnc_code_0f, tvb, pccc_cmd_offset+4, 1, ENC_LITTLE_ENDIAN, &fnc_code);
                 add_cip_pccc_function_to_info_column(pinfo, fnc_code, cip_pccc_fnc_0f_vals);
             break;

             default:
                 fnc_code = 0;
             break;
         }

         if (item_length-req_path_size-2-req_id_size-5 != 0 )
         {
                     /* Add the data tree */
            cmd_data_tree = proto_tree_add_subtree( pccc_cmd_tree, tvb, pccc_cmd_offset+5, item_length-req_path_size-req_id_size-7,
                                        ett_pccc_cmd_data, NULL, "Function Specific Data" );

            int running_offset = pccc_cmd_offset+6;
            int num_cmds;
            int sub_fnc_len;
            proto_tree *sub_fnc_tree;

            /* Add in parsing of instructions that contain data beyond the FNC code */
            /* Instructions that end at the FNC codes are already processed */
            switch(cmd_code)
            {
                case PCCC_CMD_0F:
                    switch(fnc_code){
                        /* Change CPU Mode */
                        case PCCC_FNC_0F_80:
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_cpu_mode_80, tvb, pccc_cmd_offset+5, 1, ENC_NA);
                        break;
                        /* Execute Multiple Commands */
                        case PCCC_FNC_0F_88:
                            num_cmds = tvb_get_guint8( tvb, pccc_cmd_offset+5 );
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_execute_multi_count, tvb, pccc_cmd_offset+5, 1, ENC_NA);

                            /* iterate over each of the commands and break them out */
                            for( int i=0; i < num_cmds; i++ ){
                                sub_fnc_len = tvb_get_guint8( tvb, running_offset);
                                sub_fnc_tree = proto_tree_add_subtree_format(cmd_data_tree, tvb, running_offset, sub_fnc_len+1, ett_pccc_req_id, NULL, "Sub Function #%d", i+1);

                                proto_tree_add_item(sub_fnc_tree, hf_cip_pccc_execute_multi_len, tvb, running_offset, 1, ENC_NA);
                                proto_tree_add_item(sub_fnc_tree, hf_cip_pccc_execute_multi_fnc, tvb, running_offset+1, 1, ENC_NA);
                                if( sub_fnc_len > 2 ){
                                    proto_tree_add_item(sub_fnc_tree, hf_cip_pccc_data, tvb, running_offset+2, sub_fnc_len-1, ENC_NA);
                                }
                                running_offset = running_offset+sub_fnc_len+1;
                            }
                        break;
                        /* Protected Typed Logical Read with Three Address Fields */
                        case PCCC_FNC_0F_A2:
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_byte_size, tvb, pccc_cmd_offset+5, 1, ENC_NA);
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_file_num, tvb, pccc_cmd_offset+6, 1, ENC_NA);
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_file_type, tvb, pccc_cmd_offset+7, 1, ENC_NA);
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_element_num, tvb, pccc_cmd_offset+8, 1, ENC_NA);
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_subelement_num, tvb, pccc_cmd_offset+9, 1, ENC_NA);
                        break;
                        /* Protected Typed Logical Write with Three Address Fields */
                        case PCCC_FNC_0F_AA:
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_byte_size, tvb, pccc_cmd_offset+5, 1, ENC_NA);
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_file_num, tvb, pccc_cmd_offset+6, 1, ENC_NA);
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_file_type, tvb, pccc_cmd_offset+7, 1, ENC_NA);
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_element_num, tvb, pccc_cmd_offset+8, 1, ENC_NA);
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_subelement_num, tvb, pccc_cmd_offset+9, 1, ENC_NA);
                            int byte_size;
                            byte_size = tvb_get_guint8( tvb, pccc_cmd_offset+5 );

                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_data, tvb, pccc_cmd_offset+10, byte_size, ENC_NA);
                        break;
                        default: /* just print the command data if no known command code is passed */
                            proto_tree_add_item(cmd_data_tree, hf_cip_pccc_data, tvb, pccc_cmd_offset+5, item_length-pccc_cmd_offset-5, ENC_NA);
                    }
                break;
                default: /* just print the command data if no known command code is passed */
                    proto_tree_add_item(cmd_data_tree, hf_cip_pccc_data, tvb, pccc_cmd_offset+5, 1, ENC_NA);
            }
        }
       } /* End of if-else( request ) */
    }

} /* End of dissect_cip_pccc_data() */

static int
dissect_cip_class_pccc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
   proto_item *ti;
   proto_tree *class_tree;

   /* Create display subtree for the protocol */
   ti = proto_tree_add_item(tree, proto_cip_class_pccc, tvb, 0, -1, ENC_NA);
   class_tree = proto_item_add_subtree( ti, ett_cip_class_pccc );

   dissect_cip_pccc_data( class_tree, tvb, 0, tvb_reported_length(tvb), pinfo );

   return tvb_reported_length(tvb);
}

/************************************************
 *
 * Dissector for CIP Modbus Object
 *
 ************************************************/
static void
dissect_cip_mb_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
{
   proto_item *rrsc_item;
   proto_tree *rrsc_tree, *cmd_data_tree;
   tvbuff_t *next_tvb;
   int req_path_size;
   guint8 gen_status, add_stat_size, service;

   col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP MB");

   /* Add Service code & Request/Response tree */
   service = tvb_get_guint8( tvb, offset );
   rrsc_tree = proto_tree_add_subtree( item_tree, tvb, offset, 1, ett_mb_rrsc, &rrsc_item, "Service: " );

   /* Add Request/Response */
   proto_tree_add_item( rrsc_tree, hf_cip_reqrsp, tvb, offset, 1, ENC_LITTLE_ENDIAN );

   proto_item_append_text( rrsc_item, "%s (%s)",
               val_to_str( ( service & CIP_SC_MASK ),
                  cip_sc_vals_mb , "Unknown Service (0x%02x)"),
               val_to_str_const( ( service & CIP_SC_RESPONSE_MASK )>>7,
                  cip_sc_rr, "") );

   /* Add Service code */
   proto_tree_add_item(rrsc_tree, hf_cip_mb_sc, tvb, offset, 1, ENC_LITTLE_ENDIAN );

   if( service & CIP_SC_RESPONSE_MASK )
   {
      /* Response message */
      gen_status = tvb_get_guint8( tvb, offset+2 );
      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;

      /* If there is any command specific data create a sub-tree for it */
      if( ( item_length-4-add_stat_size ) != 0 )
      {
         cmd_data_tree = proto_tree_add_subtree( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size,
                                                 ett_mb_cmd_data, NULL, "Command Specific Data" );

         if( gen_status == CI_GRC_SUCCESS || gen_status == CI_GRC_SERVICE_ERROR )
         {
            /* Success responses */
            switch (service & CIP_SC_MASK)
            {
            case SC_MB_READ_DISCRETE_INPUTS:
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_discrete_inputs_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, ENC_NA);
               break;

            case SC_MB_READ_COILS:
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_coils_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, ENC_NA);
               break;

            case SC_MB_READ_INPUT_REGISTERS:
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_input_register_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, ENC_NA);
               break;

            case SC_MB_READ_HOLDING_REGISTERS:
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_holding_register_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, ENC_NA);
               break;

            case SC_MB_WRITE_COILS:
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_coils_start_addr, tvb, offset+4+add_stat_size, 2, ENC_LITTLE_ENDIAN);
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_coils_outputs_forced, tvb, offset+4+add_stat_size+2, 2, ENC_LITTLE_ENDIAN);
               break;

            case SC_MB_WRITE_HOLDING_REGISTERS:
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_registers_start_addr, tvb, offset+4+add_stat_size, 2, ENC_LITTLE_ENDIAN);
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_registers_outputs_forced, tvb, offset+4+add_stat_size+2, 2, ENC_LITTLE_ENDIAN);
               break;

            case SC_MB_PASSTHROUGH:
               /* Passthrough response (Success) */
               if( tvb_reported_length_remaining(tvb, offset) > 0 )
               {
                  modbus_data_t modbus_data;
                  modbus_data.packet_type = RESPONSE_PACKET;
                  modbus_data.mbtcp_transid = 0;
                  modbus_data.unit_id = 0;

                  /* dissect the Modbus PDU */
                  next_tvb = tvb_new_subset_length( tvb, offset+4+add_stat_size, item_length-4-add_stat_size);

                  /* Call Modbus Dissector */
                  call_dissector_with_data(modbus_handle, next_tvb, pinfo, cmd_data_tree, &modbus_data);

               }
               break;

            default:
               proto_tree_add_item(cmd_data_tree, hf_cip_mb_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, ENC_NA);
            }
         }
         else
         {
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, ENC_NA);
         }

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

   } /* End of if reply */
   else
   {
      /* Request message */
      req_path_size = tvb_get_guint8( tvb, offset+1 )*2;

      /* If there is any command specific data creat a sub-tree for it */
      if( (item_length-req_path_size-2) != 0 )
      {
         cmd_data_tree = proto_tree_add_subtree( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2,
                                                ett_mb_cmd_data, NULL, "Command Specific Data" );

         /* Check what service code that received */
         switch (service)
         {
         case SC_MB_READ_DISCRETE_INPUTS:
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_discrete_inputs_start_addr, tvb, offset+2+req_path_size, 2, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_discrete_inputs_num_inputs, tvb, offset+2+req_path_size+2, 2, ENC_LITTLE_ENDIAN);
            break;

         case SC_MB_READ_COILS:
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_coils_start_addr, tvb, offset+2+req_path_size, 2, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_coils_num_coils, tvb, offset+2+req_path_size+2, 2, ENC_LITTLE_ENDIAN);
            break;

         case SC_MB_READ_INPUT_REGISTERS:
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_input_register_start_addr, tvb, offset+2+req_path_size, 2, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_input_register_num_registers, tvb, offset+2+req_path_size+2, 2, ENC_LITTLE_ENDIAN);
            break;

         case SC_MB_READ_HOLDING_REGISTERS:
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_holding_register_start_addr, tvb, offset+2+req_path_size, 2, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_read_holding_register_num_registers, tvb, offset+2+req_path_size+2, 2, ENC_LITTLE_ENDIAN);
            break;

         case SC_MB_WRITE_COILS:
            {
            guint16 NumCoils;

            proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_coils_start_addr, tvb, offset+2+req_path_size, 2, ENC_LITTLE_ENDIAN);
            NumCoils = tvb_get_letohs( tvb, offset+2+req_path_size+2 );
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_coils_num_coils, tvb, offset+2+req_path_size+2, 2, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_coils_data, tvb, offset+2+req_path_size+4, (NumCoils+7)/8, ENC_NA);
            }
            break;

         case SC_MB_WRITE_HOLDING_REGISTERS:
            {
            guint16 NumRegisters;

            proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_registers_start_addr, tvb, offset+2+req_path_size, 2, ENC_LITTLE_ENDIAN);
            NumRegisters = tvb_get_letohs( tvb, offset+2+req_path_size+2 );
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_registers_num_registers, tvb, offset+2+req_path_size+2, 2, ENC_LITTLE_ENDIAN);
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_write_registers_data, tvb, offset+2+req_path_size+4, NumRegisters*2, ENC_NA);
            }
            break;

         case SC_MB_PASSTHROUGH:
            /* Passthrough Request */
            if( tvb_reported_length_remaining(tvb, offset) > 0 )
            {
               modbus_data_t modbus_data;
               modbus_data.packet_type = QUERY_PACKET;
               modbus_data.mbtcp_transid = 0;
               modbus_data.unit_id = 0;

               /* dissect the Modbus PDU */
               next_tvb = tvb_new_subset_length( tvb, offset+2+req_path_size, item_length-req_path_size-2);

               /* Call Modbus Dissector */
               call_dissector_with_data(modbus_handle, next_tvb, pinfo, cmd_data_tree, &modbus_data);
            }
            break;

         default:
            proto_tree_add_item(cmd_data_tree, hf_cip_mb_data, tvb, offset+2+req_path_size, item_length-req_path_size-2, ENC_NA);
         }

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

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

   add_cip_service_to_info_column(pinfo, service, cip_sc_vals_mb);
} /* End of dissect_cip_mb_data() */

static int
dissect_cip_class_mb(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
   proto_item *ti;
   proto_tree *class_tree;

   /* Create display subtree for the protocol */
   ti = proto_tree_add_item(tree, proto_cip_class_mb, tvb, 0, -1, ENC_NA);
   class_tree = proto_item_add_subtree( ti, ett_cip_class_mb );

   dissect_cip_mb_data( class_tree, tvb, 0, tvb_reported_length(tvb), pinfo );

   return tvb_reported_length(tvb);
}

/************************************************
 *
 * Dissector for CIP Connection Configuration Object
 *
 ************************************************/
static int
dissect_cip_cco_all_attribute_common( proto_tree *cmd_tree, proto_item *ti,
    tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo)
{
   proto_item *pi;
   proto_tree *tdi_tree, *iomap_tree, *epath_tree;
   proto_tree *ncp_tree;
   int conn_path_size, variable_data_size = 0, config_data_size;
   int iomap_size, ot_rtf, to_rtf;
   int temp_data;

   /* Connection flags */
   temp_data = tvb_get_letohs( tvb, offset);
   ot_rtf = (temp_data >> 1) & 7;
   to_rtf = (temp_data >> 4) & 7;

   static int *const connection_flags[] = {
      &hf_cip_cco_con_type,
      &hf_cip_cco_ot_rtf,
      &hf_cip_cco_to_rtf,
      NULL
   };
   proto_tree_add_bitmask(cmd_tree, tvb, offset, hf_cip_cco_con_flags, ett_cco_con_flag, connection_flags, ENC_LITTLE_ENDIAN);

   /* Target device id */
   tdi_tree = proto_tree_add_subtree( cmd_tree, tvb, offset+2, 10, ett_cco_tdi, NULL, "Target Device ID");

   dissect_deviceid(tvb, offset+2, tdi_tree,
      hf_cip_cco_tdi_vendor, hf_cip_cco_tdi_devtype, hf_cip_cco_tdi_prodcode,
      hf_cip_cco_tdi_compatibility, hf_cip_cco_tdi_comp_bit, hf_cip_cco_tdi_majorrev, hf_cip_cco_tdi_minorrev, FALSE, ENC_LITTLE_ENDIAN);

   /* CS Data Index Number */
   proto_tree_add_item(cmd_tree, hf_cip_cco_cs_data_index, tvb, offset+10, 4, ENC_LITTLE_ENDIAN );

   /* Net Connection Parameters */
   ncp_tree = proto_tree_add_subtree( cmd_tree, tvb, offset+14, 14, ett_cco_ncp, NULL, "Net Connection Parameters");

   /* Timeout multiplier */
   proto_tree_add_item(ncp_tree, hf_cip_cco_timeout_multiplier, tvb, offset+14, 1, ENC_LITTLE_ENDIAN );

   dissect_transport_type_trigger(tvb, offset+15, ncp_tree, hf_cip_cco_transport_type_trigger,
                                  hf_cip_cco_fwo_dir, hf_cip_cco_fwo_trigger, hf_cip_cco_fwo_class, ett_cco_ttt);

   proto_tree_add_item(ncp_tree, hf_cip_cco_ot_rpi, tvb, offset + 16, 4, ENC_LITTLE_ENDIAN);

   /* Display O->T network connection parameters */
   cip_connID_info_t ignore;
   dissect_net_param16(tvb, offset+20, ncp_tree,
              hf_cip_cco_ot_net_param16, hf_cip_cco_fwo_own, hf_cip_cco_fwo_typ,
              hf_cip_cco_fwo_prio, hf_cip_cco_fwo_fixed_var, hf_cip_cco_fwo_con_size, ett_cco_ncp, &ignore);

   proto_tree_add_item(ncp_tree, hf_cip_cco_to_rpi, tvb, offset + 22, 4, ENC_LITTLE_ENDIAN);

   /* Display T->O network connection parameters */
   dissect_net_param16(tvb, offset+26, ncp_tree,
              hf_cip_cco_to_net_param16, hf_cip_cco_fwo_own, hf_cip_cco_fwo_typ,
              hf_cip_cco_fwo_prio, hf_cip_cco_fwo_fixed_var, hf_cip_cco_fwo_con_size, ett_cco_ncp, &ignore);

   /* Connection Path */
   conn_path_size = tvb_get_guint8( tvb, offset+28 )*2;
   proto_tree_add_item(cmd_tree, hf_cip_cco_conn_path_size, tvb, offset+28, 1, ENC_LITTLE_ENDIAN);

   /* Display the Reserved byte */
   proto_tree_add_item(cmd_tree, hf_cip_reserved8, tvb, offset+29, 1, ENC_LITTLE_ENDIAN );

   /* Add the epath */
   epath_tree = proto_tree_add_subtree(cmd_tree, tvb, offset+30, conn_path_size, ett_path, &pi, "Connection Path: ");
   dissect_epath(tvb, pinfo, epath_tree, pi, offset+30, conn_path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY, NULL, FALSE);

   variable_data_size += (conn_path_size+30);

   /* Config #1 Data */
   config_data_size = tvb_get_letohs( tvb, offset+variable_data_size);
   proto_tree_add_item(cmd_tree, hf_cip_cco_proxy_config_size, tvb, offset+variable_data_size, 2, ENC_LITTLE_ENDIAN );
   if (config_data_size > 0)
      proto_tree_add_item(cmd_tree, hf_cip_cco_proxy_config_data, tvb, offset+variable_data_size+2, config_data_size, ENC_NA);

   variable_data_size += (config_data_size+2);

   /* Config #2 Data */
   config_data_size = tvb_get_letohs( tvb, offset+variable_data_size);
   proto_tree_add_item(cmd_tree, hf_cip_cco_target_config_size, tvb, offset+variable_data_size, 2, ENC_LITTLE_ENDIAN );
   if (config_data_size > 0)
      proto_tree_add_item(cmd_tree, hf_cip_cco_target_config_data, tvb, offset+variable_data_size+2, config_data_size, ENC_NA);

   variable_data_size += (config_data_size+2);

   /* Connection Name */
   variable_data_size += dissect_cip_string_type(pinfo, cmd_tree, ti, tvb, offset + variable_data_size, hf_cip_cco_connection_name, CIP_STRING2_TYPE);

   /* I/O Mapping */
   iomap_size = tvb_get_letohs( tvb, offset+variable_data_size+2);

   iomap_tree = proto_tree_add_subtree( cmd_tree, tvb, offset+variable_data_size, iomap_size+4, ett_cco_iomap, NULL, "I/O Mapping");

   proto_tree_add_item(iomap_tree, hf_cip_cco_iomap_format_number, tvb, offset+variable_data_size, 2, ENC_LITTLE_ENDIAN );
   proto_tree_add_item(iomap_tree, hf_cip_cco_iomap_size, tvb, offset+variable_data_size+2, 2, ENC_LITTLE_ENDIAN);

   /* Attribute data */
   if (iomap_size > 0)
      proto_tree_add_item(iomap_tree, hf_cip_cco_iomap_attribute, tvb, offset+variable_data_size+4, iomap_size, ENC_NA);

   variable_data_size += (iomap_size+4);

   /* Proxy device id */
   tdi_tree = proto_tree_add_subtree( cmd_tree, tvb, offset+variable_data_size, 8, ett_cco_pdi, NULL, "Proxy Device ID");

   dissect_deviceid(tvb, offset+variable_data_size, tdi_tree,
      hf_cip_cco_pdi_vendor, hf_cip_cco_pdi_devtype, hf_cip_cco_pdi_prodcode,
      hf_cip_cco_pdi_compatibility, hf_cip_cco_pdi_comp_bit, hf_cip_cco_pdi_majorrev, hf_cip_cco_pdi_minorrev, FALSE, ENC_LITTLE_ENDIAN);

   /* Add in proxy device id size */
   variable_data_size += 8;

   if ((offset+variable_data_size < item_length) &&
       ((ot_rtf == 5) || (to_rtf == 5)))
   {
      /* Safety parameters */
      proto_tree_add_item(cmd_tree, hf_cip_cco_safety, tvb, offset+variable_data_size, 55, ENC_NA);
      variable_data_size += 55;
   }

   if (offset+variable_data_size < item_length)
   {
      proto_tree_add_item(cmd_tree, hf_cip_cco_connection_disable, tvb, offset+variable_data_size, 1, ENC_LITTLE_ENDIAN );
      variable_data_size++;
   }

   if (offset+variable_data_size < item_length)
   {
      proto_tree_add_item(cmd_tree, hf_cip_cco_net_conn_param_attr, tvb, offset+variable_data_size, 1, ENC_LITTLE_ENDIAN );
      variable_data_size++;
   }

   if (offset+variable_data_size < item_length)
   {
      /* Large Net Connection Parameter */
      ncp_tree = proto_tree_add_subtree( cmd_tree, tvb, offset+variable_data_size, 18, ett_cco_ncp, NULL, "Large Net Connection Parameters");

      proto_tree_add_item(ncp_tree, hf_cip_cco_timeout_multiplier, tvb, offset+variable_data_size, 1, ENC_LITTLE_ENDIAN );
      dissect_transport_type_trigger(tvb, offset+variable_data_size+1, ncp_tree, hf_cip_cco_transport_type_trigger,
                                  hf_cip_cco_fwo_dir, hf_cip_cco_fwo_trigger, hf_cip_cco_fwo_class, ett_cco_ttt);

      proto_tree_add_item(ncp_tree, hf_cip_cco_ot_rpi, tvb, offset + variable_data_size + 2, 4, ENC_LITTLE_ENDIAN);

      /* Display O->T network connection parameters */
      dissect_net_param32(tvb, offset+variable_data_size+6, ncp_tree,
                 hf_cip_cco_ot_net_param32, hf_cip_cco_lfwo_own, hf_cip_cco_lfwo_typ,
                 hf_cip_cco_lfwo_prio, hf_cip_cco_lfwo_fixed_var, hf_cip_cco_lfwo_con_size, ett_cco_ncp, &ignore);

      proto_tree_add_item(ncp_tree, hf_cip_cco_to_rpi, tvb, offset + variable_data_size + 10, 4, ENC_LITTLE_ENDIAN);

      /* Display T->O network connection parameters */
      dissect_net_param32(tvb, offset+variable_data_size+14, ncp_tree,
                 hf_cip_cco_to_net_param32, hf_cip_cco_lfwo_own, hf_cip_cco_lfwo_typ,
                 hf_cip_cco_lfwo_prio, hf_cip_cco_lfwo_fixed_var, hf_cip_cco_lfwo_con_size, ett_cco_ncp, &ignore);

      variable_data_size += 18;
   }

   return variable_data_size;
}

static void
dissect_cip_cco_data( proto_tree *item_tree, proto_item *ti, tvbuff_t *tvb, int offset, int item_length, packet_info *pinfo )
{
   proto_item *rrsc_item;
   proto_tree *rrsc_tree, *cmd_data_tree, *con_st_tree;
   int req_path_size;
   guint8 service, gen_status, add_stat_size;
   cip_simple_request_info_t req_data;

   col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP CCO");

   /* Add Service code & Request/Response tree */
   service = tvb_get_guint8( tvb, offset );
   rrsc_tree = proto_tree_add_subtree( item_tree, tvb, offset, 1, ett_cco_rrsc, &rrsc_item, "Service: " );

   /* Add Request/Response */
   proto_tree_add_item( rrsc_tree, hf_cip_reqrsp, tvb, offset, 1, ENC_LITTLE_ENDIAN );

   proto_item_append_text( rrsc_item, "%s (%s)",
               val_to_str( ( service & CIP_SC_MASK ),
                  cip_sc_vals_cco , "Unknown Service (0x%02x)"),
               val_to_str_const( ( service & CIP_SC_RESPONSE_MASK )>>7,
                  cip_sc_rr, "") );

   /* Add Service code */
   proto_tree_add_item(rrsc_tree, hf_cip_cco_sc, tvb, offset, 1, ENC_LITTLE_ENDIAN );

   load_cip_request_data(pinfo, &req_data);

   if(service & CIP_SC_RESPONSE_MASK )
   {
      /* Response message */

      /* Add additional status size */
      gen_status = tvb_get_guint8( tvb, offset+2 );
      add_stat_size = tvb_get_guint8( tvb, offset+3 ) * 2;

      /* If there is any command specific data create a sub-tree for it */
      if( ( item_length-4-add_stat_size ) != 0 )
      {
         cmd_data_tree = proto_tree_add_subtree( item_tree, tvb, offset+4+add_stat_size, item_length-4-add_stat_size,
                                                 ett_cco_cmd_data, NULL, "Command Specific Data" );

         if( gen_status == CI_GRC_SUCCESS )
         {
            /* Success responses */
            if (((service & CIP_SC_MASK) == SC_GET_ATT_ALL) &&
                (req_data.iInstance != SEGMENT_VALUE_NOT_SET))
            {
               if (req_data.iInstance == 0)
               {
                  /* Get Attribute All (class) request */
                  dissect_cip_get_attribute_all_rsp(tvb, pinfo, cmd_data_tree, offset + 4 + add_stat_size, &req_data);
               }
               else
               {
                  /* Get Attribute All (instance) request */

                  /* Connection status */
                  con_st_tree = proto_tree_add_subtree( cmd_data_tree, tvb, offset+4+add_stat_size, 4, ett_cco_con_status, NULL, "Connection Status");

                  proto_tree_add_item(con_st_tree, hf_cip_genstat, tvb, offset+4+add_stat_size, 1, ENC_LITTLE_ENDIAN );
                  proto_tree_add_item(con_st_tree, hf_cip_pad8, tvb, offset+4+add_stat_size+1, 1, ENC_LITTLE_ENDIAN);

                  /* Extended Status */
                  proto_tree_add_item(con_st_tree, hf_cip_cco_ext_status, tvb, offset+4+add_stat_size+2, 2, ENC_LITTLE_ENDIAN);

                  dissect_cip_cco_all_attribute_common(cmd_data_tree, ti, tvb, offset+4+add_stat_size+4, item_length, pinfo);
               }
            }
            else
            {
               /* Add data */
               proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, ENC_NA);
            }
         }
         else
         {
            /* Error responses */

            /* Add data */
            proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+4+add_stat_size, item_length-4-add_stat_size, ENC_NA);
         } /* end of if-else( CI_CRC_SUCCESS ) */

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

   } /* End of if reply */
   else
   {
      /* Request message */
      req_path_size = tvb_get_guint8( tvb, offset+1 )*2;

      /* If there is any command specific data create a sub-tree for it */
      if( (item_length-req_path_size-2) != 0 )
      {

         cmd_data_tree = proto_tree_add_subtree( item_tree, tvb, offset+2+req_path_size, item_length-req_path_size-2,
                                                 ett_cco_cmd_data, NULL, "Command Specific Data" );

         /* Check what service code that received */

         switch (service)
         {
         case SC_CCO_AUDIT_CHANGE:
            proto_tree_add_item(cmd_data_tree, hf_cip_cco_change_type, tvb, offset+2+req_path_size, 2, ENC_LITTLE_ENDIAN );
            break;
         case SC_CCO_CHANGE_COMPLETE:
            proto_tree_add_item(cmd_data_tree, hf_cip_cco_change_type, tvb, offset+2+req_path_size, 2, ENC_LITTLE_ENDIAN );
            break;
         case SC_SET_ATT_ALL:
            if ((req_data.iInstance == 0) ||
                (req_data.iInstance == SEGMENT_VALUE_NOT_SET))
            {
               /* Just add raw data */
               proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+2+req_path_size, item_length-req_path_size-2, ENC_NA);
               break;
            }

            /* Set Attribute All (instance) request */
            dissect_cip_cco_all_attribute_common(cmd_data_tree, ti, tvb, offset+2+req_path_size, item_length, pinfo);
            break;
         default:

            /* Add data */
            proto_tree_add_item(cmd_data_tree, hf_cip_data, tvb, offset+2+req_path_size, item_length-req_path_size-2, ENC_NA);
         } /* End of check service code */

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

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

   add_cip_service_to_info_column(pinfo, service, cip_sc_vals_cco);
} /* End of dissect_cip_cco_data() */

static int
dissect_cip_class_cco(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
   proto_item *ti;
   proto_tree *class_tree;

   /* Create display subtree for the protocol */
   ti = proto_tree_add_item(tree, proto_cip_class_cco, tvb, 0, -1, ENC_NA);
   class_tree = proto_item_add_subtree( ti, ett_cip_class_cco );

   dissect_cip_cco_data( class_tree, ti, tvb, 0, tvb_reported_length(tvb), pinfo );

   return tvb_reported_length(tvb);
}

static gboolean
dissect_class_cco_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
   unsigned char service, service_code, ioilen, segment;
   cip_req_info_t* preq_info;
   guint32 classid = 0;
   int offset = 0;

   service = tvb_get_guint8( tvb, offset );
   service_code = service & CIP_SC_MASK;

   /* Handle GetAttributeAll and SetAttributeAll in CCO class */
   if ((service_code == SC_GET_ATT_ALL) ||
       (service_code == SC_SET_ATT_ALL))
   {
      if (service & CIP_SC_RESPONSE_MASK)
      {
         /* Service response */
         preq_info = (cip_req_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
         if ((preq_info != NULL) &&
             (preq_info->dissector == dissector_get_uint_handle( subdissector_class_table, CI_CLS_CCO)))
         {
            call_dissector(preq_info->dissector, tvb, pinfo, tree);
            return TRUE;
         }
      }
      else
      {
         /* Service request */
         ioilen = tvb_get_guint8( tvb, offset + 1 );
         if (ioilen > 1)
         {
            segment = tvb_get_guint8( tvb, offset + 2 );
            if (((segment & CI_SEGMENT_TYPE_MASK) == CI_LOGICAL_SEGMENT) &&
                ((segment & CI_LOGICAL_SEG_TYPE_MASK) == CI_LOGICAL_SEG_CLASS_ID))
            {
               /* Logical Class ID, do a format check */
               switch ( segment & CI_LOGICAL_SEG_FORMAT_MASK )
               {
               case CI_LOGICAL_SEG_8_BIT:
                  classid = tvb_get_guint8( tvb, offset + 3 );
                  break;
               case CI_LOGICAL_SEG_16_BIT:
                  if ( ioilen >= 2 )
                     classid = tvb_get_letohs( tvb, offset + 4 );
                  break;
               case CI_LOGICAL_SEG_32_BIT:
                  if ( ioilen >= 3 )
                     classid = tvb_get_letohl( tvb, offset + 4 );
                  break;
               }
            }
         }

         if (classid == CI_CLS_CCO)
         {
            call_dissector(cip_class_cco_handle, tvb, pinfo, tree );
            return TRUE;
         }

      }
   }

   return FALSE;
}

/************************************************
 *
 * Dissector for CIP Request/Response
 * - matches requests/responses
 * - calls class specific dissector
 *
 ************************************************/

// NOLINTNEXTLINE(misc-no-recursion)
void dissect_cip_data( proto_tree *item_tree, tvbuff_t *tvb, int offset, packet_info *pinfo, cip_req_info_t* preq_info, proto_item* msp_item, gboolean is_msp_item )
{
   proto_item *ti;
   proto_tree *cip_tree, *epath_tree;
   proto_item *pi, *rrsc_item, *status_item;
   proto_tree *rrsc_tree, *status_tree, *add_status_tree;
   int req_path_size;
   unsigned char i, gen_status;
   unsigned char service,ioilen,segment;
   void *p_save_proto_data;
   cip_simple_request_info_t path_info;
   dissector_handle_t dissector;
   gint service_index;
   heur_dtbl_entry_t *hdtbl_entry;

   p_save_proto_data = p_get_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
   p_remove_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
   p_add_proto_data(wmem_file_scope(), pinfo, proto_cip, 0, preq_info);

   /* Create display subtree for the protocol */
   ti = proto_tree_add_item(item_tree, proto_cip, tvb, 0, -1, ENC_NA);
   cip_tree = proto_item_add_subtree( ti, ett_cip );

   service = tvb_get_guint8( tvb, offset );

   /* Add Service code & Request/Response tree */
   rrsc_item = proto_tree_add_uint_format_value(cip_tree, hf_cip_service,
                               tvb, offset, 1, service, "%s (%s)",
                               val_to_str( ( service & CIP_SC_MASK ), cip_sc_vals , "Unknown Service (0x%02x)"),
                               val_to_str_const( ( service & CIP_SC_RESPONSE_MASK )>>7, cip_sc_rr, ""));

   rrsc_tree = proto_item_add_subtree( rrsc_item, ett_rrsc );

   proto_tree_add_item( rrsc_tree, hf_cip_reqrsp, tvb, offset, 1, ENC_LITTLE_ENDIAN);
   proto_tree_add_item(rrsc_tree, hf_cip_service_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);

   increment_dissection_depth(pinfo);
   if( service & CIP_SC_RESPONSE_MASK )
   {
      /* Response message */
      status_tree = proto_tree_add_subtree( cip_tree, tvb, offset+2, 1, ett_status_item, &status_item, "Status: " );

      /* Add general status */
      gen_status = tvb_get_guint8( tvb, offset+2 );
      proto_tree_add_item(status_tree, hf_cip_genstat, tvb, offset+2, 1, ENC_LITTLE_ENDIAN );
      proto_item_append_text( status_item, "%s: ", val_to_str_ext( gen_status,
                     &cip_gs_vals_ext , "Unknown Response (%x)")   );

      if (is_msp_item == FALSE)
      {
          /* Add reply status to info column */
          col_append_fstr(pinfo->cinfo, COL_INFO, "%s: ",
              val_to_str_ext(gen_status, &cip_gs_vals_ext, "Unknown Response (%x)"));
      }
      else
      {
          proto_item_append_text(msp_item, "%s: ",
              val_to_str_ext(gen_status, &cip_gs_vals_ext, "Unknown Response (%x)"));
      }

      /* Add additional status size */
      guint8 add_stat_size = tvb_get_guint8( tvb, offset+3 );
      proto_tree_add_item(status_tree, hf_cip_addstat_size, tvb, offset+3, 1, ENC_LITTLE_ENDIAN);

      if( add_stat_size )
      {
         /* Add additional status */
         add_status_tree = proto_tree_add_subtree( status_tree, tvb, offset+4, add_stat_size*2, ett_add_status_item, NULL, "Additional Status" );

         for( i=0; i < add_stat_size; i ++ )
            proto_tree_add_item(add_status_tree, hf_cip_add_stat, tvb, offset+4+(i*2), 2, ENC_LITTLE_ENDIAN );
      }

      proto_item_set_len( status_item, 2 + add_stat_size*2);

      /* The previous packet service must be Unconnected Send, or match the current
         service to be a valid match. If they don't, ignore the previous data.*/
      if(  preq_info
        && !(  preq_info->bService == ( service & CIP_SC_MASK )
            || ( preq_info->bService == SC_CM_UNCON_SEND && preq_info->dissector == cip_class_cm_handle )
            )
        )
         preq_info = NULL;

      display_previous_request_path(preq_info, cip_tree, tvb, pinfo, msp_item, is_msp_item);

      /* Check to see if service is 'generic' */
      try_val_to_str_idx((service & CIP_SC_MASK), cip_sc_vals, &service_index);

      cip_service_info_t* service_entry = cip_get_service(pinfo, service);

      /* If the request set a dissector, then check that first. This ensures
         that Unconnected Send responses are properly parsed based on the
         embedded request. */
      if (preq_info && preq_info->dissector)
      {
         call_dissector(preq_info->dissector, tvb, pinfo, item_tree);
      }
      else if (service_index >= 0 && !service_entry)
      {
         /* See if object dissector wants to override generic service handling */
         if(!dissector_try_heuristic(heur_subdissector_service, tvb, pinfo, item_tree, &hdtbl_entry, NULL))
         {
           dissect_cip_generic_service_rsp(tvb, pinfo, cip_tree);
         }
      }
      else if (service_entry)
      {
         dissect_cip_object_specific_service(tvb, pinfo, cip_tree, msp_item, service_entry);
      }
      else
      {
         call_dissector( cip_class_generic_handle, tvb, pinfo, item_tree );
      }
   } /* End of if reply */
   else
   {
      /* Request message */

      /* Add path size to tree */
      req_path_size = tvb_get_guint8( tvb, offset+1);
      proto_tree_add_item(cip_tree, hf_cip_request_path_size, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);

      /* Add the epath */
      epath_tree = proto_tree_add_subtree(cip_tree, tvb, offset+2, req_path_size*2, ett_path, &pi, "Request Path: ");
      if (preq_info)
      {
         preq_info->ciaData = wmem_new(wmem_file_scope(), cip_simple_request_info_t);
         dissect_epath(tvb, pinfo, epath_tree, pi, offset+2, req_path_size*2, FALSE, FALSE, preq_info->ciaData, NULL, DISPLAY_REQUEST_PATH, msp_item, is_msp_item);
         memcpy(&path_info, preq_info->ciaData, sizeof(cip_simple_request_info_t));
      }
      else
      {
         dissect_epath(tvb, pinfo, epath_tree, pi, offset+2, req_path_size*2, FALSE, FALSE, &path_info, NULL, DISPLAY_REQUEST_PATH, msp_item, is_msp_item);
      }

      ioilen = tvb_get_guint8( tvb, offset + 1 );

      if ( preq_info )
         preq_info->dissector = NULL;
      dissector = NULL;

      /* The class ID should already be extracted if it's available */
      if (path_info.iClass != 0xFFFFFFFF)
      {
         dissector = dissector_get_uint_handle( subdissector_class_table, path_info.iClass);
      }
      else
      {
         if ( ioilen >= 1 )
         {
            segment = tvb_get_guint8( tvb, offset + 2 );
            if ((segment & CI_SEGMENT_TYPE_MASK) == CI_DATA_SEGMENT)
            {
               dissector = dissector_get_uint_handle( subdissector_symbol_table, segment );
            }
         }
      }

      if ( preq_info )
      {
         preq_info->dissector = dissector;

         /* copy IOI for access by response packet */
         preq_info->pIOI = wmem_alloc(wmem_file_scope(), ioilen*2);
         preq_info->IOILen = ioilen;
         tvb_memcpy(tvb, preq_info->pIOI, offset+2, ioilen*2);

         preq_info->bService = service;
      }

      /* Check to see if service is 'generic' */
      try_val_to_str_idx(service, cip_sc_vals, &service_index);

      cip_service_info_t* service_entry = cip_get_service(pinfo, service);
      if (service_index >= 0 && !service_entry)
      {
          /* See if object dissector wants to override generic service handling */
          if(!dissector_try_heuristic(heur_subdissector_service, tvb, pinfo, item_tree, &hdtbl_entry, NULL))
          {
             /* No need to set a custom dissector if this is just a generic service. */
             if (preq_info)
             {
                preq_info->dissector = NULL;
             }

             dissect_cip_generic_service_req(tvb, pinfo, cip_tree, &path_info);
          }
      }
      else if ( dissector )
      {
         call_dissector( dissector, tvb, pinfo, item_tree );
      }
      else if (service_entry)
      {
         dissect_cip_object_specific_service(tvb, pinfo, cip_tree, msp_item, service_entry);
      }
      else
      {
         call_dissector( cip_class_generic_handle, tvb, pinfo, item_tree );
      }
   } /* End of if-else( request ) */

   decrement_dissection_depth(pinfo);

   p_remove_proto_data(wmem_file_scope(), pinfo, proto_cip, 0);
   p_add_proto_data(wmem_file_scope(), pinfo, proto_cip, 0, p_save_proto_data);

} /* End of dissect_cip_data() */

void dissect_cip_run_idle(tvbuff_t* tvb, int offset, proto_tree* item_tree)
{
   static int * const run_idle_header[] = {
      &hf_32bitheader_roo,
      &hf_32bitheader_coo,
      &hf_32bitheader_run_idle,
      NULL
   };

   proto_tree_add_bitmask(item_tree, tvb, offset, hf_32bitheader, ett_32bitheader_tree, run_idle_header, ENC_LITTLE_ENDIAN);
}

static int
dissect_cip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
   enip_request_info_t *enip_info;
   cip_req_info_t *preq_info;

   /* Make entries in Protocol column and Info column on summary display */
   col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP");

   col_clear(pinfo->cinfo, COL_INFO);
   col_append_sep_str(pinfo->cinfo, COL_INFO, " | ", "");

   /* Each CIP request received by ENIP gets a unique ID */
   enip_info = (enip_request_info_t*)p_get_proto_data(wmem_file_scope(), pinfo, proto_enip, ENIP_REQUEST_INFO);

   if ( enip_info )
   {
      preq_info = enip_info->cip_info;
      if ( preq_info == NULL )
      {
         preq_info = wmem_new0(wmem_file_scope(), cip_req_info_t);
         enip_info->cip_info = preq_info;
      }
      dissect_cip_data( tree, tvb, 0, pinfo, enip_info->cip_info, NULL, FALSE );
   }
   else
   {
      dissect_cip_data( tree, tvb, 0, pinfo, NULL, NULL, FALSE );
   }

   return tvb_reported_length(tvb);
}

static int
dissect_cip_implicit(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
   proto_item *ti;
   proto_tree *cip_tree;

   guint32 ClassID = GPOINTER_TO_UINT(data);
   int length = tvb_reported_length_remaining(tvb, 0);

   /* Make entries in Protocol column and Info column on summary display */
   col_set_str(pinfo->cinfo, COL_PROTOCOL, "CIP");
   col_clear(pinfo->cinfo, COL_INFO);

   /* Create display subtree for the protocol */
   ti = proto_tree_add_item(tree, proto_cip, tvb, 0, length, ENC_NA);
   cip_tree = proto_item_add_subtree(ti, ett_cip);

   proto_tree_add_item(cip_tree, hf_cip_data, tvb, 0, length, ENC_NA);

   col_append_fstr(pinfo->cinfo, COL_INFO, "Implicit Data - %s",
        val_to_str(ClassID, cip_class_names_vals, "Class (0x%02x)"));

   return tvb_reported_length(tvb);
}

/*
 * Protocol initialization
 */

void
proto_register_cip(void)
{
   /* Setup list of header fields */
   static hf_register_info hf[] = {
      { &hf_attr_class_revision, { "Revision", "cip.class_revision", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_attr_class_max_instance, { "Max Instance", "cip.max_instance", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_attr_class_num_instance, { "Number of Instances", "cip.num_instance", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_attr_class_opt_attr_num, { "Number of Attributes", "cip.num_attr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_attr_class_attr_num, { "Attribute Number", "cip.attr_num", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_attr_class_opt_service_num, { "Number of Services", "cip.num_service", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_attr_class_service_code, { "Service Code", "cip.service_code", FT_UINT16, BASE_HEX, VALS(cip_sc_vals), 0, NULL, HFILL } },
      { &hf_attr_class_num_class_attr, { "Maximum ID Number Class Attributes", "cip.num_class_attr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_attr_class_num_inst_attr, { "Maximum ID Number Instance Attributes", "cip.num_inst_attr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },

      { &hf_cip_service, { "Service", "cip.service", FT_UINT8, BASE_HEX, NULL, 0, "Service Code + Request/Response", HFILL }},
      { &hf_cip_reqrsp, { "Request/Response", "cip.rr", FT_UINT8, BASE_HEX, VALS(cip_sc_rr), CIP_SC_RESPONSE_MASK, "Request or Response message", HFILL }},
      { &hf_cip_service_code, { "Service", "cip.sc", FT_UINT8, BASE_HEX, VALS(cip_sc_vals), CIP_SC_MASK, "Service Code", HFILL }},
      { &hf_cip_epath, { "EPath", "cip.epath", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_genstat, { "General Status", "cip.genstat", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_gs_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_addstat_size, { "Additional Status Size", "cip.addstat_size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_add_stat, { "Additional Status", "cip.addstat", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_request_path_size, { "Request Path Size", "cip.request_path_size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},

      { &hf_cip_path_segment, { "Path Segment", "cip.path_segment", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_path_segment_type, { "Path Segment Type", "cip.path_segment.type", FT_UINT8, BASE_DEC, VALS(cip_path_seg_vals), CI_SEGMENT_TYPE_MASK, NULL, HFILL }},
      { &hf_cip_port_ex_link_addr, { "Extended Link Address", "cip.ex_linkaddress", FT_BOOLEAN, 8, NULL, CI_PORT_SEG_EX_LINK_ADDRESS, NULL, HFILL }},
      { &hf_cip_port, { "Port", "cip.port", FT_UINT8, BASE_DEC, VALS(cip_port_number_vals), CI_PORT_SEG_PORT_ID_MASK, "Port Identifier", HFILL } },
      { &hf_cip_port_extended,{ "Port Extended", "cip.port", FT_UINT16, BASE_HEX, NULL, 0, "Port Identifier Extended", HFILL } },
      { &hf_cip_link_address_byte, { "Link Address", "cip.linkaddress.byte", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_link_address_size, { "Link Address Size", "cip.linkaddress_size", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_link_address_string, { "Link Address", "cip.linkaddress.string", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_logical_seg_type, { "Logical Segment Type", "cip.logical_segment.type", FT_UINT8, BASE_DEC, VALS(cip_logical_segment_type_vals), CI_LOGICAL_SEG_TYPE_MASK, NULL, HFILL }},
      { &hf_cip_logical_seg_format, { "Logical Segment Format", "cip.logical_segment.format", FT_UINT8, BASE_DEC, VALS(cip_logical_segment_format_vals), CI_LOGICAL_SEG_FORMAT_MASK, NULL, HFILL }},
      { &hf_cip_class8, { "Class", "cip.class", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_class_names_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_class16, { "Class", "cip.class", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_class_names_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_class32, { "Class", "cip.class", FT_UINT32, BASE_HEX|BASE_EXT_STRING, &cip_class_names_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_instance8, { "Instance", "cip.instance", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_instance16, { "Instance", "cip.instance", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_instance32, { "Instance", "cip.instance", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_member8, { "Member", "cip.member", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_member16, { "Member", "cip.member", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_member32, { "Member", "cip.member", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_attribute8, { "Attribute", "cip.attribute", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_attribute16, { "Attribute", "cip.attribute", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_attribute32, { "Attribute", "cip.attribute", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_conpoint8, { "Connection Point", "cip.connpoint", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_conpoint16, { "Connection Point", "cip.connpoint", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_conpoint32, { "Connection Point", "cip.connpoint", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_serviceid8,{ "Service ID", "cip.serviceid", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_cip_ekey_format, { "Key Format", "cip.ekey.format", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_ekey_vendor, { "Vendor ID", "cip.ekey.vendor", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_vendor_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_ekey_devtype, { "Device Type", "cip.ekey.devtype", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_devtype_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_ekey_prodcode, { "Product Code", "cip.ekey.product_code", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_ekey_compatibility, { "Compatibility", "cip.ekey.compatibility", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_ekey_comp_bit, { "Compatibility", "cip.ekey.comp_bit", FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80, "EKey: Compatibility bit", HFILL }},
      { &hf_cip_ekey_majorrev, { "Major Revision", "cip.ekey.major_rev", FT_UINT8, BASE_DEC, NULL, 0x7F, "EKey: Major Revision", HFILL }},
      { &hf_cip_ekey_minorrev, { "Minor Revision", "cip.ekey.minor_rev", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_ekey_serial_number, { "Serial Number", "cip.ekey.serial_number", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_ext_logical8,{ "Extended Logical", "cip.extlogical", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_cip_ext_logical16,{ "Extended Logical", "cip.extlogical", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_cip_ext_logical32,{ "Extended Logical", "cip.extlogical", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_cip_ext_logical_type,{ "Extended Logical Type", "cip.extlogical.type", FT_UINT8, BASE_HEX, VALS(cip_ext_logical_segment_format_vals), 0, NULL, HFILL } },
      { &hf_cip_data_seg_type, { "Data Segment Type", "cip.data_segment.type", FT_UINT8, BASE_DEC, VALS(cip_data_segment_type_vals), CI_DATA_SEG_TYPE_MASK, NULL, HFILL }},
      { &hf_cip_data_seg_size_simple, { "Data Size", "cip.data_segment.size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_data_seg_size_extended, { "Data Size", "cip.data_segment.size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0, NULL, HFILL } },
      { &hf_cip_data_seg_item, { "Data", "cip.data_segment.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_symbol, { "ANSI Symbol", "cip.symbol", FT_STRING, BASE_NONE, NULL, 0, "ANSI Extended Symbol Segment", HFILL }},
      { &hf_cip_symbol_size, { "Symbolic Symbol Size", "cip.symbol.size", FT_UINT8, BASE_DEC, NULL, 0x1F, NULL, HFILL } },
      { &hf_cip_symbol_ascii, { "ASCII Symbol", "cip.ascii_symbol", FT_STRING, BASE_NONE, NULL, 0, "ASCII Symbol Segment", HFILL } },
      { &hf_cip_symbol_extended_format,{ "Extended String Format", "cip.symbol.format", FT_UINT8, BASE_DEC, VALS(cip_symbolic_format_vals), CI_SYMBOL_SEG_FORMAT_MASK, NULL, HFILL } },
      { &hf_cip_symbol_numeric_format,{ "Extended String Numeric Format", "cip.symbol.numformat", FT_UINT8, BASE_DEC, VALS(cip_symbolic_numeric_format_vals), 0x1F, NULL, HFILL } },
      { &hf_cip_symbol_double_size, { "Double Byte Chars", "cip.symbol.size", FT_UINT8, BASE_DEC, NULL, 0x1F, NULL, HFILL } },
      { &hf_cip_symbol_triple_size, { "Triple Byte Chars", "cip.symbol.size", FT_UINT8, BASE_DEC, NULL, 0x1F, NULL, HFILL } },
      { &hf_cip_numeric_usint,{ "Numeric Symbol", "cip.numeric_symbol", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_cip_numeric_uint,{ "Numeric Symbol", "cip.numeric_symbol", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_cip_numeric_udint,{ "Numeric Symbol", "cip.numeric_symbol", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_cip_network_seg_type, { "Network Segment Type", "cip.network_segment.type", FT_UINT8, BASE_DEC, VALS(cip_network_segment_type_vals), CI_NETWORK_SEG_TYPE_MASK, NULL, HFILL }},
      { &hf_cip_seg_schedule, { "Multiplier/Phase", "cip.network_segment.schedule", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_fixed_tag, { "Fixed Tag", "cip.network_segment.fixed_tag", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_prod_inhibit_time, { "Production Inhibit Time (ms)", "cip.network_segment.prod_inhibit", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_prod_inhibit_time_us, { "Production Inhibit Time (us)", "cip.network_segment.prod_inhibit", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_cip_seg_network_size, { "Network Segment Length", "cip.network_segment.length", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_seg_network_subtype, { "Extended Segment Subtype", "cip.network_segment.subtype", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_cip_seg_safety_format, { "Safety Format", "cip.safety_segment.format", FT_UINT8, BASE_DEC, VALS(cip_safety_segment_format_type_vals),  0, NULL, HFILL }},
      { &hf_cip_seg_safety_reserved, { "Reserved", "cip.safety_segment.reserved", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_configuration_crc, { "Configuration CRC (SCCRC)", "cip.safety_segment.configuration_crc", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_configuration_timestamp, { "Configuration Timestamp (SCTS)", "cip.safety_segment.configuration_timestamp", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_configuration_date, { "Configuration (Manual) Date", "cip.safety_segment.configuration_date", FT_UINT16, BASE_HEX, VALS(cipsafety_snn_date_vals), 0, NULL, HFILL }},
      { &hf_cip_seg_safety_configuration_time, { "Configuration (Manual) Time", "cip.safety_segment.configuration_time", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_time_correction_epi, { "Time Correction EPI", "cip.safety_segment.time_correction_epi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_seg_safety_time_correction_net_params, { "Time Correction Network Connection Parameters", "cip.safety_segment.time_correction.net_params", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_time_correction_own, { "Redundant Owner", "cip.safety_segment.time_correction.owner", FT_UINT16, BASE_DEC, VALS(cip_con_owner_vals), 0x8000, "Time Correction: Redundant owner bit", HFILL }},
      { &hf_cip_seg_safety_time_correction_typ, { "Connection Type", "cip.safety_segment.time_correction.type", FT_UINT16, BASE_DEC, VALS(cip_con_type_vals), 0x6000, "Time Correction: Connection type", HFILL }},
      { &hf_cip_seg_safety_time_correction_prio, { "Priority", "cip.safety_segment.time_correction.prio", FT_UINT16, BASE_DEC, VALS(cip_con_prio_vals), 0x0C00, "Time Correction: Connection priority", HFILL }},
      { &hf_cip_seg_safety_time_correction_fixed_var, { "Connection Size Type", "cip.safety_segment.time_correction.f_v", FT_UINT16, BASE_DEC, VALS(cip_con_fw_vals), 0x0200, "Time Correction: Fixed or variable connection size", HFILL }},
      { &hf_cip_seg_safety_time_correction_con_size, { "Connection Size", "cip.safety_segment.time_correction.consize", FT_UINT16, BASE_DEC, NULL, 0x01FF, "Time Correction: Connection size", HFILL }},
      { &hf_cip_seg_safety_tunid, { "Target UNID (TUNID)", "cip.safety_segment.tunid", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_tunid_snn_timestamp, { "SNN Timestamp", "cip.safety_segment.tunid.snn.timestamp", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_tunid_snn_date, { "SNN (Manual) Date", "cip.safety_segment.tunid.snn.date", FT_UINT16, BASE_HEX, VALS(cipsafety_snn_date_vals), 0, NULL, HFILL }},
      { &hf_cip_seg_safety_tunid_snn_time, { "SNN (Manual) Time", "cip.safety_segment.tunid.snn.time", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_tunid_nodeid, { "Node ID", "cip.safety_segment.tunid.nodeid", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_ounid, { "Originator UNID (OUNID)", "cip.safety_segment.ounid", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_ounid_snn_timestamp, { "SNN Timestamp", "cip.safety_segment.ounid.snn.timestamp", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_UTC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_ounid_snn_date, { "SNN (Manual) Date", "cip.safety_segment.ounid.snn.date", FT_UINT16, BASE_HEX, VALS(cipsafety_snn_date_vals), 0, NULL, HFILL }},
      { &hf_cip_seg_safety_ounid_snn_time, { "SNN (Manual) Time", "cip.safety_segment.ounid.snn.time", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_ounid_nodeid, { "Node ID", "cip.safety_segment.ounid.nodeid", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_ping_epi_multiplier, { "Ping Interval EPI Multiplier", "cip.safety_segment.ping_epi_multiplier", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_time_coord_msg_min_multiplier, { "Time Coord Msg Min Multiplier", "cip.safety_segment.time_coord_msg_min_multiplier", FT_UINT16, BASE_CUSTOM, CF_FUNC(cip_safety_128us_fmt), 0, NULL, HFILL }},
      { &hf_cip_seg_safety_network_time_expected_multiplier, { "Network Time Expectation Multiplier", "cip.safety_segment.network_time_expected_multiplier", FT_UINT16, BASE_CUSTOM, CF_FUNC(cip_safety_128us_fmt), 0, NULL, HFILL }},
      { &hf_cip_seg_safety_timeout_multiplier, { "Timeout Multiplier", "cip.safety_segment.timeout_multiplier", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_max_consumer_number, { "Max Consumer Number", "cip.safety_segment.max_consumer_number", FT_UINT8, BASE_DEC|BASE_RANGE_STRING, RVALS(safety_max_consumer_numbers), 0, NULL, HFILL }},
      { &hf_cip_seg_safety_conn_param_crc, { "Connection Parameters CRC (CPCRC)", "cip.safety_segment.conn_param_crc", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_time_correction_conn_id, { "Time Correction Connection ID", "cip.safety_segment.time_correction_conn_id", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_max_fault_number, { "Max Fault Number", "cip.safety_segment.max_fault_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_init_timestamp, { "Initial Timestamp", "cip.safety_segment.init_timestamp", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_init_rollover, { "Initial Rollover Value", "cip.safety_segment.init_rollover", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_seg_safety_data, { "Safety Data", "cip.safety_segment.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_class_max_inst32, { "Max Instance", "cip.class.max_inst", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_class_num_inst32, { "Number of Instances", "cip.class.num_inst", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_reserved8, { "Reserved", "cip.reserved", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_reserved24, { "Reserved", "cip.reserved", FT_UINT24, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pad8, { "Pad Byte", "cip.pad", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},

      { &hf_cip_sc_get_attr_list_attr_count, { "Attribute Count", "cip.getlist.attr_count", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_sc_get_attr_list_attr_status, { "Attribute Status", "cip.getlist.attr_status", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_gs_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_sc_set_attr_list_attr_count, { "Attribute Count", "cip.setlist.attr_count", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_sc_set_attr_list_attr_status, { "Attribute Status", "cip.setlist.attr_status", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_gs_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_sc_reset_param, { "Reset type", "cip.reset.type", FT_UINT8, BASE_DEC, VALS(cip_reset_type_vals), 0, NULL, HFILL }},
      { &hf_cip_sc_create_instance, { "Instance", "cip.create.instance", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_sc_mult_serv_pack_num_services, { "Number of Services", "cip.msp.num_services", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_sc_mult_serv_pack_offset, { "Offset", "cip.msp.offset", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_find_next_object_max_instance, { "Maximum ID", "cip.find_next_object.max_instance", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_find_next_object_num_instances, { "Number of Instances", "cip.find_next_object.num_instances", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_find_next_object_instance_item, { "Instance", "cip.find_next_object.instance", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_sc_group_sync_is_sync, { "IsSynchronized", "cip.group_sync.is_sync", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_data, { "Data", "cip.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},

      { &hf_id_vendor_id, { "Vendor ID", "cip.id.vendor_id", FT_UINT16, BASE_HEX | BASE_EXT_STRING, &cip_vendor_vals_ext, 0, NULL, HFILL } },
      { &hf_id_device_type, { "Device Type", "cip.id.device_type", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_devtype_vals_ext, 0, NULL, HFILL }},
      { &hf_id_product_code, { "Product Code", "cip.id.product_code", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_id_major_rev, { "Major Revision", "cip.id.major_rev", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_id_minor_rev, { "Minor Revision", "cip.id.minor_rev", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_id_status, { "Status", "cip.id.status", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_id_serial_number, { "Serial Number", "cip.id.serial_number", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_id_product_name, { "Product Name", "cip.id.product_name", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_id_state, { "State", "cip.id.state", FT_UINT8, BASE_HEX, VALS(cip_id_state_vals), 0, NULL, HFILL } },
      { &hf_id_config_value, { "Configuration Consistency Value", "cip.id.config_value", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_id_heartbeat, { "Heartbeat Interval", "cip.id.heartbeat", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_id_status_owned, { "Owned", "cip.id.owned", FT_UINT16, BASE_DEC, NULL, 0x0001, NULL, HFILL } },
      { &hf_id_status_conf, { "Configured", "cip.id.conf", FT_UINT16, BASE_DEC, NULL, 0x0004, NULL, HFILL } },
      { &hf_id_status_extended1, { "Extended Device Status", "cip.id.ext", FT_UINT16, BASE_HEX, NULL, 0x00F0, NULL, HFILL } },
      { &hf_id_status_minor_fault_rec, { "Minor Recoverable Fault", "cip.id.minor_fault1", FT_UINT16, BASE_DEC, NULL, 0x0100, NULL, HFILL } },
      { &hf_id_status_minor_fault_unrec, { "Minor Unrecoverable Fault", "cip.id.minor_fault2", FT_UINT16, BASE_DEC, NULL, 0x0200, NULL, HFILL } },
      { &hf_id_status_major_fault_rec, { "Major Recoverable Fault", "cip.id.major_fault1", FT_UINT16, BASE_DEC, NULL, 0x0400, NULL, HFILL } },
      { &hf_id_status_major_fault_unrec, { "Major Unrecoverable Fault", "cip.id.major_fault2", FT_UINT16, BASE_DEC, NULL, 0x0800, NULL, HFILL } },
      { &hf_id_status_extended2, { "Extended Device Status 2", "cip.id.ext2", FT_UINT16, BASE_HEX, NULL, 0xF000, NULL, HFILL } },

      { &hf_msg_rout_num_classes, { "Number of Classes", "cip.mr.num_classes", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_msg_rout_classes, { "Class", "cip.mr.class", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_class_names_vals_ext, 0, NULL, HFILL }},
      { &hf_msg_rout_num_available, { "Number Available", "cip.mr.num_available", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_msg_rout_num_active, { "Number Active", "cip.mr.num_active", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_msg_rout_active_connections, { "Active Connection", "cip.mr.active_connections", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},

      { &hf_conn_mgr_open_requests, { "Open Requests", "cip.cm.open_requests", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_open_format_rejects, { "Open Format Rejects", "cip.cm.open_format_rejects", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_open_resource_rejects, { "Open Resource Rejects", "cip.cm.open_resource_rejects", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_other_open_rejects, { "Other Open Rejects", "cip.cm.other_open_rejects", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_close_requests, { "Close Requests", "cip.cm.close_requests", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_close_format_requests, { "Close Format Requests", "cip.cm.close_format_requests", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_close_other_requests, { "Close Other Requests", "cip.cm.close_other_requests", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_conn_timouts, { "Connection Timeouts", "cip.cm.conn_timouts", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_num_conn_entries, { "Number of Connection Entries (Bits)", "cip.cm.conn_entries", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_num_conn_entries_bytes, { "Number of Connection Entries (Bytes)", "cip.cm.conn_entries_bytes", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_conn_open_bits, { "Connection Open Bits", "cip.cm.conn_open_bits", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_conn_mgr_cpu_utilization, { "CPU Utilization", "cip.cm.cpu_util", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_conn_mgr_max_buff_size, { "Max Buff Size", "cip.cm.max_buff_size", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_conn_mgr_buff_size_remaining, { "Buff Size Remaining", "cip.cm.buff_remain", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL } },

      { &hf_stringi_number_char, { "Number of Characters", "cip.stringi.num", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_stringi_language_char, { "Language Chars", "cip.stringi.language_char", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
      { &hf_stringi_char_string_struct, { "Char String Struct", "cip.stringi.char_string_struct", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_stringi_char_set, { "Char Set", "cip.stringi.char_set", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_stringi_international_string, { "International String", "cip.stringi.int_string", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },

      { &hf_file_filename, { "File Name", "cip.file.file_name", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },

      { &hf_time_sync_ptp_enable, { "PTP Enable", "cip.time_sync.ptp_enable", FT_BOOLEAN, 8, TFS(&tfs_enabled_disabled), 0, NULL, HFILL }},
      { &hf_time_sync_is_synchronized, { "Is Synchronized", "cip.time_sync.is_synchronized", FT_BOOLEAN, 8, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_sys_time_micro, { "System Time (Microseconds)", "cip.time_sync.sys_time_micro", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_sys_time_nano, { "System Time (Nanoseconds)", "cip.time_sync.sys_time_nano", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_offset_from_master, { "Offset from Master", "cip.time_sync.offset_from_master", FT_INT64, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_max_offset_from_master, { "Max Offset from Master", "cip.time_sync.max_offset_from_master", FT_UINT64, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_mean_path_delay_to_master, { "Mean Path Delay To Master", "cip.time_sync.mean_path_delay_to_master", FT_UINT64, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_clock_id, { "Clock Identity", "cip.time_sync.gm_clock.clock_id", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_clock_class, { "Clock Class", "cip.time_sync.gm_clock.clock_class", FT_UINT16, BASE_DEC, VALS(cip_time_sync_clock_class_vals), 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_accuracy, { "Time Accuracy", "cip.time_sync.gm_clock.time_accuracy", FT_UINT16, BASE_DEC, VALS(cip_time_sync_time_accuracy_vals), 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_offset_scaled_log_variance, { "Offset Scaled Log Variance", "cip.time_sync.gm_clock.offset_scaled_log_variance", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_current_utc_offset, { "Current UTC Offset", "cip.time_sync.gm_clock.current_utc_offset", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_property_flags, { "Time Property Flags", "cip.time_sync.gm_clock.time_property_flags", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_property_flags_leap61, { "Leap indicator 61", "cip.time_sync.gm_clock.time_property_flags.leap61", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0001, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_property_flags_leap59, { "Leap indicator 59", "cip.time_sync.gm_clock.time_property_flags.leap59", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0002, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_property_flags_current_utc_valid, { "Current UTC Offset Valid", "cip.time_sync.gm_clock.time_property_flags.current_utc_valid", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0004, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_property_flags_ptp_timescale, { "PTP Timescale", "cip.time_sync.gm_clock.time_property_flags.ptp_timescale", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0008, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_property_flags_time_traceable, { "Time traceable", "cip.time_sync.gm_clock.time_property_flags.time_traceable", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0010, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_property_flags_freq_traceable, { "Frequency traceable", "cip.time_sync.gm_clock.time_property_flags.freq_traceable", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0020, NULL, HFILL }},
      { &hf_time_sync_gm_clock_time_source, { "Time Source", "cip.time_sync.gm_clock.time_source", FT_UINT16, BASE_DEC, VALS(cip_time_sync_time_source_vals), 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_priority1, { "Priority1", "cip.time_sync.gm_clock.priority1", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_gm_clock_priority2, { "Priority2", "cip.time_sync.gm_clock.priority2", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_parent_clock_clock_id, { "Clock Identity", "cip.time_sync.parent_clock.clock_id", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_parent_clock_port_number, { "Port Number", "cip.time_sync.parent_clock.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_parent_clock_observed_offset_scaled_log_variance, { "Observed Offset Scaled Log Variance", "cip.time_sync.parent_clock.observed_offset_scaled_log_variance", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_parent_clock_observed_phase_change_rate, { "Observed Phase Change Rate", "cip.time_sync.parent_clock.observed_phase_change_rate", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_local_clock_clock_id, { "Clock Identity", "cip.time_sync.local_clock.clock_id", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_local_clock_clock_class, { "Clock Class", "cip.time_sync.local_clock.clock_class", FT_UINT16, BASE_DEC, VALS(cip_time_sync_clock_class_vals), 0, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_accuracy, { "Time Accuracy", "cip.time_sync.local_clock.time_accuracy", FT_UINT16, BASE_DEC, VALS(cip_time_sync_time_accuracy_vals), 0, NULL, HFILL }},
      { &hf_time_sync_local_clock_offset_scaled_log_variance, { "Offset Scaled Log Variance", "cip.time_sync.local_clock.offset_scaled_log_variance", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_local_clock_current_utc_offset, { "Current UTC Offset", "cip.time_sync.local_clock.current_utc_offset", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_property_flags, { "Time Property Flags", "cip.time_sync.local_clock.time_property_flags", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_property_flags_leap61, { "Leap indicator 61", "cip.time_sync.local_clock.time_property_flags.leap61", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0001, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_property_flags_leap59, { "Leap indicator 59", "cip.time_sync.local_clock.time_property_flags.leap59", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0002, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_property_flags_current_utc_valid, { "Current UTC Offset Valid", "cip.time_sync.local_clock.time_property_flags.current_utc_valid", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0004, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_property_flags_ptp_timescale, { "PTP Timescale", "cip.time_sync.local_clock.time_property_flags.ptp_timescale", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0008, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_property_flags_time_traceable, { "Time traceable", "cip.time_sync.local_clock.time_property_flags.time_traceable", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0010, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_property_flags_freq_traceable, { "Frequency traceable", "cip.time_sync.local_clock.time_property_flags.freq_traceable", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0020, NULL, HFILL }},
      { &hf_time_sync_local_clock_time_source, { "Time Source", "cip.time_sync.local_clock.time_source", FT_UINT16, BASE_DEC, VALS(cip_time_sync_time_source_vals), 0, NULL, HFILL }},
      { &hf_time_sync_num_ports, { "Port Number", "cip.time_sync.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_state_info_num_ports, { "Number of Ports", "cip.time_sync.port_state_info.num_ports", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_state_info_port_num, { "Port Number", "cip.time_sync.port_state_info.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_state_info_port_state, { "Port State", "cip.time_sync.port_state_info.port_state", FT_UINT16, BASE_DEC, VALS(cip_time_sync_port_state_vals), 0, NULL, HFILL }},
      { &hf_time_sync_port_enable_cfg_num_ports, { "Number of Ports", "cip.time_sync.port_enable_cfg.num_ports", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_enable_cfg_port_num, { "Port Number", "cip.time_sync.port_enable_cfg.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_enable_cfg_port_enable, { "Port Enable", "cip.time_sync.port_enable_cfg.port_enable", FT_BOOLEAN, 16, TFS(&tfs_enabled_disabled), 0, NULL, HFILL }},
      { &hf_time_sync_port_log_announce_num_ports, { "Number of Ports", "cip.time_sync.port_log_announce.num_ports", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_log_announce_port_num, { "Port Number", "cip.time_sync.port_log_announce.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_log_announce_interval, { "Port Log Announce Interval", "cip.time_sync.port_log_announce.interval", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_log_sync_num_ports, { "Number of Ports", "cip.time_sync.port_log_sync.num_ports", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_log_sync_port_num, { "Port Number", "cip.time_sync.port_log_sync.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_log_sync_port_log_sync_interval, { "Port Log Sync Interval", "cip.time_sync.port_log_sync.port_log_sync_interval", FT_INT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_priority1, { "Priority1", "cip.time_sync.priority1", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_priority2, { "Priority2", "cip.time_sync.priority2", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_domain_number, { "Domain number", "cip.time_sync.domain_number", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_clock_type, { "Clock Type", "cip.time_sync.clock_type", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_clock_type_ordinary, { "Ordinary Clock", "cip.time_sync.clock_type.ordinary", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0080, NULL, HFILL }},
      { &hf_time_sync_clock_type_boundary, { "Boundary Clock", "cip.time_sync.clock_type.boundary", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0040, NULL, HFILL }},
      { &hf_time_sync_clock_type_end_to_end, { "End-to-End Transparent Clock", "cip.time_sync.clock_type.end_to_end", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0010, NULL, HFILL }},
      { &hf_time_sync_clock_type_management, { "Management Node", "cip.time_sync.clock_type.management", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0008, NULL, HFILL }},
      { &hf_time_sync_clock_type_slave_only, { "Slave Only", "cip.time_sync.clock_type.slave_only", FT_BOOLEAN, 16, TFS(&tfs_set_notset), 0x0100, NULL, HFILL }},
      { &hf_time_sync_manufacture_id_oui, { "Manufacture Identity OUI", "cip.time_sync.manufacture_id.oui", FT_UINT24, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_manufacture_id_reserved, { "Reserved", "cip.time_sync.manufacture_id.reserved", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_prod_desc_size, { "Product Description Size", "cip.time_sync.prod_desc_size", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_prod_desc_str, { "Product Description", "cip.time_sync.prod_desc", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_revision_data_size, { "Revision Data Size", "cip.time_sync.revision_data_size", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_revision_data_str, { "Revision Data", "cip.time_sync.revision_data", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_user_desc_size, { "User Description Size", "cip.time_sync.user_desc_size", FT_UINT32, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_user_desc_str, { "User Description", "cip.time_sync.user_desc", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_profile_id_info_num_ports, { "Number of Ports", "cip.time_sync.port_profile_id_info.num_ports", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_profile_id_info_port_num, { "Port Number", "cip.time_sync.port_profile_id_info.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_profile_id_info_profile_id, { "Port Profile Identity", "cip.time_sync.port_profile_id_info.profile_id", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_phys_addr_info_num_ports, { "Number of Ports", "cip.time_sync.port_phys_addr_info.num_ports", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_phys_addr_info_port_num, { "Port Number", "cip.time_sync.port_phys_addr_info.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_phys_addr_info_phys_proto, { "Physical Protocol", "cip.time_sync.port_profile_id_info.phys_proto", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_phys_addr_info_addr_size, { "Size of Address", "cip.time_sync.port_phys_addr_info.addr_size", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_phys_addr_info_phys_addr, { "Port Physical Address", "cip.time_sync.port_profile_id_info.phys_addr", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_proto_addr_info_num_ports, { "Number of Ports", "cip.time_sync.port_proto_addr_info.num_ports", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_proto_addr_info_port_num, { "Port Number", "cip.time_sync.port_proto_addr_info.port_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_proto_addr_info_network_proto, { "Network Protocol", "cip.time_sync.port_proto_addr_info.network_proto", FT_UINT16, BASE_DEC, VALS(cip_time_sync_network_protocol_vals), 0, NULL, HFILL }},
      { &hf_time_sync_port_proto_addr_info_addr_size, { "Size of Address", "cip.time_sync.port_proto_addr_info.addr_size", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_port_proto_addr_info_port_proto_addr, { "Port Protocol Address", "cip.time_sync.port_profile_id_info.port_proto_addr", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_steps_removed, { "Steps Removed", "cip.time_sync.steps_removed", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_sys_time_and_offset_time, { "System Time (Microseconds)", "cip.time_sync.sys_time_and_offset.time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0, NULL, HFILL }},
      { &hf_time_sync_sys_time_and_offset_offset, { "System Offset (Microseconds)", "cip.time_sync.sys_time_and_offset.offset", FT_UINT64, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_port_entry_port, { "Entry Port", "cip.port.entry_port", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_port_type, { "Port Type", "cip.port.type", FT_UINT16, BASE_DEC | BASE_RANGE_STRING, RVALS(cip_port_type_vals), 0, NULL, HFILL } },
      { &hf_port_number, { "Port Number", "cip.port.number", FT_UINT16, BASE_DEC, VALS(cip_port_number_vals), 0, NULL, HFILL } },
      { &hf_port_min_node_num, { "Minimum Node Number", "cip.port.min_node", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_port_max_node_num, { "Maximum Node Number", "cip.port.max_node", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_port_name, { "Port Name", "cip.port.name", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL } },
      { &hf_port_num_comm_object_entries, { "Number of entries", "cip.port.num_comm_object_entries", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
      { &hf_path_len_usint, { "Path Length", "cip.path_len", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL } },
      { &hf_path_len_uint, { "Path Length", "cip.path_len", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL } },

      { &hf_32bitheader, { "32-bit Header", "cip.32bitheader", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL } },
      { &hf_32bitheader_roo, { "ROO", "cip.32bitheader.roo", FT_UINT32, BASE_HEX, NULL, 0xC, "Ready for Ownership of Outputs", HFILL } },
      { &hf_32bitheader_coo, { "COO", "cip.32bitheader.coo", FT_UINT32, BASE_HEX, NULL, 0x2, "Claim Output Ownership", HFILL } },
      { &hf_32bitheader_run_idle, { "Run/Idle", "cip.32bitheader.run_idle", FT_UINT32, BASE_HEX, VALS(cip_run_idle_vals), 0x1, NULL, HFILL } },

      { &hf_cip_connection, { "CIP Connection Index", "cip.connection", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL } },
      { &hf_cip_fwd_open_in, { "Forward Open Request In", "cip.fwd_open_in", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL } },
      { &hf_cip_fwd_close_in, { "Forward Close Request In", "cip.fwd_close_in", FT_FRAMENUM, BASE_NONE, NULL, 0, NULL, HFILL } },
   };

   static hf_register_info hf_cm[] = {
      { &hf_cip_cm_sc, { "Service", "cip.cm.sc", FT_UINT8, BASE_HEX, VALS(cip_sc_vals_cm), CIP_SC_MASK, NULL, HFILL }},
      { &hf_cip_cm_genstat, { "General Status", "cip.cm.genstat", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_gs_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_cm_addstat_size, { "Additional Status Size", "cip.cm.addstat_size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_cm_ext_status, { "Extended Status", "cip.cm.ext_status", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_cm_ext_st_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_cm_add_status, { "Additional Status", "cip.cm.addstat", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_priority, { "Priority", "cip.cm.priority", FT_UINT8, BASE_DEC, NULL, 0x10, NULL, HFILL }},
      { &hf_cip_cm_tick_time, { "Tick time", "cip.cm.tick_time", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL }},
      { &hf_cip_cm_timeout_tick, { "Time-out ticks", "cip.cm.timeout_tick", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_timeout, { "Actual Time Out", "cip.cm.timeout", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_milliseconds, 0, NULL, HFILL }},
      { &hf_cip_cm_ot_connid, { "O->T Network Connection ID", "cip.cm.ot_connid", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_to_connid, { "T->O Network Connection ID", "cip.cm.to_connid", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_connid, { "Connection ID", "cip.connid", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_conn_serial_num, { "Connection Serial Number", "cip.cm.conn_serial_num", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_vendor, { "Originator Vendor ID", "cip.cm.vendor", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_vendor_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_cm_timeout_multiplier, { "Connection Timeout Multiplier", "cip.cm.timeout_multiplier", FT_UINT8, BASE_DEC, VALS(cip_con_time_mult_vals), 0, NULL, HFILL }},
      { &hf_cip_cm_ot_rpi, { "O->T RPI", "cip.cm.otrpi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_cm_ot_timeout, { "O->T Timeout Threshold", "cip.cm.ot_timeout", FT_FLOAT, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0, NULL, HFILL }},
      { &hf_cip_cm_ot_net_params32, { "O->T Network Connection Parameters", "cip.cm.ot_net_params", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_ot_net_params16, { "O->T Network Connection Parameters", "cip.cm.ot_net_params", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_to_rpi, { "T->O RPI", "cip.cm.torpi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_cm_to_timeout, { "T->O Timeout Threshold", "cip.cm.to_timeout", FT_FLOAT, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0, NULL, HFILL }},

      { &hf_cip_safety_nte_ms, { "Network Time Expectation (Produce Timeout)", "cip.safety.nte", FT_FLOAT, BASE_NONE|BASE_UNIT_STRING, &units_milliseconds, 0, NULL, HFILL }},

      { &hf_cip_cm_to_net_params32, { "T->O Network Connection Parameters", "cip.cm.to_net_params", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_to_net_params16, { "T->O Network Connection Parameters", "cip.cm.to_net_params", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_transport_type_trigger, { "Transport Type/Trigger", "cip.cm.transport_type_trigger", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_conn_path_size, { "Connection Path Size", "cip.cm.connpath_size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_cm_ot_api, { "O->T API", "cip.cm.otapi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_cm_to_api, { "T->O API", "cip.cm.toapi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_cm_app_reply_size, { "Application Reply Size", "cip.cm.app_reply_size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_cm_app_reply_data , { "Application Reply", "cip.cm.app_reply_data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_consumer_number, { "Consumer Number", "cip.cm.consumer_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_targ_vendor_id, { "Target Vendor ID", "cip.cm.targ_vendor", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_vendor_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_cm_targ_dev_serial_num, { "Target Device Serial Number", "cip.cm.targ_dev_serial_num", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_targ_conn_serial_num, { "Target Connection Serial Number", "cip.cm.targ_conn_serial_num", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_initial_timestamp, { "Initial Timestamp", "cip.cm.initial_timestamp", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_initial_rollover, { "Initial Rollover Value", "cip.cm.initial_rollover", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_remain_path_size, { "Remaining Path Size", "cip.cm.remain_path_size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_cm_msg_req_size, { "Embedded Message Request Size", "cip.cm.msg_req_size", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0, NULL, HFILL }},
      { &hf_cip_cm_route_path_size, { "Route Path Size", "cip.cm.route_path_size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_cm_orig_serial_num, { "Originator Serial Number", "cip.cm.orig_serial_num", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_fwo_con_size, { "Connection Size", "cip.cm.fwo.consize", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0x01FF, "Fwd Open: Connection size", HFILL }},
      { &hf_cip_cm_lfwo_con_size, { "Connection Size", "cip.cm.fwo.consize", FT_UINT32, BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0xFFFF, "Large Fwd Open: Connection size", HFILL }},
      { &hf_cip_cm_fwo_fixed_var, { "Connection Size Type", "cip.cm.fwo.f_v", FT_UINT16, BASE_DEC, VALS(cip_con_fw_vals), 0x0200, "Fwd Open: Fixed or variable connection size", HFILL }},
      { &hf_cip_cm_lfwo_fixed_var, { "Connection Size Type", "cip.cm.fwo.f_v", FT_UINT32, BASE_DEC, VALS(cip_con_fw_vals), 0x02000000, "Large Fwd Open: Fixed or variable connection size", HFILL }},
      { &hf_cip_cm_fwo_prio, { "Priority", "cip.cm.fwo.prio", FT_UINT16, BASE_DEC, VALS(cip_con_prio_vals), 0x0C00, "Fwd Open: Connection priority", HFILL }},
      { &hf_cip_cm_lfwo_prio, { "Priority", "cip.cm.fwo.prio", FT_UINT32, BASE_DEC, VALS(cip_con_prio_vals), 0x0C000000, "Large Fwd Open: Connection priority", HFILL }},
      { &hf_cip_cm_fwo_typ, { "Connection Type", "cip.cm.fwo.type", FT_UINT16, BASE_DEC, VALS(cip_con_type_vals), 0x6000, "Fwd Open: Connection type", HFILL }},
      { &hf_cip_cm_lfwo_typ, { "Connection Type", "cip.cm.fwo.type", FT_UINT32, BASE_DEC, VALS(cip_con_type_vals), 0x60000000, "Large Fwd Open: Connection type", HFILL }},
      { &hf_cip_cm_fwo_own, { "Redundant Owner", "cip.cm.fwo.owner", FT_UINT16, BASE_DEC, VALS(cip_con_owner_vals), 0x8000, "Fwd Open: Redundant owner bit", HFILL }},
      { &hf_cip_cm_lfwo_own, { "Redundant Owner", "cip.cm.fwo.owner", FT_UINT32, BASE_DEC, VALS(cip_con_owner_vals), 0x80000000, "Large Fwd Open: Redundant owner bit", HFILL }},
      { &hf_cip_cm_fwo_dir, { "Direction", "cip.cm.fwo.dir", FT_BOOLEAN, 8, TFS(&tfs_server_client), CI_PRODUCTION_DIR_MASK, "Fwd Open: Direction", HFILL }},
      { &hf_cip_cm_fwo_trigg, { "Trigger", "cip.cm.fwo.trigger", FT_UINT8, BASE_DEC, VALS(cip_con_trigg_vals), CI_PRODUCTION_TRIGGER_MASK, "Fwd Open: Production trigger", HFILL }},
      { &hf_cip_cm_fwo_class, { "Class", "cip.cm.fwo.transport", FT_UINT8, BASE_DEC, VALS(cip_con_class_vals), CI_TRANSPORT_CLASS_MASK, "Fwd Open: Transport Class", HFILL }},
      { &hf_cip_cm_gco_conn, { "Number of Connections", "cip.cm.gco.conn", FT_UINT8, BASE_DEC, NULL, 0, "GetConnOwner: Number of Connections", HFILL }},
      { &hf_cip_cm_gco_coo_conn, { "COO Connections", "cip.cm.gco.coo_conn", FT_UINT8, BASE_DEC, NULL, 0, "GetConnOwner: COO Connections", HFILL }},
      { &hf_cip_cm_gco_roo_conn, { "ROO Connections", "cip.cm.gco.roo_conn", FT_UINT8, BASE_DEC, NULL, 0, "GetConnOwner: ROO Connections", HFILL }},
      { &hf_cip_cm_gco_last_action, { "Last Action", "cip.cm.gco.la", FT_UINT8, BASE_DEC, VALS(cip_con_last_action_vals), 0, "GetConnOwner: Last Action", HFILL }},
      { &hf_cip_cm_ext112_ot_rpi_type, { "Trigger", "cip.cm.ext112otrpi_type", FT_UINT8, BASE_DEC, VALS(cip_cm_rpi_type_vals), 0, NULL, HFILL }},
      { &hf_cip_cm_ext112_to_rpi_type, { "Trigger", "cip.cm.ext112torpi_type", FT_UINT8, BASE_DEC, VALS(cip_cm_rpi_type_vals), 0, NULL, HFILL }},
      { &hf_cip_cm_ext112_ot_rpi, { "Acceptable O->T RPI", "cip.cm.ext112otrpi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_cm_ext112_to_rpi, { "Acceptable T->O RPI", "cip.cm.ext112torpi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_cm_ext126_size, { "Maximum Size", "cip.cm.ext126_size", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_ext127_size, { "Maximum Size", "cip.cm.ext127_size", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cm_ext128_size, { "Maximum Size", "cip.cm.ext128_size", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }}
   };

  static hf_register_info hf_pccc[] = {
      { &hf_cip_pccc_sc, { "Service", "cip.pccc.sc", FT_UINT8, BASE_HEX, VALS(cip_sc_vals_pccc), CIP_SC_MASK, NULL, HFILL }},
      { &hf_cip_pccc_req_id_len, { "Requestor ID Length", "cip.pccc.req.id.len", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_cip_vend_id, { "CIP Vendor ID", "cip.pccc.cip.vend.id", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_cip_serial_num, { "CIP Serial Number", "cip.pccc.cip.serial.num", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_cmd_code, { "Command Code", "cip.pccc.cmd.code", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_sts_code, { "Status", "cip.pccc.gs.status", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_gs_st_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_pccc_ext_sts_code, { "Extended Status", "cip.pccc.es.status", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_es_st_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_pccc_tns_code, { "Transaction Code", "cip.pccc.tns.code", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_fnc_code_06, { "Function Code", "cip.pccc.fnc.code_06", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_fnc_06_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_pccc_fnc_code_07, { "Function Code", "cip.pccc.fnc.code_07", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_fnc_07_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_pccc_fnc_code_0f, { "Function Code", "cip.pccc.fnc.code_0f", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_fnc_0f_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_pccc_byte_size, { "Byte Size", "cip.pccc.byte.size", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_file_num, { "File Number", "cip.pccc.file.num", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_file_type, { "File Type", "cip.pccc.file.type", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_file_type_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_pccc_element_num, { "Element Number", "cip.pccc.element.num", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_subelement_num, { "Sub-Element Number", "cip.pccc.subelement.num", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
#if 0
      { &hf_cip_pccc_cpu_mode_3a, { "CPU Mode", "cip.pccc.cpu.mode_3a", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_cpu_mode_3a_vals_ext, 0, NULL, HFILL }},
#endif
      { &hf_cip_pccc_cpu_mode_80, { "CPU Mode", "cip.pccc.cpu.mode_80", FT_UINT8, BASE_HEX|BASE_EXT_STRING, &cip_pccc_cpu_mode_80_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_pccc_resp_code, { "Response Code", "cip.pccc.resp.code", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_execute_multi_count, { "Execute Multiple Command - Number of Commands", "cip.pccc.execute.multi.count", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_execute_multi_len, { "Execute Multiple Command - Command Length", "cip.pccc.execute.multi.len", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_pccc_execute_multi_fnc, { "Execute Multiple Command - Function Code", "cip.pccc.execute.multi.code", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},

      { &hf_cip_pccc_data, { "Data", "cip.pccc.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}
   };

   static hf_register_info hf_mb[] = {
      { &hf_cip_mb_sc, { "Service", "cip.mb.sc", FT_UINT8, BASE_HEX, VALS(cip_sc_vals_mb), CIP_SC_MASK, NULL, HFILL }},
      { &hf_cip_mb_read_coils_start_addr, { "Starting Address", "cip.mb.read_coils.start_addr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_coils_num_coils, { "Quantity of Coils", "cip.mb.read_coils.num_coils", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_coils_data, { "Data", "cip.mb.read_coils.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_discrete_inputs_start_addr, { "Starting Address", "cip.mb.read_discrete_inputs.start_addr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_discrete_inputs_num_inputs, { "Quantity of Inputs", "cip.mb.read_discrete_inputs.num_inputs", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_discrete_inputs_data, { "Data", "cip.mb.read_discrete_inputs.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_holding_register_start_addr, { "Starting Address", "cip.mb.read_holding_register.start_addr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_holding_register_num_registers, { "Quantity of Holding Registers", "cip.mb.read_holding_register.num_registers", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_holding_register_data, { "Data", "cip.mb.read_holding_register.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_input_register_start_addr, { "Starting Address", "cip.mb.read_input_register.start_addr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_input_register_num_registers, { "Quantity of Input Registers", "cip.mb.read_input_register.num_registers", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_read_input_register_data, { "Data", "cip.mb.read_input_register.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_write_coils_start_addr, { "Starting Address", "cip.mb.write_coils.start_addr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_write_coils_outputs_forced, { "Outputs Forced", "cip.mb.write_coils.outputs_forced", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_write_coils_num_coils, { "Quantity of Coils", "cip.mb.write_coils.num_coils", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_write_coils_data, { "Data", "cip.mb.write_coils.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_write_registers_start_addr, { "Starting Address", "cip.mb.write_registers.start_addr", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_write_registers_outputs_forced, { "Outputs Forced", "cip.mb.write_registers.outputs_forced", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_write_registers_num_registers, { "Quantity of Holding Registers", "cip.mb.write_registers.num_registers", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_write_registers_data, { "Data", "cip.mb.write_registers.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_mb_data, { "Data", "cip.mb.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }}
   };

   static hf_register_info hf_cco[] = {
      { &hf_cip_cco_sc, { "Service", "cip.cco.sc", FT_UINT8, BASE_HEX, VALS(cip_sc_vals_cco), CIP_SC_MASK, NULL, HFILL }},
      { &hf_cip_cco_format_number, { "Format Number", "cip.cco.format_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_edit_signature, { "Edit Signature", "cip.cco.edit_signature", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_con_flags, { "Connection Flags", "cip.cco.connflags", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_con_type, { "Connection O_T", "cip.cco.con", FT_UINT16, BASE_DEC, VALS(cip_con_vals), 0x0001, NULL, HFILL }},
      { &hf_cip_cco_ot_rtf, { "O->T real time transfer format", "cip.cco.otrtf", FT_UINT16, BASE_DEC, VALS(cip_con_rtf_vals), 0x000E, NULL, HFILL }},
      { &hf_cip_cco_to_rtf, { "T->O real time transfer format", "cip.cco.tortf", FT_UINT16, BASE_DEC, VALS(cip_con_rtf_vals), 0x0070, NULL, HFILL }},
      { &hf_cip_cco_tdi_vendor, { "Vendor ID", "cip.cco.tdi.vendor", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_vendor_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_cco_tdi_devtype, { "Device Type", "cip.cco.tdi.devtype", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_devtype_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_cco_tdi_prodcode, { "Product Code", "cip.cco.tdi.product_code", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_tdi_compatibility, { "Compatibility", "cip.cco.tdi.compatibility", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_tdi_comp_bit, { "Compatibility", "cip.cco.tdi.comp_bit", FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80, NULL, HFILL }},
      { &hf_cip_cco_tdi_majorrev, { "Major Revision", "cip.cco.tdi.major_rev", FT_UINT8, BASE_DEC, NULL, 0x7F, NULL, HFILL }},
      { &hf_cip_cco_tdi_minorrev, { "Minor Revision", "cip.cco.tdi.minor_rev", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_pdi_vendor, { "Vendor ID", "cip.cco.pdi.vendor", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_vendor_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_cco_pdi_devtype, { "Device Type", "cip.cco.pdi.devtype", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_devtype_vals_ext, 0, NULL, HFILL }},
      { &hf_cip_cco_pdi_prodcode, { "Product Code", "cip.cco.pdi.product_code", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_pdi_compatibility, { "Compatibility", "cip.cco.pdi.compatibility", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_pdi_comp_bit, { "Compatibility", "cip.cco.pdi.comp_bit", FT_UINT8, BASE_HEX, VALS(cip_com_bit_vals), 0x80, NULL, HFILL }},
      { &hf_cip_cco_pdi_majorrev, { "Major Revision", "cip.cco.pdi.major_rev", FT_UINT8, BASE_DEC, NULL, 0x7F, NULL, HFILL }},
      { &hf_cip_cco_pdi_minorrev, { "Minor Revision", "cip.cco.pdi.minor_rev", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_cs_data_index, { "CS Data Index Number", "cip.cco.cs_data_index", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_timeout_multiplier, { "Connection Timeout Multiplier", "cip.cco.timeout_multiplier", FT_UINT8, BASE_DEC, VALS(cip_con_time_mult_vals), 0, NULL, HFILL }},
      { &hf_cip_cco_ot_rpi, { "O->T RPI", "cip.cco.otrpi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_cco_ot_net_param32, { "O->T Network Connection Parameters", "cip.cco.ot_net_params", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_ot_net_param16, { "O->T Network Connection Parameters", "cip.cco.ot_net_params", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_to_rpi, { "T->O RPI", "cip.cco.torpi", FT_UINT32, BASE_CUSTOM, CF_FUNC(cip_rpi_api_fmt), 0, NULL, HFILL }},
      { &hf_cip_cco_to_net_param16, { "T->O Network Connection Parameters", "cip.cco.to_net_params", FT_UINT32, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_to_net_param32, { "T->O Network Connection Parameters", "cip.cco.to_net_params", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_transport_type_trigger, { "Transport Type/Trigger", "cip.cco.transport_type_trigger", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_fwo_con_size, { "Connection Size", "cip.cco.consize", FT_UINT16, BASE_DEC, NULL, 0x01FF, NULL, HFILL }},
      { &hf_cip_cco_lfwo_con_size, { "Connection Size", "cip.cco.consize", FT_UINT32, BASE_DEC, NULL, 0xFFFF, NULL, HFILL }},
      { &hf_cip_cco_fwo_fixed_var, { "Connection Size Type", "cip.cco.f_v", FT_UINT16, BASE_DEC, VALS(cip_con_fw_vals), 0x0200, NULL, HFILL }},
      { &hf_cip_cco_lfwo_fixed_var, { "Connection Size Type", "cip.cco.f_v", FT_UINT32, BASE_DEC, VALS(cip_con_fw_vals), 0x02000000, NULL, HFILL }},
      { &hf_cip_cco_fwo_prio, { "Priority", "cip.cco.prio", FT_UINT16, BASE_DEC, VALS(cip_con_prio_vals), 0x0C00, NULL, HFILL }},
      { &hf_cip_cco_lfwo_prio, { "Priority", "cip.cco.prio", FT_UINT32, BASE_DEC, VALS(cip_con_prio_vals), 0x0C000000, NULL, HFILL }},
      { &hf_cip_cco_fwo_typ, { "Connection Type", "cip.cco.type", FT_UINT16, BASE_DEC, VALS(cip_con_type_vals), 0x6000, NULL, HFILL }},
      { &hf_cip_cco_lfwo_typ, { "Connection Type", "cip.cco.type", FT_UINT32, BASE_DEC, VALS(cip_con_type_vals), 0x60000000, NULL, HFILL }},
      { &hf_cip_cco_fwo_own, { "Redundant Owner", "cip.cco.owner", FT_UINT16, BASE_DEC, VALS(cip_con_owner_vals), 0x8000, NULL, HFILL }},
      { &hf_cip_cco_lfwo_own, { "Redundant Owner", "cip.cco.owner", FT_UINT32, BASE_DEC, VALS(cip_con_owner_vals), 0x80000000, NULL, HFILL }},
      { &hf_cip_cco_fwo_dir, { "Direction", "cip.cco.dir", FT_BOOLEAN, 8, TFS(&tfs_server_client), CI_PRODUCTION_DIR_MASK, NULL, HFILL }},
      { &hf_cip_cco_fwo_trigger, { "Trigger", "cip.cco.trigger", FT_UINT8, BASE_DEC, VALS(cip_con_trigg_vals), CI_PRODUCTION_TRIGGER_MASK, NULL, HFILL }},
      { &hf_cip_cco_fwo_class, { "Class", "cip.cco.transport", FT_UINT8, BASE_DEC, VALS(cip_con_class_vals), CI_TRANSPORT_CLASS_MASK, NULL, HFILL }},
      { &hf_cip_cco_conn_path_size, { "Connection Path Size", "cip.cco.connpath_size", FT_UINT8, BASE_DEC|BASE_UNIT_STRING, &units_word_words, 0, NULL, HFILL }},
      { &hf_cip_cco_proxy_config_size, { "Proxy Config Data Size", "cip.cco.proxy_config_size", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_target_config_size, { "Target Config Data Size", "cip.cco.target_config_size", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_iomap_format_number, { "Format number", "cip.cco.iomap_format_number", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_iomap_size, { "Mapping data size", "cip.cco.iomap_size", FT_UINT16, BASE_DEC|BASE_UNIT_STRING, &units_byte_bytes, 0, NULL, HFILL }},
      { &hf_cip_cco_connection_disable, { "Connection Disable", "cip.cco.connection_disable", FT_UINT8, BASE_DEC, NULL, 0x01, NULL, HFILL }},
      { &hf_cip_cco_net_conn_param_attr, { "Net Connection Parameter Attribute Selection", "cip.cco.net_conn_param_attr", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_proxy_config_data, { "Proxy Config Data", "cip.cco.proxy_config_data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_target_config_data, { "Target Config Data", "cip.cco.target_config_data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_iomap_attribute, { "Attribute Data", "cip.cco.iomap", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_safety, { "Safety Parameters", "cip.cco.safety", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_change_type, { "Change Type", "cip.cco.change_type", FT_UINT16, BASE_DEC, VALS(cip_cco_change_type_vals), 0, NULL, HFILL }},
      { &hf_cip_cco_connection_name, { "Connection Name", "cip.cco.connection_name", FT_STRING, BASE_NONE, NULL, 0, NULL, HFILL }},
      { &hf_cip_cco_ext_status, { "Extended Status", "cip.cco.ext_status", FT_UINT16, BASE_HEX|BASE_EXT_STRING, &cip_cm_ext_st_vals_ext, 0, NULL, HFILL }},
   };

   /* Setup protocol subtree array */
   static gint *ett[] = {
      &ett_cip_class_generic,
      &ett_cip,
      &ett_path,
      &ett_path_seg,
      &ett_rrsc,
      &ett_mcsc,
      &ett_cia_path,
      &ett_data_seg,
      &ett_cmd_data,
      &ett_port_path,
      &ett_network_seg,
      &ett_network_seg_safety,
      &ett_network_seg_safety_time_correction_net_params,
      &ett_cip_seg_safety_tunid,
      &ett_cip_seg_safety_tunid_snn,
      &ett_cip_seg_safety_ounid,
      &ett_cip_seg_safety_ounid_snn,
      &ett_status_item,
      &ett_add_status_item,
      &ett_cip_get_attributes_all_item,
      &ett_cip_get_attribute_list,
      &ett_cip_get_attribute_list_item,
      &ett_cip_set_attribute_list,
      &ett_cip_set_attribute_list_item,
      &ett_cip_mult_service_packet,
      &ett_cip_msp_offset,
      &ett_time_sync_gm_clock_flags,
      &ett_time_sync_local_clock_flags,
      &ett_time_sync_port_state_info,
      &ett_time_sync_port_enable_cfg,
      &ett_time_sync_port_log_announce,
      &ett_time_sync_port_log_sync,
      &ett_time_sync_clock_type,
      &ett_time_sync_port_profile_id_info,
      &ett_time_sync_port_phys_addr_info,
      &ett_time_sync_port_proto_addr_info,
      &ett_id_status,
      &ett_32bitheader_tree,
      &ett_connection_info,
   };

   static gint *ett_cm[] = {
      &ett_cip_class_cm,
      &ett_cm_rrsc,
      &ett_cm_mes_req,
      &ett_cm_ncp,
      &ett_cm_cmd_data,
      &ett_cm_ttt,
      &ett_cm_add_status_item,
      &ett_cip_cm_pid,
      &ett_cip_cm_safety
   };

   static gint *ett_pccc[] = {
      &ett_cip_class_pccc,
      &ett_pccc_rrsc,
      &ett_pccc_req_id,
      &ett_pccc_cmd_data
    };

   static gint *ett_mb[] = {
      &ett_cip_class_mb,
      &ett_mb_rrsc,
      &ett_mb_cmd_data
    };

   static gint *ett_cco[] = {
      &ett_cip_class_cco,
      &ett_cco_iomap,
      &ett_cco_con_status,
      &ett_cco_con_flag,
      &ett_cco_tdi,
      &ett_cco_pdi,
      &ett_cco_ncp,
      &ett_cco_rrsc,
      &ett_cco_cmd_data,
      &ett_cco_ttt,
    };

   static ei_register_info ei[] = {
      { &ei_mal_identity_revision, { "cip.malformed.id.revision", PI_MALFORMED, PI_ERROR, "Malformed Identity revision", EXPFILL }},
      { &ei_mal_identity_status, { "cip.malformed.id.status", PI_MALFORMED, PI_ERROR, "Malformed Identity status", EXPFILL } },
      { &ei_mal_msg_rout_num_classes, { "cip.malformed.msg_rout.num_classes", PI_MALFORMED, PI_ERROR, "Malformed Message Router Attribute 1", EXPFILL }},
      { &ei_mal_time_sync_gm_clock, { "cip.malformed.time_sync.gm_clock", PI_MALFORMED, PI_ERROR, "Malformed Grandmaster clock info", EXPFILL }},
      { &ei_mal_time_sync_parent_clock, { "cip.malformed.time_sync.parent_clock", PI_MALFORMED, PI_ERROR, "Malformed Parent clock info", EXPFILL }},
      { &ei_mal_time_sync_local_clock, { "cip.malformed.time_sync.local_clock", PI_MALFORMED, PI_ERROR, "Malformed Local clock info", EXPFILL }},
      { &ei_mal_time_sync_port_state_info, { "cip.malformed.time_sync.port_state_info", PI_MALFORMED, PI_ERROR, "Malformed Port State Info", EXPFILL }},
      { &ei_mal_time_sync_port_state_info_ports, { "cip.malformed.time_sync.port_state_info.ports", PI_MALFORMED, PI_ERROR, "Malformed Port State Info - too many ports", EXPFILL }},
      { &ei_mal_time_sync_port_enable_cfg, { "cip.malformed.time_sync.port_enable_cfg", PI_MALFORMED, PI_ERROR, "Malformed Port Enable Cfg", EXPFILL }},
      { &ei_mal_time_sync_port_enable_cfg_ports, { "cip.malformed.time_sync.port_enable_cfg.ports", PI_MALFORMED, PI_ERROR, "Malformed Port Enable Cfg - too many ports", EXPFILL }},
      { &ei_mal_time_sync_port_log_announce, { "cip.malformed.time_sync.port_log_announce", PI_MALFORMED, PI_ERROR, "Malformed Port Log Announcement Interval Cfg", EXPFILL }},
      { &ei_mal_time_sync_port_log_announce_ports, { "cip.malformed.time_sync.port_log_announce.ports", PI_MALFORMED, PI_ERROR, "Malformed Port Log Announcement Interval Cfg - too many ports", EXPFILL }},
      { &ei_mal_time_sync_port_log_sync, { "cip.malformed.time_sync.port_log_sync", PI_MALFORMED, PI_ERROR, "Malformed Port Log Sync Interval Cfg", EXPFILL }},
      { &ei_mal_time_sync_port_log_sync_ports, { "cip.malformed.time_sync.port_log_sync.ports", PI_MALFORMED, PI_ERROR, "Malformed Port Log Sync Interval Cfg - too many ports", EXPFILL }},
      { &ei_mal_time_sync_clock_type, { "cip.malformed.time_sync.clock_type", PI_MALFORMED, PI_ERROR, "Malformed Clock Type", EXPFILL }},
      { &ei_mal_time_sync_manufacture_id, { "cip.malformed.time_sync.manufacture_id", PI_MALFORMED, PI_ERROR, "Malformed Manufacture Identity", EXPFILL }},
      { &ei_mal_time_sync_prod_desc, { "cip.malformed.time_sync.prod_desc", PI_MALFORMED, PI_ERROR, "Malformed Product Description", EXPFILL }},
      { &ei_mal_time_sync_prod_desc_64, { "cip.malformed.time_sync.prod_desc.limit_64", PI_PROTOCOL, PI_WARN, "Product Description limited to 64 characters", EXPFILL }},
      { &ei_mal_time_sync_prod_desc_size, { "cip.malformed.time_sync.prod_desc.size", PI_MALFORMED, PI_ERROR, "Malformed Product Description - invalid size", EXPFILL }},
      { &ei_mal_time_sync_revision_data, { "cip.malformed.time_sync.revision_data", PI_MALFORMED, PI_ERROR, "Malformed Revision Data", EXPFILL }},
      { &ei_mal_time_sync_revision_data_32, { "cip.malformed.time_sync.revision_data.limit_32", PI_PROTOCOL, PI_WARN, "Revision Data limited to 32 characters", EXPFILL }},
      { &ei_mal_time_sync_revision_data_size, { "cip.malformed.time_sync.revision_data.size", PI_MALFORMED, PI_ERROR, "Malformed Revision Data - invalid size", EXPFILL }},
      { &ei_mal_time_sync_user_desc, { "cip.malformed.time_sync.user_desc", PI_MALFORMED, PI_ERROR, "Malformed User Description", EXPFILL }},
      { &ei_mal_time_sync_user_desc_128, { "cip.malformed.time_sync.user_desc.limit_128", PI_PROTOCOL, PI_WARN, "User Description limited to 128 characters", EXPFILL }},
      { &ei_mal_time_sync_user_desc_size, { "cip.malformed.time_sync.user_desc.size", PI_MALFORMED, PI_ERROR, "Malformed User Description - invalid size", EXPFILL }},
      { &ei_mal_time_sync_port_profile_id_info, { "cip.malformed.time_sync.port_profile_id_info", PI_MALFORMED, PI_ERROR, "Malformed Port Profile Identity Info", EXPFILL }},
      { &ei_mal_time_sync_port_profile_id_info_ports, { "cip.malformed.time_sync.port_profile_id_info.ports", PI_MALFORMED, PI_ERROR, "Malformed Port Profile Identity Info - too many ports", EXPFILL }},
      { &ei_mal_time_sync_port_phys_addr_info, { "cip.malformed.time_sync.port_phys_addr_info", PI_MALFORMED, PI_ERROR, "Malformed Port Physical Address Info", EXPFILL }},
      { &ei_mal_time_sync_port_phys_addr_info_ports, { "cip.malformed.time_sync.port_phys_addr_info.ports", PI_MALFORMED, PI_ERROR, "Malformed Port Physical Address Info - too many ports", EXPFILL }},
      { &ei_mal_time_sync_port_proto_addr_info, { "cip.malformed.time_sync.port_proto_addr_info", PI_MALFORMED, PI_ERROR, "Malformed Port Protocol Address Info", EXPFILL }},
      { &ei_mal_time_sync_port_proto_addr_info_ports, { "cip.malformed.time_sync.port_proto_addr_info.ports", PI_MALFORMED, PI_ERROR, "Malformed Port Protocol Address Info - too many ports", EXPFILL }},
      { &ei_mal_time_sync_sys_time_and_offset, { "cip.malformed.time_sync.sys_time_and_offset", PI_MALFORMED, PI_ERROR, "Malformed System Time and Offset", EXPFILL }},
      { &ei_proto_log_seg_format, { "cip.unsupported.log_seg_format", PI_PROTOCOL, PI_WARN, "Unsupported Logical Segment Format", EXPFILL }},
      { &ei_mal_incomplete_epath, { "cip.malformed.incomplete_epath", PI_MALFORMED, PI_ERROR, "Incomplete EPATH", EXPFILL }},
      { &ei_proto_electronic_key_format, { "cip.unsupported.electronic_key_format", PI_PROTOCOL, PI_WARN, "Unsupported Electronic Key Format", EXPFILL }},
      { &ei_proto_special_segment_format, { "cip.unsupported.special_segment_format", PI_PROTOCOL, PI_WARN, "Unsupported Special Segment Format", EXPFILL }},
      { &ei_proto_log_seg_type, { "cip.unsupported.log_seg_type", PI_PROTOCOL, PI_WARN, "Unsupported Logical Segment Type", EXPFILL }},
      { &ei_proto_log_sub_seg_type, { "cip.unsupported.log_sub_seg_type", PI_PROTOCOL, PI_WARN, "Unsupported Sub-Segment Type", EXPFILL }},
      { &ei_proto_ext_string_format, { "cip.unsupported.ext_string_format", PI_PROTOCOL, PI_WARN, "Unsupported Extended String Format", EXPFILL } },
      { &ei_proto_ext_network, { "cip.malformed.ext_network", PI_PROTOCOL, PI_ERROR, "Malformed Extended Network Segment Format", EXPFILL } },
      { &ei_proto_seg_type, { "cip.unsupported.seg_type", PI_PROTOCOL, PI_WARN, "Unsupported Segment Type", EXPFILL }},
      { &ei_proto_unsupported_datatype, { "cip.unsupported.datatype", PI_PROTOCOL, PI_WARN, "Unsupported Datatype", EXPFILL }},
      { &ei_mal_serv_gal, { "cip.malformed.get_attribute_list", PI_MALFORMED, PI_ERROR, "Malformed Get Attribute List service", EXPFILL }},
      { &ei_mal_serv_gal_count, { "cip.malformed.get_attribute_list.count", PI_MALFORMED, PI_ERROR, "Malformed Get Attribute List attribute list count greater than packet size", EXPFILL }},
      { &ei_mal_serv_sal, { "cip.malformed.set_attribute_list", PI_MALFORMED, PI_ERROR, "Malformed Set Attribute List service", EXPFILL }},
      { &ei_mal_serv_sal_count, { "cip.malformed.set_attribute_list.count", PI_MALFORMED, PI_ERROR, "Malformed Set Attribute List attribute list count greater than packet size", EXPFILL }},
      { &ei_mal_msp_services, { "cip.malformed.msp.services", PI_MALFORMED, PI_WARN, "Multiple Service Packet too many services for packet", EXPFILL }},
      { &ei_mal_msp_inv_offset, { "cip.malformed.msp.inv_offset", PI_MALFORMED, PI_WARN, "Multiple Service Packet service invalid offset", EXPFILL }},
      { &ei_mal_msp_missing_services, { "cip.malformed.msp.missing_services", PI_MALFORMED, PI_ERROR, "Multiple Service Packet service missing Number of Services field", EXPFILL }},
      { &ei_mal_serv_find_next_object, { "cip.malformed.find_next_object", PI_MALFORMED, PI_ERROR, "Find Next Object service missing Number of List Members field", EXPFILL }},
      { &ei_mal_serv_find_next_object_count, { "cip.malformed.find_next_object.count", PI_MALFORMED, PI_ERROR, "Find Next Object instance list count greater than packet size", EXPFILL }},
      { &ei_mal_rpi_no_data, { "cip.malformed.rpi_no_data", PI_MALFORMED, PI_WARN, "RPI not acceptable - missing extended data", EXPFILL }},
      { &ei_mal_fwd_close_missing_data, { "cip.malformed.fwd_close_missing_data", PI_MALFORMED, PI_ERROR, "Forward Close response missing application reply data", EXPFILL }},
      { &ei_mal_opt_attr_list, { "cip.malformed.opt_attr_list", PI_MALFORMED, PI_ERROR, "Optional attribute list missing data", EXPFILL }},
      { &ei_mal_opt_service_list, { "cip.malformed.opt_service_list", PI_MALFORMED, PI_ERROR, "Optional service list missing data", EXPFILL }},
      { &ei_mal_padded_epath_size, { "cip.malformed.epath.size", PI_MALFORMED, PI_ERROR, "Malformed EPATH vs Size", EXPFILL } },
      { &ei_mal_missing_string_data, { "cip.malformed.missing_str_data", PI_MALFORMED, PI_ERROR, "Missing string data", EXPFILL } },

      { &ei_cip_safety_open_type1, { "cip.analysis.safety_open_type1", PI_PROTOCOL, PI_NOTE, "Type 1 - Safety Open with Data", EXPFILL } },
      { &ei_cip_safety_open_type2a, { "cip.analysis.safety_open_type2a", PI_PROTOCOL, PI_NOTE, "Type 2a - Safety Open with SCID check", EXPFILL } },
      { &ei_cip_safety_open_type2b, { "cip.analysis.safety_open_type2b", PI_PROTOCOL, PI_NOTE, "Type 2b - Safety Open without SCID check", EXPFILL } },
      { &ei_cip_no_fwd_close, { "cip.analysis.no_fwd_close", PI_PROTOCOL, PI_NOTE, "No Forward Close seen for this CIP Connection", EXPFILL } },
   };

   module_t *cip_module;
   expert_module_t* expert_cip;

   /* Register the protocol name and description */
   proto_cip = proto_register_protocol("Common Industrial Protocol",
       "CIP", "cip");
   cip_handle = register_dissector("cip", dissect_cip, proto_cip);

   register_dissector("cip_implicit", dissect_cip_implicit, proto_cip);

   /* Required function calls to register the header fields and subtrees used */
   proto_register_field_array(proto_cip, hf, array_length(hf));
   proto_register_subtree_array(ett, array_length(ett));

   expert_cip = expert_register_protocol(proto_cip);
   expert_register_field_array(expert_cip, ei, array_length(ei));

   cip_module = prefs_register_protocol(proto_cip, NULL);
   prefs_register_bool_preference(cip_module, "enhanced_info_column",
      "Display enhanced Info column data",
      "Whether the CIP dissector should display enhanced/verbose data in the Info column for CIP explicit messages",
      &cip_enhanced_info_column);

   subdissector_class_table = register_dissector_table("cip.class.iface",
      "CIP Class Interface Handle", proto_cip, FT_UINT32, BASE_HEX);
   subdissector_symbol_table = register_dissector_table("cip.data_segment.iface",
      "CIP Data Segment Interface Handle", proto_cip, FT_UINT32, BASE_HEX);

   /* Register the protocol name and description */
   proto_cip_class_generic = proto_register_protocol("CIP Class Generic",
       "CIPCLS", "cipcls");
   cip_class_generic_handle = register_dissector("cipcls",
       dissect_cip_class_generic, proto_cip_class_generic);

   /* Register the protocol name and description */
   proto_cip_class_cm = proto_register_protocol("CIP Connection Manager",
       "CIPCM", "cipcm");
   cip_class_cm_handle = register_dissector("cipcm",
       dissect_cip_class_cm, proto_cip_class_cm);
   proto_register_field_array(proto_cip_class_cm, hf_cm, array_length(hf_cm));
   proto_register_subtree_array(ett_cm, array_length(ett_cm));

   proto_cip_class_pccc = proto_register_protocol("CIP PCCC Object",
       "CIPPCCC", "cippccc");
   cip_class_pccc_handle = register_dissector("cippccc",
       dissect_cip_class_pccc, proto_cip_class_pccc);
   proto_register_field_array(proto_cip_class_pccc, hf_pccc, array_length(hf_pccc));
   proto_register_subtree_array(ett_pccc, array_length(ett_pccc));

   proto_cip_class_mb = proto_register_protocol("CIP Modbus Object",
       "CIPMB", "cipmb");
   cip_class_mb_handle = register_dissector("cipmb",
       dissect_cip_class_mb, proto_cip_class_mb);
   proto_register_field_array(proto_cip_class_mb, hf_mb, array_length(hf_mb));
   proto_register_subtree_array(ett_mb, array_length(ett_mb));

   proto_cip_class_cco = proto_register_protocol("CIP Connection Configuration Object",
       "CIPCCO", "cipcco");
   cip_class_cco_handle = register_dissector("cipcco",
       dissect_cip_class_cco, proto_cip_class_cco);
   proto_register_field_array(proto_cip_class_cco, hf_cco, array_length(hf_cco));
   proto_register_subtree_array(ett_cco, array_length(ett_cco));

   /* Register a heuristic dissector on the service of the message so objects
    * can override the dissector for common services */
   heur_subdissector_service = register_heur_dissector_list("cip.sc", proto_cip);

   build_get_attr_all_table();
} /* end of proto_register_cip() */

void
proto_reg_handoff_cip(void)
{
   /* Register for UCMM CIP data, using EtherNet/IP SendRRData service*/
   dissector_add_uint( "enip.srrd.iface", ENIP_CIP_INTERFACE, cip_handle );

   dissector_add_uint("cip.connection.class", CI_CLS_MR, cip_handle);

   /* Register dissector handle for generic class */
   dissector_add_uint( "cip.class.iface", 0, cip_class_generic_handle );

   /* Register dissector handle for Connection Manager */
   dissector_add_uint( "cip.class.iface", CI_CLS_CM, cip_class_cm_handle );

   /* Register dissector handle for the PCCC class */
   dissector_add_uint( "cip.class.iface", CI_CLS_PCCC, cip_class_pccc_handle );

   /* Register dissector handle for Modbus Object */
   dissector_add_uint( "cip.class.iface", CI_CLS_MB, cip_class_mb_handle );
   modbus_handle = find_dissector_add_dependency("modbus", proto_cip_class_mb);

   /* Register dissector handle for Connection Configuration Object */
   dissector_add_uint( "cip.class.iface", CI_CLS_CCO, cip_class_cco_handle );
   heur_dissector_add("cip.sc", dissect_class_cco_heur, "CIP Connection Configuration Object", "cco_cip", proto_cip_class_cco, HEURISTIC_ENABLE);

   proto_enip = proto_get_id_by_filter_name( "enip" );
   proto_modbus = proto_get_id_by_filter_name( "modbus" );

} /* end of proto_reg_handoff_cip() */

/*
 * Editor modelines
 *
 * Local Variables:
 * c-basic-offset: 3
 * tab-width: 8
 * indent-tabs-mode: nil
 * End:
 *
 * ex: set shiftwidth=3 tabstop=8 expandtab:
 * :indentSize=3:tabSize=8:noTabs=true:
 */
