/* packet-pcomtcp.c * Routines for PCOM/TCP dissection * Copyright 2018, Luis Rosa * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later */ /* * PCOM is a protocol to communicate with Unitronics PLCs either by serial or TCP. * Two modes are available, ASCII and Binary. * * See https://unitronicsplc.com/Download/SoftwareUtilities/Unitronics%20PCOM%20Protocol.pdf * */ #include #include #include void proto_reg_handoff_pcomtcp(void); void proto_register_pcomtcp(void); /* Initialize the protocol and registered fields */ static int proto_pcomtcp = -1; static int proto_pcomascii = -1; static int proto_pcombinary = -1; static int hf_pcomtcp_transid = -1; static int hf_pcomtcp_protocol = -1; static int hf_pcomtcp_reserved = -1; static int hf_pcomtcp_length = -1; static int hf_pcomascii_stx = -1; static int hf_pcomascii_unitid = -1; static int hf_pcomascii_command_code = -1; static int hf_pcomascii_address = -1; static int hf_pcomascii_length = -1; static int hf_pcomascii_address_value = -1; static int hf_pcomascii_command = -1; static int hf_pcomascii_checksum = -1; static int hf_pcomascii_etx = -1; static int hf_pcombinary_stx = -1; static int hf_pcombinary_id = -1; static int hf_pcombinary_reserved1 = -1; static int hf_pcombinary_reserved2 = -1; static int hf_pcombinary_reserved3 = -1; static int hf_pcombinary_command = -1; static int hf_pcombinary_reserved4 = -1; static int hf_pcombinary_command_specific = -1; static int hf_pcombinary_data_length = -1; static int hf_pcombinary_header_checksum = -1; static int hf_pcombinary_data = -1; static int hf_pcombinary_footer_checksum = -1; static int hf_pcombinary_etx = -1; static expert_field ei_pcomtcp_reserved_bad_value = EI_INIT; static expert_field ei_pcomascii_command_unsupported = EI_INIT; static expert_field ei_pcombinary_reserved1_bad_value = EI_INIT; static expert_field ei_pcombinary_reserved2_bad_value = EI_INIT; static expert_field ei_pcombinary_reserved3_bad_value = EI_INIT; static expert_field ei_pcombinary_reserved4_bad_value = EI_INIT; static expert_field ei_pcombinary_command_unsupported = EI_INIT; /* Initialize the subtree pointers */ static gint ett_pcomtcp = -1; static gint ett_pcomascii = -1; static gint ett_pcombinary = -1; static dissector_handle_t pcomtcp_handle; static dissector_handle_t pcomascii_handle; static dissector_handle_t pcombinary_handle; /* PCOM/TCP definitions */ #define PCOMTCP_MIN_LENGTH 6 #define PCOMTCP_TCP_PORT 20256 #define PCOM_ASCII 101 #define PCOM_BINARY 102 static range_t *global_pcomtcp_tcp_ports = NULL; /* Port 20256, by default */ /* Translate pcomp_protocol to string */ static const value_string pcomp_protocol_vals[] = { { PCOM_ASCII, "ASCII mode" }, { PCOM_BINARY, "Binary mode" }, { 0, NULL }, }; #define PCOMASCII_ID_COMMAND 0x4944 // "ID" #define PCOMASCII_START_COMMAND 0x434352 // "CCR" #define PCOMASCII_STOP_COMMAND 0x434353 // "CCS" #define PCOMASCII_RESET_COMMAND 0x434345 // "CCE" #define PCOMASCII_INIT_COMMAND 0x434349 // "CCI" #define PCOMASCII_REPLY_ADMIN_COMMAND 0x4343 // "CC" #define PCOMASCII_GET_UNITID 0x5547 // "UG" #define PCOMASCII_SET_UNITID 0x5553 // "US" #define PCOMASCII_GET_RTC 0x5243 // "RC" #define PCOMASCII_SET_RTC 0x5343 // "SC" #define PCOMASCII_READ_INPUTS 0x5245 // "RE" #define PCOMASCII_READ_OUTPUTS 0x5241 // "RA" #define PCOMASCII_READ_SYSTEM_BITS 0x4753 // "GS" #define PCOMASCII_READ_SYSTEM_INTEGERS 0x4746 // "GF" #define PCOMASCII_READ_SYSTEM_LONGS 0x524e48 // "RNH" #define PCOMASCII_READ_SYSTEM_DOUBLE_WORDS 0x524e4a // "RNJ" #define PCOMASCII_READ_MEMORY_BITS 0x5242 // "RB" #define PCOMASCII_READ_MEMORY_INTEGERS 0x5257 // "RW" #define PCOMASCII_READ_MEMORY_LONGS 0x524e4c // "RNL" #define PCOMASCII_READ_MEMORY_DOUBLE_WORDS 0x524e44 // "RND" #define PCOMASCII_READ_LONGS 0x524e // "RN" #define PCOMASCII_WRITE_OUTPUTS 0x5341 // "SA" #define PCOMASCII_WRITE_SYSTEM_BITS 0x5353 // "SS" #define PCOMASCII_WRITE_SYSTEM_INTEGERS 0x5346 // "SF" #define PCOMASCII_WRITE_SYSTEM_LONGS 0x534e48 // "SNH" #define PCOMASCII_WRITE_SYSTEM_DOUBLE_WORDS 0x534e4a // "SNJ" #define PCOMASCII_WRITE_MEMORY_BITS 0x5342 // "SB" #define PCOMASCII_WRITE_MEMORY_INTEGERS 0x5357 // "SW" #define PCOMASCII_WRITE_MEMORY_LONGS 0x534e4c // "SNL" #define PCOMASCII_WRITE_MEMORY_DOUBLE_WORDS 0x534e44 // "SND" #define PCOMASCII_WRITE_LONGS 0x534e // "SN" #define PCOMBINARY_GET_PLC_NAME_REQUEST 0x0c #define PCOMBINARY_GET_PLC_NAME_REPLY 0x8c #define PCOMBINARY_READ_OPERANDS_REQUEST 0x4d #define PCOMBINARY_READ_OPERANDS_REPLY 0xcd #define PCOMBINARY_READ_DATA_TABLE_REQUEST 0x04 #define PCOMBINARY_READ_DATA_TABLE_REPLY 0x84 #define PCOMBINARY_WRITE_DATA_TABLE_REQUEST 0x44 #define PCOMBINARY_WRITE_DATA_TABLE_REPLY 0xc4 /* Translate pcomascii_command_code to string */ static const value_string pcomascii_cc_vals[] = { { PCOMASCII_ID_COMMAND, "Send Identification Command" }, { PCOMASCII_START_COMMAND, "Send Start Command" }, { PCOMASCII_STOP_COMMAND, "Send Stop Command" }, { PCOMASCII_RESET_COMMAND, "Send Reset Command" }, { PCOMASCII_INIT_COMMAND, "Send Init Command" }, { PCOMASCII_REPLY_ADMIN_COMMAND, "Reply of Admin Commands (CC*)" }, { PCOMASCII_GET_UNITID, "Get UnitID" }, { PCOMASCII_SET_UNITID, "Set UnitID" }, { PCOMASCII_GET_RTC, "Get RTC" }, { PCOMASCII_SET_RTC, "Set RTC" }, { PCOMASCII_READ_INPUTS, "Read Inputs" }, { PCOMASCII_READ_OUTPUTS, "Read Outputs" }, { PCOMASCII_READ_SYSTEM_BITS, "Read System Bits" }, { PCOMASCII_READ_SYSTEM_INTEGERS, "Read System Integers" }, { PCOMASCII_READ_SYSTEM_LONGS, "Read System Longs" }, { PCOMASCII_READ_SYSTEM_DOUBLE_WORDS, "Read System Double Words" }, { PCOMASCII_READ_MEMORY_BITS, "Read Memory Bits" }, { PCOMASCII_READ_MEMORY_INTEGERS, "Read Memory Integers" }, { PCOMASCII_READ_MEMORY_LONGS, "Read Memory Longs" }, { PCOMASCII_READ_MEMORY_DOUBLE_WORDS, "Read Memory Double Words" }, { PCOMASCII_READ_LONGS, "Read Longs / Double Words" }, { PCOMASCII_WRITE_OUTPUTS, "Write Outputs" }, { PCOMASCII_WRITE_SYSTEM_BITS, "Write System Bits" }, { PCOMASCII_WRITE_SYSTEM_INTEGERS, "Write System Integers" }, { PCOMASCII_WRITE_SYSTEM_LONGS, "Write System Longs" }, { PCOMASCII_WRITE_SYSTEM_DOUBLE_WORDS,"Write System Double Words" }, { PCOMASCII_WRITE_MEMORY_BITS, "Write Memory Bits" }, { PCOMASCII_WRITE_MEMORY_INTEGERS, "Write Memory Integers" }, { PCOMASCII_WRITE_MEMORY_LONGS, "Write Memory Longs" }, { PCOMASCII_WRITE_MEMORY_DOUBLE_WORDS,"Write Memory Double Words" }, { PCOMASCII_WRITE_LONGS, "Write Longs / Double Words" }, { 0, NULL }, }; /* Translate pcombinary_command requests to string */ static const value_string pcombinary_command_vals_request[] = { { PCOMBINARY_GET_PLC_NAME_REQUEST, "Get PLC Name Request" }, { PCOMBINARY_READ_OPERANDS_REQUEST, "Read Operands Request" }, { PCOMBINARY_READ_DATA_TABLE_REQUEST, "Read Data Table Request" }, { PCOMBINARY_WRITE_DATA_TABLE_REQUEST, "Write Data Table Request" }, { 0, NULL }, }; /* Translate pcombinary_command reply to string */ static const value_string pcombinary_command_vals_reply[] = { { PCOMBINARY_GET_PLC_NAME_REPLY, "Get PLC Name Reply" }, { PCOMBINARY_READ_OPERANDS_REPLY, "Read Operands Reply" }, { PCOMBINARY_READ_DATA_TABLE_REPLY, "Read Data Table Reply" }, { PCOMBINARY_WRITE_DATA_TABLE_REPLY, "Write Data Table Reply" }, { 0, NULL }, }; /* Code to actually dissect the packets */ static int dissect_pcomtcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { /* Set up structures needed to add the protocol subtree and manage it */ proto_item *ti; proto_tree *pcomtcp_tree; tvbuff_t *next_tvb; guint offset = 0; const char *pkt_type = ""; guint8 pcom_mode; const char *pcom_mode_str = ""; proto_item *hf_pcomtcp_reserved_item = NULL; /* Check that the packet is long enough for it to belong to us. */ if (tvb_reported_length(tvb) < PCOMTCP_MIN_LENGTH) return 0; pcom_mode = tvb_get_guint8(tvb, 2); if ( pcom_mode != PCOM_ASCII && pcom_mode != PCOM_BINARY ) return 0; pcom_mode_str = val_to_str(pcom_mode, pcomp_protocol_vals, "Unknown mode (%d)"); if (value_is_in_range(global_pcomtcp_tcp_ports, pinfo->srcport)) pkt_type = "Reply"; else pkt_type = "Query"; col_set_str(pinfo->cinfo, COL_PROTOCOL, "PCOM/TCP"); col_clear(pinfo->cinfo, COL_INFO); col_add_fstr(pinfo->cinfo, COL_INFO, "%s in %s", pkt_type, pcom_mode_str); /* create display subtree for the protocol */ ti = proto_tree_add_item(tree, proto_pcomtcp, tvb, 0, -1, ENC_NA); pcomtcp_tree = proto_item_add_subtree(ti, ett_pcomtcp); proto_tree_add_item(pcomtcp_tree, hf_pcomtcp_transid, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(pcomtcp_tree, hf_pcomtcp_protocol, tvb, offset, 1, ENC_NA); offset += 1; hf_pcomtcp_reserved_item = proto_tree_add_item(pcomtcp_tree, hf_pcomtcp_reserved, tvb, offset, 1, ENC_NA); if(tvb_get_guint8(tvb, offset) !=0){ expert_add_info_format(pinfo, hf_pcomtcp_reserved_item, &ei_pcomtcp_reserved_bad_value,"Isn't 0"); } offset += 1; proto_tree_add_item(pcomtcp_tree, hf_pcomtcp_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); /* dissect the PCOM Data */ offset += 2; next_tvb = tvb_new_subset_remaining(tvb, offset); if( tvb_reported_length_remaining(tvb, offset) > 0 ){ if ( pcom_mode == PCOM_ASCII) call_dissector_with_data(pcomascii_handle, next_tvb, pinfo, tree, &pcom_mode); else call_dissector_with_data(pcombinary_handle, next_tvb, pinfo, tree, &pcom_mode); } return tvb_reported_length(tvb); } static int dissect_pcomascii(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { /* Set up structures needed to add the protocol subtree and manage it */ proto_item *ti; proto_tree *pcomascii_tree; proto_item *hf_pcomascii_command_item = NULL; guint offset = 0; guint16 nvalues; guint8 i; guint8 cc_len; guint32 cc; const gchar* cc_str; const gchar* cc_str2; guint8 op_type; guint8 op_size; /* Create protocol tree */ ti = proto_tree_add_item(tree, proto_pcomascii, tvb, offset, -1, ENC_NA); pcomascii_tree = proto_item_add_subtree(ti, ett_pcomascii); if (value_is_in_range(global_pcomtcp_tcp_ports, pinfo->srcport)) { // Reply proto_tree_add_item(pcomascii_tree, hf_pcomascii_stx, tvb, offset, 2, ENC_ASCII); // "/A" offset += 2; }else{ proto_tree_add_item(pcomascii_tree, hf_pcomascii_stx, tvb, offset, 1, ENC_ASCII); // "/" offset += 1; } proto_tree_add_item(pcomascii_tree, hf_pcomascii_unitid, tvb, offset, 2, ENC_ASCII|ENC_NA); offset += 2; // CCs can be 2 or 3 hex chars cc = tvb_get_ntoh24(tvb, offset); cc_str = try_val_to_str(cc, pcomascii_cc_vals); if ( cc_str != NULL && !value_is_in_range(global_pcomtcp_tcp_ports, pinfo->srcport)) { cc_len = 3; }else { cc = tvb_get_ntohs(tvb, offset); cc_str = try_val_to_str(cc, pcomascii_cc_vals); if (cc_str != NULL ){ cc_len = 2; }else{ cc_len = 0; } } if ( cc_len > 0 ){ cc_str2 = tvb_get_string_enc(pinfo->pool, tvb, offset, cc_len, ENC_ASCII); proto_tree_add_string_format_value(pcomascii_tree, hf_pcomascii_command_code, tvb, offset, cc_len, cc_str2, "%s (%s)", cc_str, cc_str2); offset += cc_len; switch(cc) { case PCOMASCII_READ_INPUTS: case PCOMASCII_READ_OUTPUTS: case PCOMASCII_READ_SYSTEM_BITS: case PCOMASCII_READ_MEMORY_BITS: op_type = 1; // read operation op_size = 1; // 1 char per operand break; case PCOMASCII_READ_SYSTEM_INTEGERS: case PCOMASCII_READ_MEMORY_INTEGERS: op_type = 1; // read operation op_size = 4; // 4 chars per operand break; case PCOMASCII_READ_SYSTEM_LONGS: case PCOMASCII_READ_MEMORY_LONGS: case PCOMASCII_READ_SYSTEM_DOUBLE_WORDS: case PCOMASCII_READ_MEMORY_DOUBLE_WORDS: case PCOMASCII_READ_LONGS: op_type = 1; // read operation op_size = 8; // 8 chars per operand break; case PCOMASCII_WRITE_OUTPUTS: case PCOMASCII_WRITE_SYSTEM_BITS: case PCOMASCII_WRITE_MEMORY_BITS: op_type = 2; // write operation op_size = 1; // 1 char per operand break; case PCOMASCII_WRITE_SYSTEM_INTEGERS: case PCOMASCII_WRITE_MEMORY_INTEGERS: op_type = 2; // write operation op_size = 4; // 4 chars per operand break; case PCOMASCII_WRITE_MEMORY_LONGS: case PCOMASCII_WRITE_SYSTEM_LONGS: case PCOMASCII_WRITE_SYSTEM_DOUBLE_WORDS: case PCOMASCII_WRITE_MEMORY_DOUBLE_WORDS: case PCOMASCII_WRITE_LONGS: op_type = 2; // write operation op_size = 8; // 8 chars per operand break; default: op_type = 0; op_size = 0; break; } if (value_is_in_range(global_pcomtcp_tcp_ports, pinfo->destport)) { // Request if(op_type == 1 || op_type == 2) { // read & write op proto_tree_add_item(pcomascii_tree, hf_pcomascii_address, tvb, offset, 4, ENC_ASCII); offset += 4; proto_tree_add_item(pcomascii_tree, hf_pcomascii_length, tvb, offset, 2, ENC_ASCII); offset += 2; } if(op_type == 2) { // write only nvalues = (tvb_reported_length(tvb)-3-offset) / op_size; for (i = 0; i < nvalues; i++) { proto_tree_add_item(pcomascii_tree, hf_pcomascii_address_value, tvb, offset, op_size , ENC_ASCII); offset += op_size; } } } else { // reply if(op_type == 1) { // read only nvalues = (tvb_reported_length(tvb)-offset-3) / op_size; for (i = 0; i < nvalues; i++) { proto_tree_add_item(pcomascii_tree, hf_pcomascii_address_value, tvb, offset, op_size , ENC_ASCII); offset += op_size; } } } } if (tvb_reported_length(tvb)-offset-3 > 0){ // remaining (variable) bytes between CC and checksum hf_pcomascii_command_item = proto_tree_add_item(pcomascii_tree, hf_pcomascii_command, tvb, offset, tvb_reported_length(tvb)-offset-3, ENC_ASCII); offset += (tvb_reported_length(tvb)-offset-3); //-3 from checksum and etx if(cc_len <= 0){ expert_add_info_format(pinfo, hf_pcomascii_command_item, &ei_pcomascii_command_unsupported, "Unsupported Command"); } } proto_tree_add_item(pcomascii_tree, hf_pcomascii_checksum, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(pcomascii_tree, hf_pcomascii_etx, tvb, offset, 1, ENC_ASCII); return tvb_reported_length(tvb); } static int dissect_pcombinary(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { /* Set up structures needed to add the protocol subtree and manage it */ proto_item *ti; proto_tree *pcombinary_tree; guint offset = 0; proto_item *hf_pcombinary_reserved1_item = NULL; proto_item *hf_pcombinary_reserved2_item = NULL; proto_item *hf_pcombinary_reserved3_item = NULL; proto_item *hf_pcombinary_reserved4_item = NULL; proto_item *hf_pcombinary_command_item = NULL; guint8 command; const gchar* command_str; /* Create protocol tree */ ti = proto_tree_add_item(tree, proto_pcombinary, tvb, offset, -1, ENC_NA); pcombinary_tree = proto_item_add_subtree(ti, ett_pcombinary); proto_tree_add_item(pcombinary_tree, hf_pcombinary_stx, tvb, offset, 6, ENC_ASCII); offset += 6; if (value_is_in_range(global_pcomtcp_tcp_ports, pinfo->srcport)) { // these bytes are transposed hf_pcombinary_reserved1_item = proto_tree_add_item(pcombinary_tree, hf_pcombinary_reserved1, tvb, offset, 1, ENC_NA); if(tvb_get_guint8(tvb, offset) !=254){ expert_add_info_format(pinfo, hf_pcombinary_reserved1_item, &ei_pcombinary_reserved1_bad_value,"Isn't 0xfe"); } offset += 1; proto_tree_add_item(pcombinary_tree, hf_pcombinary_id, tvb, offset, 1, ENC_NA); offset += 1; }else{ proto_tree_add_item(pcombinary_tree, hf_pcombinary_id, tvb, offset, 1, ENC_NA); offset += 1; proto_tree_add_item(pcombinary_tree, hf_pcombinary_reserved1, tvb, offset, 1, ENC_NA); offset += 1; } hf_pcombinary_reserved2_item = proto_tree_add_item(pcombinary_tree, hf_pcombinary_reserved2, tvb, offset, 1, ENC_NA); if( tvb_get_guint8(tvb, offset) !=1) expert_add_info_format(pinfo, hf_pcombinary_reserved2_item, &ei_pcombinary_reserved2_bad_value,"Isn't 1"); offset += 1; hf_pcombinary_reserved3_item = proto_tree_add_item(pcombinary_tree, hf_pcombinary_reserved3, tvb, offset, 3, ENC_LITTLE_ENDIAN); if( tvb_get_letoh24(tvb,offset) != 0) expert_add_info_format(pinfo, hf_pcombinary_reserved3_item, &ei_pcombinary_reserved3_bad_value,"Isn't 0"); offset += 3; command = tvb_get_guint8(tvb, offset); if (value_is_in_range(global_pcomtcp_tcp_ports, pinfo->srcport)) { // reply command_str = try_val_to_str(command, pcombinary_command_vals_reply); }else{ command_str = try_val_to_str(command, pcombinary_command_vals_request); } if ( command_str != NULL ) { proto_tree_add_uint_format_value(pcombinary_tree, hf_pcombinary_command, tvb, offset, 1, command, "%s (%02x)", command_str, command); }else{ hf_pcombinary_command_item = proto_tree_add_item(pcombinary_tree, hf_pcombinary_command, tvb, offset, 1, ENC_NA); expert_add_info_format(pinfo, hf_pcombinary_command_item, &ei_pcombinary_command_unsupported,"Unsupported Command");} offset += 1; hf_pcombinary_reserved4_item = proto_tree_add_item(pcombinary_tree, hf_pcombinary_reserved4, tvb, offset, 1, ENC_NA); if( tvb_get_guint8(tvb, offset) !=0) expert_add_info_format(pinfo, hf_pcombinary_reserved4_item, &ei_pcombinary_reserved4_bad_value,"Isn't 0"); offset += 1; proto_tree_add_item(pcombinary_tree, hf_pcombinary_command_specific, tvb, offset, 6, ENC_NA); offset += 6; proto_tree_add_item(pcombinary_tree, hf_pcombinary_data_length, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(pcombinary_tree, hf_pcombinary_header_checksum, tvb, offset, 2, ENC_NA); offset += 2; if ((tvb_reported_length(tvb) - 27) > 0) // ( -3 footer - 24 header) proto_tree_add_item(pcombinary_tree, hf_pcombinary_data, tvb, offset, tvb_reported_length(tvb)-27, ENC_NA); offset += (tvb_reported_length(tvb)-27); proto_tree_add_item(pcombinary_tree, hf_pcombinary_footer_checksum, tvb, offset, 2, ENC_LITTLE_ENDIAN); offset += 2; proto_tree_add_item(pcombinary_tree, hf_pcombinary_etx, tvb, offset, 1, ENC_ASCII); return tvb_reported_length(tvb); } static void apply_pcomtcp_prefs(void) { global_pcomtcp_tcp_ports = prefs_get_range_value("pcomtcp", "tcp.port"); } void proto_register_pcomtcp(void) { static hf_register_info hf_pcomtcp[] = { { &hf_pcomtcp_transid, { "Transaction Identifier", "pcomtcp.trans_id", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_pcomtcp_protocol, { "Protocol Mode", "pcomtcp.protocol", FT_UINT8, BASE_DEC, VALS(pcomp_protocol_vals), 0x0, NULL, HFILL } }, { &hf_pcomtcp_reserved, { "Reserved", "pcomtcp.reserved", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_pcomtcp_length, { "Length (bytes)", "pcomtcp.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, }; static hf_register_info hf_pcomascii[] = { { &hf_pcomascii_stx, { "STX", "pcomascii.stx", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcomascii_unitid, { "Unit Identifier", "pcomascii.unitid", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcomascii_command_code, { "Command Code", "pcomascii.command_code", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcomascii_address, { "Address", "pcomascii.address", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcomascii_length, { "Length", "pcomascii.length", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcomascii_address_value, { "Address Value", "pcomascii.address_value", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcomascii_command, { "Command", "pcomascii.command", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcomascii_checksum, { "Checksum", "pcomascii.checksum", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcomascii_etx, { "ETX", "pcomascii.etx", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, }; static hf_register_info hf_pcombinary[] = { { &hf_pcombinary_stx, { "STX", "pcombinary.stx", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_id, { "ID (CANBUS or RS485)", "pcombinary.id", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_reserved1, { "Reserved", "pcombinary.reserved1", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_reserved2, { "Reserved", "pcombinary.reserved2", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_reserved3, { "Reserved", "pcombinary.reserved3", FT_UINT24, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_command, { "Command", "pcombinary.command", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_reserved4, { "Reserved", "pcombinary.reserved0", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_command_specific, { "Command Details", "pcombinary.command_specific", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_data_length, { "Data Length", "pcombinary.data_length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_header_checksum, { "(Header) Checksum", "pcombinary.header_checksum", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_data, { "Data", "pcombinary.data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_footer_checksum, { "(Footer) Checksum", "pcombinary.footer_checksum", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL } }, { &hf_pcombinary_etx, { "ETX", "pcombinary.etx", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL } }, }; /* Setup protocol subtree array */ static gint *ett[] = { &ett_pcomtcp, &ett_pcomascii, &ett_pcombinary }; static ei_register_info pcomtcp_ei[] = { { &ei_pcomtcp_reserved_bad_value, { "pcombinary.reserved.bad_value", PI_PROTOCOL, PI_WARN, "Isn't 0", EXPFILL } }, }; static ei_register_info pcombinary_ei[] = { { &ei_pcombinary_reserved1_bad_value, { "pcombinary.reserved1.bad_value", PI_PROTOCOL, PI_WARN, "Isn't 0xfe", EXPFILL } }, { &ei_pcombinary_reserved2_bad_value, { "pcombinary.reserved2.bad_value", PI_PROTOCOL, PI_WARN, "Isn't 1", EXPFILL } }, { &ei_pcombinary_reserved3_bad_value, { "pcombinary.reserved3.bad_value", PI_PROTOCOL, PI_WARN, "Isn't 0", EXPFILL } }, { &ei_pcombinary_reserved4_bad_value, { "pcombinary.reserved4.bad_value", PI_PROTOCOL, PI_WARN, "Isn't 0", EXPFILL } }, { &ei_pcombinary_command_unsupported, { "pcombinary.command.unsupported", PI_PROTOCOL, PI_WARN, "Unsupported Command", EXPFILL } }, }; static ei_register_info pcomascii_ei[] = { { &ei_pcomascii_command_unsupported, { "pcomascii.command.unsupported", PI_PROTOCOL, PI_WARN, "Unsupported Command", EXPFILL } }, }; expert_module_t* expert_pcomtcp; expert_module_t* expert_pcomascii; expert_module_t* expert_pcombinary; /* Register the protocol name and description */ proto_pcomtcp = proto_register_protocol("PCOM/TCP","PCOM/TCP", "pcomtcp"); proto_pcomascii = proto_register_protocol("PCOM ASCII","PCOM ASCII", "pcomascii"); proto_pcombinary = proto_register_protocol("PCOM BINARY","PCOM BINARY", "pcombinary"); pcomtcp_handle = register_dissector("pcomtcp", dissect_pcomtcp, proto_pcomtcp); pcomascii_handle = register_dissector("pcomascii", dissect_pcomascii, proto_pcomascii); pcombinary_handle = register_dissector("pcombinary", dissect_pcombinary, proto_pcombinary); /* Required function calls to register the header fields and subtrees */ proto_register_field_array(proto_pcomtcp, hf_pcomtcp, array_length(hf_pcomtcp)); proto_register_field_array(proto_pcomascii, hf_pcomascii, array_length(hf_pcomascii)); proto_register_field_array(proto_pcombinary, hf_pcombinary, array_length(hf_pcombinary)); proto_register_subtree_array(ett, array_length(ett)); expert_pcomtcp = expert_register_protocol(proto_pcomtcp); expert_pcomascii = expert_register_protocol(proto_pcomascii); expert_pcombinary = expert_register_protocol(proto_pcombinary); expert_register_field_array(expert_pcomtcp, pcomtcp_ei, array_length(pcomtcp_ei)); expert_register_field_array(expert_pcomascii, pcomascii_ei, array_length(pcomascii_ei)); expert_register_field_array(expert_pcombinary, pcombinary_ei, array_length(pcombinary_ei)); prefs_register_protocol(proto_pcomtcp, apply_pcomtcp_prefs); } void proto_reg_handoff_pcomtcp(void) { dissector_add_uint_with_preference("tcp.port", PCOMTCP_TCP_PORT, pcomtcp_handle); apply_pcomtcp_prefs(); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 8 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=8 expandtab: * :indentSize=4:tabSize=8:noTabs=true: */