Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-cp2179.c
blob2407bb5803639127defb534710297201ce88b42f
1 /* packet-cp2179.c
2 * Routines for Communication Protocol 2179 (aka "Cooper 2179") Dissection
3 * By Qiaoyin Yang (qiaoyin[DOT]yang[AT]gmail.com
4 * Copyright 2014-2015,Schweitzer Engineering Laboratories
6 * Enhancements by Chris Bontje (cbontje<at>gmail<dot>com, Aug 2018
7 ************************************************************************************************
8 * Wireshark - Network traffic analyzer
9 * By Gerald Combs <gerald@wireshark.org>
10 * Copyright 1998 Gerald Combs
12 * SPDX-License-Identifier: GPL-2.0-or-later
14 ************************************************************************************************
15 CP2179 protocol is a serial based protocol. The 2179 protocol is implemented with minor variations between vendors.
16 The RTAC implemented the 2179 client supporting a limited function codes and command codes. The RTAC doesn't support
17 multiple function codes in a single request and the dissector also doesn't support decoding these or corresponding responses.
18 * Dissector Notes:
19 A brief explanation of how a request and response messages are formulated in 2179 protocol.
20 The CP2179 request messages will follow the pattern below:
21 AA AA BB CC DD DD XX XX .... XX EE EE
23 A = 16-bit address field. The Most significant 5 bit is the Client address, the 11 bits for RTU address.
24 B = 8-bit Function code
25 C = 8-bit Command code
26 D = 16-bit Number of characters in the data field.
27 X = data field
28 E = 16-bit CRC
30 AA AA BB CC DD EE EE XX XX ... XX FF FF
32 A = 16-bit address field. The Most significant 5 bit is the Client address, the 11 bits for RTU address.
33 B = 8-bit Function code
34 C = 8-bit Status
35 D = 8-bit Port Status
36 E = 16-bit Number of characters
37 X = data field
38 F = 16-bit CRC
39 ************************************************************************************************/
41 #include "config.h"
42 #include <epan/packet.h>
43 #include <epan/conversation.h>
44 #include <epan/prefs.h>
45 #include <epan/proto_data.h>
47 void proto_reg_handoff_cp2179(void);
48 void proto_register_cp2179(void);
50 /* Message Length Constants */
51 #define CP2179_MIN_LENGTH 7
52 #define RESPONSE_HEADER_SIZE 7 /*includes addr, addr, function, status, port status, number of characters */
53 #define BASIC_SCAN_REQ_LEN 8
54 #define SPECIAL_CALC_REQ_ALL_LEN 8
55 #define SBO_OPERATE_REQ_LEN 8
56 #define SPECIAL_CALC_REQ_RANGE_LEN 9
57 #define SBO_SELECT_REQ_LEN 9
58 #define SBO_OPERATE_REPLY_LEN 9
59 #define SBO_SELECT_REPLY_LEN 10
61 static bool cp2179_telnet_clean = true;
63 /* Message Types */
64 #define BASIC_SCAN_REQUEST 1
65 #define BASIC_SCAN_RESPONSE 2
66 #define SPECIAL_CALC_REQUEST_ALL 3
67 #define SPECIAL_CALC_RESPONSE_ALL 4
68 #define SPECIAL_CALC_REQUEST_RANGE 5
69 #define SPECIAL_CALC_RESPONSE_RANGE 6
70 #define SPECIAL_CALC_RESPONSE 7
71 #define SCAN_INCLUSIVE_16_ANALOG_REQUEST 8
72 #define SCAN_INCLUSIVE_16_ANALOG_RESPONSE 9
73 #define SBO_SELECT_REQUEST 10
74 #define SBO_SELECT_RESPONSE 11
75 #define SBO_OPERATE_REQUEST 12
76 #define SBO_OPERATE_RESPONSE 13
77 #define INIT_RTU_REQUEST 14
78 #define INIT_RTU_RESPONSE 15
79 #define RESET_ACC_REQUEST 16
80 #define RESET_ACC_RESPONSE 17
81 #define TIMETAG_INFO_REQUEST 18
82 #define TIMETAG_INFO_RESPONSE 19
83 #define RST_RESPONSE 20
85 /* Message type Lookup */
86 static const value_string cp2179_messagetype_vals[] = {
87 {BASIC_SCAN_REQUEST, "Basic Scan Request"},
88 {BASIC_SCAN_RESPONSE, "Basic Scan Response"},
89 {SPECIAL_CALC_REQUEST_ALL, "Special Calc Request All"},
90 {SPECIAL_CALC_RESPONSE_ALL, "Special Calc Response All"},
91 {SPECIAL_CALC_REQUEST_RANGE, "Special Calc Request a Range"},
92 {SPECIAL_CALC_RESPONSE_RANGE, "Special Calc Response a Range"},
93 {SPECIAL_CALC_RESPONSE, "Special Calc Response"},
94 {SCAN_INCLUSIVE_16_ANALOG_REQUEST, "Scan Inclusive Request"},
95 {SCAN_INCLUSIVE_16_ANALOG_RESPONSE, "Scan Inclusive Response"},
96 {SBO_SELECT_REQUEST, "SBO Select Request"},
97 {SBO_SELECT_RESPONSE, "SBO Select Response"},
98 {SBO_OPERATE_REQUEST, "SBO Operate Request"},
99 {SBO_OPERATE_RESPONSE, "SBO Operate Response"},
100 {INIT_RTU_REQUEST, "INIT RTU Request"},
101 {INIT_RTU_RESPONSE, "INIT RTU Response"},
102 {RESET_ACC_REQUEST, "RESET Accumulator Request"},
103 {RESET_ACC_RESPONSE, "RESET Accumulator Response"},
104 {TIMETAG_INFO_REQUEST, "Time-Tagged Information Request"},
105 {TIMETAG_INFO_RESPONSE, "Time-Tagged Information Response"},
106 {RST_RESPONSE, "RST Response - Out of Sequence SBO"},
107 { 0, NULL }
111 static value_string_ext cp2179_messagetype_vals_ext = VALUE_STRING_EXT_INIT(cp2179_messagetype_vals);
113 /* List contains request data */
114 typedef struct {
115 wmem_list_t *request_frame_data;
116 } cp2179_conversation;
118 /* CP2179 function codes */
119 #define BASIC_SCAN 0x00
120 #define SCAN_INCLUSIVE 0x01
121 #define SCAN_FOR_SPECIAL_CALC 0x03
122 #define RETRIEVE_TIME_TAGGED_INFO 0x04 /* not supported by the RTAC */
123 #define SCAN_BY_TABLE 0x0A
124 #define SUPERVISORY_CONTROL 0x10
125 #define RTU_CONFIG 0x20
126 #define RETURN_RTU_CONFIG 0x25
127 #define REPORT_EXCEPTION_DATA 0x0D
128 #define RST_RESPONSE_CODE 0x80
130 /* Function code Lookup */
131 static const value_string FunctionCodenames[] = {
132 { BASIC_SCAN, "Basic Scan" },
133 { SCAN_INCLUSIVE, "Scan Inclusive"},
134 { SCAN_FOR_SPECIAL_CALC, "Scan Floating Points" },
135 { RETRIEVE_TIME_TAGGED_INFO, "Retrieve Time Tagged Information" },
136 { SCAN_BY_TABLE, "Scan by Table" },
137 { SUPERVISORY_CONTROL, "Supervisory Control" },
138 { RTU_CONFIG, "RTU Internal Control" },
139 { RETURN_RTU_CONFIG, "Return RTU Config"},
140 { REPORT_EXCEPTION_DATA, "Report Exception data"},
141 { RST_RESPONSE_CODE, "RST Response"},
142 { 0, NULL }
145 /* Function Code 0x00 (Basic Scan) Command Codes */
146 #define SIMPLE_STATUS_DATA 0x01
147 #define ALWAYS_RESERVED 0x02
148 #define TWO_BIT_STATUS 0x04
149 #define ANALOG_16_BIT 0x08
150 #define SS_AND_ANA16 0x09
151 #define ACCUMULATOR_16_BIT 0x40
153 /* Function Code 0x03 (Special Calc / Floating Point) Command Codes */
154 #define SPECIAL_CALC_ALL 0x80
155 #define SPECIAL_CALC_RANGE 0x00
157 /* Function Code 0x10 (Supervisory Control) Command Codes */
158 #define SBO_SELECT_OPEN 0x10
159 #define SBO_SELECT_CLOSE 0x11
160 #define SBO_OPERATE 0x20
162 static const value_string cp2179_CommandCodeNames [] = {
163 { SPECIAL_CALC_RANGE, "Request a Range of Special Calc"},
164 { SIMPLE_STATUS_DATA, "Simple Status" },
165 { ALWAYS_RESERVED, "Reserved" },
166 { TWO_BIT_STATUS, "2 Bit Data Status" },
167 { ANALOG_16_BIT, "16 Bit Analog" },
168 { SS_AND_ANA16, "Simple Status and 16-bit Analog" },
169 { SBO_SELECT_OPEN, "SBO Open" },
170 { SBO_SELECT_CLOSE, "SBO Close" },
171 { SBO_OPERATE, "SBO Operate" },
172 { ACCUMULATOR_16_BIT, "16 Bit Pulsed Accumulator" },
173 { SPECIAL_CALC_ALL, "Request All Special Calc Data"},
174 { 0, NULL }
177 static value_string_ext cp2179_CommandCodeNames_ext = VALUE_STRING_EXT_INIT(cp2179_CommandCodeNames);
179 /* Function Code 0x04 (Retrieve Time Tagged Information) Command Codes */
180 #define TIMETAG_INFO_RETRYLAST_SINGLEREC 0x00
181 #define TIMETAG_INFO_RETRYLAST_DUMP 0x20
182 #define TIMETAG_INFO_SINGLEREC 0x40
183 #define TIMETAG_INFO_DUMP 0x60
185 static const value_string cp2179_FC04_CommandCodeNames [] = {
186 { TIMETAG_INFO_RETRYLAST_SINGLEREC, "Retransmit Last Single Record" },
187 { TIMETAG_INFO_RETRYLAST_DUMP, "Retransmit Last Dump All Records" },
188 { TIMETAG_INFO_SINGLEREC, "Return Single Record" },
189 { TIMETAG_INFO_DUMP, "Dump All Records" },
190 { 0, NULL }
194 /* Function Code 0x20 (RTU Control) Command Codes */
195 #define INIT_RTU_CONFIGURATION 0x00
196 #define RESET_ACCUMULATOR 0x11
198 /* Function Code 0x20 Command Code Lookup */
199 static const value_string cp2179_FC20_CommandCodeNames [] = {
200 { INIT_RTU_CONFIGURATION, "Initialize RTU Config" },
201 { RESET_ACCUMULATOR, "Accumulator Reset" },
202 { 0, NULL }
205 /* Holds Request information required to later decode a response */
206 typedef struct {
207 uint32_t fnum; /* frame number */
208 uint16_t address_word;
209 uint8_t function_code;
210 uint8_t commmand_code;
211 uint16_t numberofcharacters;
212 uint8_t *requested_points;
213 } request_frame;
216 static int proto_cp2179;
218 /* Initialize the subtree pointers */
219 static int ett_cp2179;
220 static int ett_cp2179_header;
221 static int ett_cp2179_addr;
222 static int ett_cp2179_fc;
223 static int ett_cp2179_data;
224 static int ett_cp2179_subdata;
225 static int ett_cp2179_event;
227 /* Initialize the protocol and registered fields */
228 static int hf_cp2179_request_frame;
229 static int hf_cp2179_rtu_address;
230 static int hf_cp2179_master_address;
231 static int hf_cp2179_function_code;
232 static int hf_cp2179_nop_flag;
233 static int hf_cp2179_rst_flag;
234 static int hf_cp2179_reserved;
235 static int hf_cp2179_command_code;
236 static int hf_cp2179_command_code_fc04;
237 static int hf_cp2179_command_code_fc20;
238 static int hf_cp2179_sbo_request_point;
239 static int hf_cp2179_resetacc_request_point;
240 static int hf_cp2179_speccalc_request_point;
241 static int hf_cp2179_scaninc_startreq_point;
242 static int hf_cp2179_scaninc_stopreq_point;
243 static int hf_cp2179_number_characters;
244 static int hf_cp2179_analog_16bit;
245 static int hf_cp2179_accumulator;
246 static int hf_cp2179_crc;
247 /* static int hf_cp2179_data_field; */
248 static int hf_cp2179_status_byte;
249 static int hf_cp2179_port_status_byte;
250 static int hf_cp2179_simplestatusbit;
251 static int hf_cp2179_simplestatusbit0;
252 static int hf_cp2179_simplestatusbit1;
253 static int hf_cp2179_simplestatusbit2;
254 static int hf_cp2179_simplestatusbit3;
255 static int hf_cp2179_simplestatusbit4;
256 static int hf_cp2179_simplestatusbit5;
257 static int hf_cp2179_simplestatusbit6;
258 static int hf_cp2179_simplestatusbit7;
259 static int hf_cp2179_simplestatusbit8;
260 static int hf_cp2179_simplestatusbit9;
261 static int hf_cp2179_simplestatusbit10;
262 static int hf_cp2179_simplestatusbit11;
263 static int hf_cp2179_simplestatusbit12;
264 static int hf_cp2179_simplestatusbit13;
265 static int hf_cp2179_simplestatusbit14;
266 static int hf_cp2179_simplestatusbit15;
267 static int hf_cp2179_specialcalc;
268 static int hf_cp2179_2bitstatus;
269 static int hf_cp2179_2bitstatuschg0;
270 static int hf_cp2179_2bitstatuschg1;
271 static int hf_cp2179_2bitstatuschg2;
272 static int hf_cp2179_2bitstatuschg3;
273 static int hf_cp2179_2bitstatuschg4;
274 static int hf_cp2179_2bitstatuschg5;
275 static int hf_cp2179_2bitstatuschg6;
276 static int hf_cp2179_2bitstatuschg7;
277 static int hf_cp2179_2bitstatusstatus0;
278 static int hf_cp2179_2bitstatusstatus1;
279 static int hf_cp2179_2bitstatusstatus2;
280 static int hf_cp2179_2bitstatusstatus3;
281 static int hf_cp2179_2bitstatusstatus4;
282 static int hf_cp2179_2bitstatusstatus5;
283 static int hf_cp2179_2bitstatusstatus6;
284 static int hf_cp2179_2bitstatusstatus7;
285 static int hf_cp2179_timetag_moredata;
286 static int hf_cp2179_timetag_numsets;
287 static int hf_cp2179_timetag_event_type;
288 static int hf_cp2179_timetag_event_date_hundreds;
289 static int hf_cp2179_timetag_event_date_tens;
290 static int hf_cp2179_timetag_event_hour;
291 static int hf_cp2179_timetag_event_minute;
292 static int hf_cp2179_timetag_event_second;
295 static dissector_handle_t cp2179_handle;
297 static int * const cp2179_simplestatus_bits[] = {
298 &hf_cp2179_simplestatusbit0,
299 &hf_cp2179_simplestatusbit1,
300 &hf_cp2179_simplestatusbit2,
301 &hf_cp2179_simplestatusbit3,
302 &hf_cp2179_simplestatusbit4,
303 &hf_cp2179_simplestatusbit5,
304 &hf_cp2179_simplestatusbit6,
305 &hf_cp2179_simplestatusbit7,
306 &hf_cp2179_simplestatusbit8,
307 &hf_cp2179_simplestatusbit9,
308 &hf_cp2179_simplestatusbit10,
309 &hf_cp2179_simplestatusbit11,
310 &hf_cp2179_simplestatusbit12,
311 &hf_cp2179_simplestatusbit13,
312 &hf_cp2179_simplestatusbit14,
313 &hf_cp2179_simplestatusbit15,
314 NULL
317 static int * const cp2179_2bitstatus_bits[] = {
318 &hf_cp2179_2bitstatuschg0,
319 &hf_cp2179_2bitstatuschg1,
320 &hf_cp2179_2bitstatuschg2,
321 &hf_cp2179_2bitstatuschg3,
322 &hf_cp2179_2bitstatuschg4,
323 &hf_cp2179_2bitstatuschg5,
324 &hf_cp2179_2bitstatuschg6,
325 &hf_cp2179_2bitstatuschg7,
326 &hf_cp2179_2bitstatusstatus0,
327 &hf_cp2179_2bitstatusstatus1,
328 &hf_cp2179_2bitstatusstatus2,
329 &hf_cp2179_2bitstatusstatus3,
330 &hf_cp2179_2bitstatusstatus4,
331 &hf_cp2179_2bitstatusstatus5,
332 &hf_cp2179_2bitstatusstatus6,
333 &hf_cp2179_2bitstatusstatus7,
334 NULL
338 /**********************************************************************************************************/
339 /* Clean all instances of 0xFFFF from Telnet payload to compensate for IAC control code (replace w/ 0xFF) */
340 /* Function Duplicated from packet-telnet.c (unescape_and_tvbuffify_telnet_option) */
341 /**********************************************************************************************************/
342 static tvbuff_t *
343 clean_telnet_iac(packet_info *pinfo, tvbuff_t *tvb, int offset, int len)
345 tvbuff_t *telnet_tvb;
346 uint8_t *buf;
347 const uint8_t *spos;
348 uint8_t *dpos;
349 int skip_byte, len_remaining;
351 spos=tvb_get_ptr(tvb, offset, len);
352 buf = (uint8_t *)wmem_alloc(pinfo->pool, len);
353 dpos = buf;
354 skip_byte = 0;
355 len_remaining = len;
356 while(len_remaining > 0){
358 /* Only analyze two sequential bytes of source tvb if we have at least two bytes left */
359 if (len_remaining > 1) {
360 /* If two sequential 0xFF's exist, increment skip_byte counter, decrement */
361 /* len_remaining by 2 and copy a single 0xFF to dest tvb. */
362 if((spos[0]==0xff) && (spos[1]==0xff)){
363 skip_byte++;
364 len_remaining -= 2;
365 *(dpos++) = 0xff;
366 spos += 2;
367 continue;
370 /* If we only have a single byte left, or there were no sequential 0xFF's, copy byte from src tvb to dest tvb */
371 *(dpos++) = *(spos++);
372 len_remaining--;
374 telnet_tvb = tvb_new_child_real_data(tvb, buf, len-skip_byte, len-skip_byte);
375 add_new_data_source(pinfo, telnet_tvb, "Processed Telnet Data");
377 return telnet_tvb;
380 /******************************************************************************************************/
381 /* Code to Dissect Request frames */
382 /******************************************************************************************************/
383 static int
384 dissect_request_frame(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, int offset, uint16_t message_type )
386 /* Set up structures needed to add the protocol subtree and manage it */
387 proto_tree *cp2179_proto_tree = NULL;
388 proto_tree *cp2179_addr_tree = NULL;
389 proto_tree *cp2179_fc_tree = NULL;
391 proto_item *cp2179_proto_item = NULL;
393 uint8_t req_command_code = 0;
394 uint8_t function_code = 0;
396 uint16_t address_word = -1;
397 uint16_t requestnumberofcharacters = 0;
399 cp2179_proto_item = proto_tree_add_item(tree, proto_cp2179, tvb, 0, -1, ENC_NA);
400 cp2179_proto_tree = proto_item_add_subtree(cp2179_proto_item, ett_cp2179_header);
402 /* RTU & Master Address are encoded into a 16-bit word */
403 address_word = tvb_get_letohs(tvb, offset);
404 cp2179_addr_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, 2, ett_cp2179_addr, NULL,
405 "RTU Address: %d, Master Address: %d", (address_word & 0x7FF), ((address_word & 0xF800) >> 11) );
407 proto_tree_add_item(cp2179_addr_tree, hf_cp2179_rtu_address, tvb, offset, 2, ENC_LITTLE_ENDIAN);
408 proto_tree_add_item(cp2179_addr_tree, hf_cp2179_master_address, tvb, offset, 2, ENC_LITTLE_ENDIAN);
409 offset += 2;
411 /* Report the function code */
412 function_code = tvb_get_uint8(tvb, offset) & 0x3f;
413 cp2179_fc_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, 1, ett_cp2179_fc, NULL,
414 "Function Code: %s (0x%02x)", val_to_str_const(function_code, FunctionCodenames, "Unknown Function Code"), function_code);
416 proto_tree_add_item(cp2179_fc_tree, hf_cp2179_function_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
417 proto_tree_add_item(cp2179_fc_tree, hf_cp2179_reserved, tvb, offset, 1, ENC_LITTLE_ENDIAN);
418 offset += 1;
420 /* The command-byte interpretation is dependent on the function code. */
421 switch(message_type)
423 case INIT_RTU_REQUEST:
424 case RESET_ACC_REQUEST:
425 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_command_code_fc20 , tvb, offset, 1, ENC_LITTLE_ENDIAN);
426 break;
428 case BASIC_SCAN_REQUEST:
429 case SCAN_INCLUSIVE_16_ANALOG_REQUEST:
430 req_command_code = tvb_get_uint8(tvb, offset);
431 /* Update Info column with useful information of Command Code Type */
432 col_append_fstr(pinfo->cinfo, COL_INFO, " [ %s ]", val_to_str_ext_const(req_command_code, &cp2179_CommandCodeNames_ext, "Unknown Command Code"));
433 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_command_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
434 break;
436 case TIMETAG_INFO_REQUEST:
437 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_command_code_fc04 , tvb, offset, 1, ENC_LITTLE_ENDIAN);
438 break;
440 default:
441 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_command_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
442 break;
444 offset += 1;
446 requestnumberofcharacters = tvb_get_letohs(tvb, 4);
447 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_number_characters, tvb, offset, 2, ENC_LITTLE_ENDIAN);
448 offset += 2;
450 /* If request number is greater than 0, there is data field present in the request */
451 if ( requestnumberofcharacters > 0 ){
452 /*Depends on the packet type, the data field should be dissect differently*/
453 switch (message_type)
455 case SBO_SELECT_REQUEST:
456 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_sbo_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
457 offset += 1;
458 break;
460 /*Reset Accumulator request data field will always only has 1 byte. The Sequence ID of the Accumulator that it wants to reset*/
461 case RESET_ACC_REQUEST:
462 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_resetacc_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
463 offset += 1;
464 break;
466 /* A special calculation that requests for a range of points will have a list of sequence ID of the Special Calculation points */
467 case SPECIAL_CALC_REQUEST_RANGE:
469 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_speccalc_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
470 offset += 1;
471 }while(tvb_reported_length_remaining(tvb, offset) > 2);
472 break;
474 /*Scan Inclusive will have a starting sequence ID and a ending sequence ID in the data field.*/
475 case SCAN_INCLUSIVE_16_ANALOG_REQUEST:
477 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_scaninc_startreq_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
478 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_scaninc_stopreq_point, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
479 offset += 2;
480 }while(tvb_reported_length_remaining(tvb, offset) > 2);
481 break;
485 /* The last two bytes of the message are a 16-bit CRC */
486 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_crc, tvb, offset, 2, ENC_BIG_ENDIAN);
488 return tvb_reported_length(tvb);
492 /******************************************************************************************************/
493 /* Code to dissect Response frames */
494 /******************************************************************************************************/
495 static int
496 dissect_response_frame(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, int offset, uint16_t message_type)
498 /* Set up structures needed to add the protocol subtree and manage it */
499 proto_item *response_item = NULL;
500 proto_item *cp2179_proto_item = NULL;
501 proto_item *cp2179_subdata_item = NULL;
503 proto_tree *cp2179_proto_tree = NULL;
504 proto_tree *cp2179_addr_tree = NULL;
505 proto_tree *cp2179_fc_tree = NULL;
506 proto_tree *cp2179_data_tree = NULL;
507 proto_tree *cp2179_event_tree = NULL;
509 cp2179_conversation *conv;
510 uint32_t req_frame_num;
511 uint16_t req_address_word;
512 uint8_t req_command_code;
513 bool request_found = false;
514 request_frame *request_data;
516 int analogtestvalue = 0;
517 int analog16_num = 0;
518 int point_num = 0;
520 unsigned function_code;
521 unsigned simplestatusseq = 0x30;
523 uint16_t address_word = 0;
524 uint16_t numberofcharacters = -1;
526 float specialcalvalue = 0;
528 int x, y, num_records = 0, recordsize = 0, num_values = 0;
530 cp2179_proto_item = proto_tree_add_item(tree, proto_cp2179, tvb, 0, -1, ENC_NA);
531 cp2179_proto_tree = proto_item_add_subtree(cp2179_proto_item, ett_cp2179_header);
533 /* RTU & Master Address are encoded into a 16-bit word */
534 address_word = tvb_get_letohs(tvb, offset);
536 cp2179_addr_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, 2, ett_cp2179_addr, NULL,
537 "RTU Address: %d, Master Address: %d", (address_word & 0x7FF), ((address_word & 0xF800) >> 11) );
539 proto_tree_add_item(cp2179_addr_tree, hf_cp2179_rtu_address, tvb, 0, 2, ENC_LITTLE_ENDIAN);
540 proto_tree_add_item(cp2179_addr_tree, hf_cp2179_master_address, tvb, 0, 2, ENC_LITTLE_ENDIAN);
541 offset += 2;
543 /*The response always echos the function code in request, except when the RTU can't perform the required function.
544 It may set the NOP or RST bit. Bit 0 to bit 5 is the field for function codes. Bit 6 is NOP bit. Bit 7 is RST bit. */
545 function_code = tvb_get_uint8(tvb, offset);
547 cp2179_fc_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, 1, ett_cp2179_fc, NULL,
548 "Function Code: %s (0x%02x)", val_to_str_const(function_code, FunctionCodenames, "Unknown Function Code"), function_code);
550 proto_tree_add_item(cp2179_fc_tree, hf_cp2179_function_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
551 proto_tree_add_item(cp2179_fc_tree, hf_cp2179_nop_flag, tvb, offset, 1, ENC_LITTLE_ENDIAN);
552 proto_tree_add_item(cp2179_fc_tree, hf_cp2179_rst_flag, tvb, offset, 1, ENC_LITTLE_ENDIAN);
554 offset += 1;
556 /* Status Byte & Port Status */
557 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_status_byte, tvb, offset, 1, ENC_LITTLE_ENDIAN);
558 offset += 1;
559 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_port_status_byte, tvb, offset, 1, ENC_LITTLE_ENDIAN);
560 offset += 1;
562 /* Number of characters */
563 numberofcharacters = tvb_get_letohs(tvb, 5);
564 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_number_characters, tvb, offset, 2, ENC_LITTLE_ENDIAN);
565 offset += 2;
567 /* get the conversation data */
568 conv = (cp2179_conversation *)p_get_proto_data(wmem_file_scope(), pinfo, proto_cp2179, 0);
570 if (conv) {
571 wmem_list_frame_t *frame = wmem_list_head(conv->request_frame_data);
572 /* Cycle through all logged instances of request frames, looking for request frame number that occurred immediately
573 prior to current frame number that has a matching address word */
574 while (frame && !request_found) {
575 request_data = (request_frame *)wmem_list_frame_data(frame);
576 req_frame_num = request_data->fnum;
577 req_command_code = request_data->commmand_code;
578 req_address_word = request_data->address_word;
579 if ((pinfo->num > req_frame_num) && (req_address_word == address_word)) {
580 response_item = proto_tree_add_uint(cp2179_proto_tree, hf_cp2179_request_frame, tvb, 0, 0, req_frame_num);
581 proto_item_set_generated(response_item);
582 request_found = true;
584 frame = wmem_list_frame_next(frame);
587 if (request_found)
589 switch (message_type)
591 case SBO_SELECT_RESPONSE:
592 case SBO_OPERATE_RESPONSE:
593 case RESET_ACC_RESPONSE:
594 case INIT_RTU_RESPONSE:
596 if ( numberofcharacters > 0 ){
597 /* Based on the message type, process the next byte differently */
598 if ( message_type == SBO_SELECT_RESPONSE ){
599 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_sbo_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
600 offset += 1;
602 if ( message_type == RESET_ACC_RESPONSE ){
603 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_resetacc_request_point, tvb, offset, 1, ENC_LITTLE_ENDIAN);
604 offset += 1;
607 break;
609 case SPECIAL_CALC_RESPONSE:
610 /* Based on the command code from the corresponding request, dissect the data field differently.
611 If required a range of Special calculation, display the requested sequence ID and corresponding
612 values. The requested sequence number is obtained from the previous request frame. */
613 cp2179_data_tree = proto_tree_add_subtree(cp2179_proto_tree, tvb, offset, numberofcharacters, ett_cp2179_data, NULL, "CP2179 Data Field");
615 if (req_command_code == SPECIAL_CALC_ALL ){
617 specialcalvalue = tvb_get_letohieee_float(tvb, offset );
618 proto_tree_add_float_format(cp2179_data_tree, hf_cp2179_specialcalc, tvb, offset, 4, specialcalvalue,
619 "Special Calculation %u : %f", point_num, specialcalvalue);
620 point_num += 1;
621 offset += 4;
622 }while(tvb_reported_length_remaining(tvb, offset) > 2);
624 /*If it request all the special calculation data, dissect all of them and associated a sequence ID with it.*/
625 else if (req_command_code == SPECIAL_CALC_RANGE ){
627 specialcalvalue = tvb_get_letohieee_float(tvb, offset );
628 proto_tree_add_float_format(cp2179_data_tree, hf_cp2179_specialcalc, tvb, offset, 4, specialcalvalue,
629 "Special Calculation %u : %f", request_data->requested_points[point_num], specialcalvalue);
630 point_num += 1;
631 offset += 4;
632 }while(tvb_reported_length_remaining(tvb, offset) > 2);
634 break;
636 case SCAN_INCLUSIVE_16_ANALOG_RESPONSE:
638 cp2179_data_tree = proto_tree_add_subtree(cp2179_proto_tree, tvb, offset, numberofcharacters, ett_cp2179_data, NULL, "CP2179 Data Field");
640 /* Update Info column with useful information of Command Code Type */
641 col_append_fstr(pinfo->cinfo, COL_INFO, " [ %s ]", val_to_str_ext_const(req_command_code, &cp2179_CommandCodeNames_ext, "Unknown Command Code"));
643 /*Report the values of the requested SCAN inclusive data. To figure out which sequence ID the values in the response associated with,
644 we read the request_frame information and show the corresponding sequence ID of the data in response frame.*/
646 analogtestvalue = tvb_get_letohis(tvb, offset);
647 proto_tree_add_int_format(cp2179_data_tree, hf_cp2179_analog_16bit, tvb, offset, 2, request_data->requested_points[point_num],
648 "Analog (16 bit) %u : %d", request_data->requested_points[point_num], analogtestvalue);
649 point_num += 1;
650 offset += 2;
651 }while(tvb_reported_length_remaining(tvb, offset) > 2);
653 break;
655 case BASIC_SCAN_RESPONSE:
657 cp2179_data_tree = proto_tree_add_subtree(cp2179_proto_tree, tvb, offset, numberofcharacters, ett_cp2179_data, NULL, "CP2179 Data Field");
659 /* Update Info column with useful information of Command Code Type */
660 col_append_fstr(pinfo->cinfo, COL_INFO, " [ %s ]", val_to_str_ext_const(req_command_code, &cp2179_CommandCodeNames_ext, "Unknown Command Code"));
662 switch (req_command_code)
664 /* Based the command code from the request frame, we dissect the response data differently.
665 For example, if the request packet has command byte as ANALOG_16_BIT, the
666 the data field in the response should be dissected as 16-bit signed integer(s). */
667 case ACCUMULATOR_16_BIT:
669 analogtestvalue = tvb_get_letohs(tvb, offset);
670 proto_tree_add_uint_format(cp2179_data_tree, hf_cp2179_accumulator, tvb, offset, 2, analog16_num,
671 "Accumulator %u : %u", analog16_num, analogtestvalue);
672 analog16_num += 1;
673 offset += 2;
674 }while(tvb_reported_length_remaining(tvb, offset) > 2);
676 break;
678 case ANALOG_16_BIT:
680 analogtestvalue = tvb_get_letohis(tvb, offset);
681 proto_tree_add_int_format(cp2179_data_tree, hf_cp2179_analog_16bit, tvb, offset, 2, analog16_num,
682 "Analog (16 bit) %u : %i", analog16_num, analogtestvalue);
683 analog16_num += 1;
684 offset += 2;
685 }while(tvb_reported_length_remaining(tvb, offset) > 2);
687 break;
689 case SIMPLE_STATUS_DATA:
691 cp2179_subdata_item = proto_tree_add_bitmask(cp2179_data_tree, tvb, offset, hf_cp2179_simplestatusbit,
692 ett_cp2179_subdata, cp2179_simplestatus_bits, ENC_LITTLE_ENDIAN);
693 proto_item_set_text(cp2179_subdata_item, "Simple Status Point 0x%x", simplestatusseq);
695 simplestatusseq += 1;
696 offset += 2;
697 }while(tvb_reported_length_remaining(tvb, offset) > 2);
699 break;
701 case TWO_BIT_STATUS:
703 cp2179_subdata_item = proto_tree_add_bitmask(cp2179_data_tree, tvb, offset, hf_cp2179_2bitstatus,
704 ett_cp2179_subdata, cp2179_2bitstatus_bits, ENC_LITTLE_ENDIAN);
705 proto_item_set_text(cp2179_subdata_item, "2 Bit Status Point 0x%x", simplestatusseq);
707 simplestatusseq += 1;
708 offset += 2;
709 }while(tvb_reported_length_remaining(tvb, offset) > 2);
711 break;
712 } /* end of command code switch */
714 break;
716 } /* end of basic scan response switch */
718 case TIMETAG_INFO_RESPONSE:
720 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_timetag_moredata, tvb, offset, 1, ENC_LITTLE_ENDIAN);
721 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_timetag_numsets, tvb, offset, 1, ENC_LITTLE_ENDIAN);
723 num_records = tvb_get_uint8(tvb, offset) & 0x7F;
724 offset += 1;
726 if (num_records == 0 || numberofcharacters <= 1)
727 break;
729 recordsize = (numberofcharacters-1) / num_records;
730 num_values = (recordsize-6) / 2; /* Determine how many 16-bit analog values are present in each event record */
732 for (x = 0; x < num_records; x++)
734 cp2179_event_tree = proto_tree_add_subtree_format(cp2179_proto_tree, tvb, offset, recordsize, ett_cp2179_event, NULL, "Event Record # %d", x+1);
735 proto_tree_add_item(cp2179_event_tree, hf_cp2179_timetag_event_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
736 proto_tree_add_item(cp2179_event_tree, hf_cp2179_timetag_event_date_hundreds, tvb, offset+1, 1, ENC_LITTLE_ENDIAN);
737 proto_tree_add_item(cp2179_event_tree, hf_cp2179_timetag_event_date_tens, tvb, offset+2, 1, ENC_LITTLE_ENDIAN);
738 proto_tree_add_item(cp2179_event_tree, hf_cp2179_timetag_event_hour, tvb, offset+3, 1, ENC_LITTLE_ENDIAN);
739 proto_tree_add_item(cp2179_event_tree, hf_cp2179_timetag_event_minute, tvb, offset+4, 1, ENC_LITTLE_ENDIAN);
740 proto_tree_add_item(cp2179_event_tree, hf_cp2179_timetag_event_second, tvb, offset+5, 1, ENC_LITTLE_ENDIAN);
741 offset += 6;
743 for (y = 0; y < num_values; y++)
745 analogtestvalue = tvb_get_letohis(tvb, offset);
746 proto_tree_add_int_format(cp2179_event_tree, hf_cp2179_analog_16bit, tvb, offset, 2, analogtestvalue,
747 "Analog Value (16 bit) %u : %d", y+1, analogtestvalue);
748 offset += 2;
751 break;
753 break;
755 } /* end of message type switch */
757 proto_tree_add_item(cp2179_proto_tree, hf_cp2179_crc, tvb, offset, 2, ENC_BIG_ENDIAN);
759 } /* request found */
761 } /* conversation data found */
763 if (!request_found) {
764 proto_item_append_text(response_item, ", No Request found");
765 return 0;
768 return tvb_reported_length(tvb);
771 /******************************************************************************************************/
772 /* Load Request information into bs request struct */
773 /******************************************************************************************************/
774 static request_frame* copy_request_frame(tvbuff_t *tvb )
776 /* Set up structures needed to add the protocol request and use it for dissecting response packet */
777 unsigned offset = 0;
778 uint8_t idx=0 ;
779 request_frame *frame;
780 uint16_t num_objects=0;
782 /* get a new frame and initialize it */
783 frame = wmem_new(wmem_file_scope(), request_frame);
785 /* update the data within the structure frame */
786 frame->address_word = tvb_get_letohs(tvb, offset); offset +=2;
787 frame->function_code = tvb_get_uint8(tvb, offset); offset +=1;
788 frame->commmand_code = tvb_get_uint8(tvb, offset); offset +=1;
789 frame->numberofcharacters = tvb_get_letohs(tvb, offset);offset +=2;
791 /*Keep track of the request data field in a request.
792 Such as SCAN INCLUSIVE request contains a Start Sequence Number and an Ending Sequence Number. */
793 if (frame->function_code == SCAN_INCLUSIVE) {
794 uint8_t startpt, endpt;
795 startpt = tvb_get_uint8(tvb, offset);
796 endpt = tvb_get_uint8(tvb, offset+1);
797 num_objects = (endpt - startpt) + 1;
798 frame->requested_points = (uint8_t *)wmem_alloc(wmem_file_scope(), num_objects * sizeof(uint8_t));
800 /* We have a range of 'request' points */
801 for (idx = 0; idx < num_objects; idx++) {
802 frame->requested_points[idx] = startpt;
803 startpt++;
805 /* offset += 2; */
807 /* Get Details for all Requested Points */
808 else {
809 num_objects = frame->numberofcharacters;
810 frame->requested_points = (uint8_t *)wmem_alloc(wmem_file_scope(), num_objects * sizeof(uint8_t));
811 for (idx = 0; idx < num_objects; idx++) {
812 frame->requested_points[idx] = tvb_get_uint8(tvb, offset);
813 offset += 1;
818 return frame;
823 /******************************************************************************************************/
824 /* Classify the different packet type */
825 /******************************************************************************************************/
826 static int
827 classify_message_type(tvbuff_t *tvb)
829 int message_type = -1;
830 uint8_t function_code;
831 uint8_t command_code;
832 uint16_t requestnumberofcharacters = 0;
833 uint16_t responsenumberofcharacters = 0;
834 uint16_t message_length = 0;
837 message_length = tvb_reported_length(tvb);
839 /* The response always echos the function code from the request, except when the RTU can't perform the required function.
840 It may set the NOP or RST bit. Bit 0 to bit 5 is the field for function codes. Bit 6 is NOP bit. Bit 7 is RST bit. */
841 function_code = tvb_get_uint8(tvb, 2);
842 command_code = tvb_get_uint8(tvb, 3);
844 /* We still don't know what type of message this is, request or response */
845 /* Get the 'number of characters' value, for both request frames (offset 4) and response frames (offset 5) */
846 requestnumberofcharacters = tvb_get_letohs(tvb, 4);
847 responsenumberofcharacters = tvb_get_letohs(tvb, 5);
849 /* 2179 protocol messages do not have a flag that tells you whether it is a request or a response.
850 Use various values within the message (function code, command code, message length) to determine this */
851 switch (function_code ){
852 /* Basic Scan Operation (Function code 0x00), supported by the RTAC */
853 case BASIC_SCAN:
854 /* Basic Scan Request Message */
855 if ( (requestnumberofcharacters == 0) && (message_length == BASIC_SCAN_REQ_LEN) ) {
856 message_type = BASIC_SCAN_REQUEST ;
858 /* Basic Scan Response Message */
859 else if ( (responsenumberofcharacters > 0) && (message_length > BASIC_SCAN_REQ_LEN) ) {
860 message_type = BASIC_SCAN_RESPONSE;
863 break;
865 /* Supervisory Control (Function code 0x10), supported by the RTAC */
866 case SUPERVISORY_CONTROL:
867 /* SBO Select Request */
868 if ( (requestnumberofcharacters == 1) && (message_length == SBO_SELECT_REQ_LEN) ) {
869 message_type = SBO_SELECT_REQUEST;
871 /* SBO Select Response */
872 else if ( (responsenumberofcharacters == 1) && (message_length == SBO_SELECT_REPLY_LEN) ) {
873 message_type = SBO_SELECT_RESPONSE;
875 /* SBO Operate Request */
876 else if (requestnumberofcharacters == 0) {
877 if ( (message_length == SBO_OPERATE_REQ_LEN) && (command_code == SBO_OPERATE) ) {
878 message_type = SBO_OPERATE_REQUEST;
881 /* SBO Operate Response */
882 else if (responsenumberofcharacters == 0) {
883 if (message_length == SBO_OPERATE_REPLY_LEN) {
884 message_type = SBO_OPERATE_RESPONSE;
888 break;
890 /* Scan for floating point (aka: special calculations) (Function code 0x03), supported by the RTAC */
891 case SCAN_FOR_SPECIAL_CALC:
892 /* Special Calc Request All */
893 if ( (requestnumberofcharacters == 0) && (command_code == SPECIAL_CALC_ALL ) ) {
894 message_type = SPECIAL_CALC_REQUEST_ALL;
896 /* Special Calc Request Range */
897 else if ( (requestnumberofcharacters > 0) && (command_code == SPECIAL_CALC_RANGE ) ) {
898 message_type = SPECIAL_CALC_REQUEST_RANGE;
900 /* Special Calc Response */
901 else if ( (responsenumberofcharacters > 0) && (message_length == (responsenumberofcharacters + 9) ) ) {
902 message_type = SPECIAL_CALC_RESPONSE;
905 break;
907 /* Retrieve Time-tagged information (Function code 0x04), not supported by RTAC */
908 case RETRIEVE_TIME_TAGGED_INFO:
910 if (requestnumberofcharacters == 0) {
911 message_type = TIMETAG_INFO_REQUEST;
913 else {
914 message_type = TIMETAG_INFO_RESPONSE;
917 break;
919 /* Scan Inclusive (Function Code 0x01), supported by the RTAC */
920 case SCAN_INCLUSIVE:
921 /* Scan Inclusive Response */
922 if ( (responsenumberofcharacters > 0) ) {
923 message_type = SCAN_INCLUSIVE_16_ANALOG_RESPONSE;
926 /* Scan Inclusive Request */
927 if( (command_code == ANALOG_16_BIT) && (requestnumberofcharacters == 2) ) {
928 message_type = SCAN_INCLUSIVE_16_ANALOG_REQUEST;
931 break;
933 /* RTU Internal Control and Configuration (Function Code 0x20) */
934 case RTU_CONFIG:
935 if (responsenumberofcharacters == 0) {
936 message_type = INIT_RTU_RESPONSE;
938 if ( (requestnumberofcharacters == 0) && (command_code == INIT_RTU_CONFIGURATION) ) {
939 message_type = INIT_RTU_REQUEST;
942 if (responsenumberofcharacters == 1) {
943 message_type = RESET_ACC_RESPONSE;
945 if ( (requestnumberofcharacters == 1) && (command_code == RESET_ACCUMULATOR) ) {
946 message_type = RESET_ACC_REQUEST;
948 break;
949 case RST_RESPONSE_CODE:
950 message_type = RST_RESPONSE;
951 break;
952 default :
953 message_type = -99;
954 break;
957 return message_type;
962 /******************************************************************************************************/
963 /* Code to dissect CP2179 protocol packets */
964 /******************************************************************************************************/
965 static int
966 dissect_cp2179_pdu(tvbuff_t *cp2179_tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
968 int offset = 0;
969 int16_t message_type;
970 col_set_str(pinfo->cinfo, COL_PROTOCOL, "CP2179");
971 col_clear(pinfo->cinfo,COL_INFO);
973 message_type = classify_message_type(cp2179_tvb);
974 /* set information for Information column for CP2179 */
975 col_set_str(pinfo->cinfo, COL_INFO, val_to_str_ext_const(message_type, &cp2179_messagetype_vals_ext, "Unknown Message Type"));
977 if (!pinfo->fd->visited){
978 conversation_t *conversation = NULL;
979 cp2179_conversation *conv_data = NULL;
981 /* Find a conversation, create a new if no one exists */
982 conversation = find_or_create_conversation(pinfo);
983 conv_data = (cp2179_conversation *)conversation_get_proto_data(conversation, proto_cp2179);
985 if (conv_data == NULL){
986 conv_data = wmem_new(wmem_file_scope(), cp2179_conversation);
987 conv_data->request_frame_data = wmem_list_new(wmem_file_scope());
988 conversation_add_proto_data(conversation, proto_cp2179, (void *)conv_data);
991 p_add_proto_data(wmem_file_scope(), pinfo, proto_cp2179, 0, conv_data);
993 if ((message_type == BASIC_SCAN_REQUEST) || (message_type == SBO_SELECT_REQUEST)
994 ||(message_type == SPECIAL_CALC_REQUEST_ALL)||(message_type == SBO_OPERATE_REQUEST)
995 ||(message_type == SPECIAL_CALC_REQUEST_RANGE)||(message_type == INIT_RTU_REQUEST)
996 ||(message_type == RESET_ACC_REQUEST)||(message_type == SCAN_INCLUSIVE_16_ANALOG_REQUEST)) {
998 /*fill the request frame. It holds the request information, to be used later when dissecting the response. */
999 request_frame *frame_ptr = NULL;
1000 frame_ptr = copy_request_frame(cp2179_tvb);
1002 /*also hold the current frame number*/
1003 frame_ptr->fnum = pinfo->num;
1004 wmem_list_prepend(conv_data->request_frame_data, frame_ptr);
1006 } /* !visited */
1008 if (tvb_reported_length_remaining(cp2179_tvb, offset) > 0){
1009 switch (message_type){
1010 case BASIC_SCAN_REQUEST:
1011 case TIMETAG_INFO_REQUEST:
1012 case SBO_SELECT_REQUEST:
1013 case SBO_OPERATE_REQUEST:
1014 case SPECIAL_CALC_REQUEST_ALL:
1015 case SPECIAL_CALC_REQUEST_RANGE:
1016 case SCAN_INCLUSIVE_16_ANALOG_REQUEST:
1017 case RESET_ACC_REQUEST:
1018 case INIT_RTU_REQUEST:
1019 dissect_request_frame(cp2179_tvb, tree, pinfo, offset, message_type);
1020 break;
1022 case BASIC_SCAN_RESPONSE:
1023 case TIMETAG_INFO_RESPONSE:
1024 case SBO_SELECT_RESPONSE:
1025 case SBO_OPERATE_RESPONSE:
1026 case SPECIAL_CALC_RESPONSE:
1027 case SCAN_INCLUSIVE_16_ANALOG_RESPONSE:
1028 case INIT_RTU_RESPONSE:
1029 case RESET_ACC_RESPONSE:
1030 case RST_RESPONSE:
1031 dissect_response_frame(cp2179_tvb, tree, pinfo, offset, message_type);
1032 break;
1033 default:
1034 break;
1035 } /* packet type */
1036 } /* length remaining */
1038 return tvb_reported_length(cp2179_tvb);
1042 /******************************************************************************************************/
1043 /* Dissect (and possibly Re-assemble) CP2179 protocol payload data */
1044 /******************************************************************************************************/
1045 static int
1046 dissect_cp2179(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
1048 tvbuff_t *cp2179_tvb;
1049 int length = tvb_reported_length(tvb);
1051 /* Check for the packet length, a 2179 Message is at least 7 byte long*/
1052 if(length < CP2179_MIN_LENGTH){
1053 return 0;
1056 if((pinfo->srcport) && cp2179_telnet_clean){
1057 cp2179_tvb = clean_telnet_iac(pinfo, tvb, 0, length);
1059 else{
1060 cp2179_tvb = tvb_new_subset_length( tvb, 0, length);
1063 dissect_cp2179_pdu(cp2179_tvb, pinfo, tree, data);
1065 return length;
1068 void
1069 proto_register_cp2179(void)
1071 static hf_register_info hf[] =
1073 { &hf_cp2179_request_frame,
1074 { "Request Frame", "cp2179.request_frame",
1075 FT_FRAMENUM, BASE_NONE,
1076 NULL, 0x0,
1077 NULL, HFILL }
1080 { &hf_cp2179_rtu_address,
1081 { "RTU Address", "cp2179.RTUAddress",
1082 FT_UINT16, BASE_DEC,
1083 NULL, 0x07FF,
1084 NULL, HFILL }
1087 { &hf_cp2179_master_address,
1088 { "Master Address", "cp2179.MasterAddress",
1089 FT_UINT16, BASE_DEC,
1090 NULL, 0xF800,
1091 NULL, HFILL }
1093 { &hf_cp2179_function_code,
1094 { "Function Code", "cp2179.functioncode",
1095 FT_UINT8, BASE_HEX,
1096 VALS(FunctionCodenames), 0x3F,
1097 NULL, HFILL }
1100 { &hf_cp2179_nop_flag,
1101 { "NOP Flag", "cp2179.nop_flag",
1102 FT_UINT8, BASE_DEC,
1103 NULL, 0x40,
1104 NULL, HFILL }
1107 { &hf_cp2179_rst_flag,
1108 { "RST Flag", "cp2179.rst_flag",
1109 FT_UINT8, BASE_DEC,
1110 NULL, 0x80,
1111 NULL, HFILL }
1114 { &hf_cp2179_reserved,
1115 { "Reserved Bits", "cp2179.Reserved",
1116 FT_UINT8, BASE_DEC,
1117 NULL, 0xC0,
1118 NULL, HFILL }
1121 { &hf_cp2179_command_code,
1122 { "Command Code", "cp2179.commandcode",
1123 FT_UINT8, BASE_HEX,
1124 VALS(cp2179_CommandCodeNames), 0x0,
1125 NULL, HFILL }
1128 { &hf_cp2179_command_code_fc04,
1129 { "Command Code (FC 0x04)", "cp2179.commandcode.fc04",
1130 FT_UINT8, BASE_HEX,
1131 VALS(cp2179_FC04_CommandCodeNames), 0x0,
1132 NULL, HFILL }
1136 { &hf_cp2179_command_code_fc20,
1137 { "Command Code (FC 0x20)", "cp2179.commandcode.fc20",
1138 FT_UINT8, BASE_HEX,
1139 VALS(cp2179_FC20_CommandCodeNames), 0x0,
1140 NULL, HFILL }
1142 { &hf_cp2179_status_byte,
1143 { "RTU Status", "cp2179.rtustatus",
1144 FT_UINT8, BASE_DEC,
1145 0x0, 0x0,
1146 NULL, HFILL }
1148 { &hf_cp2179_port_status_byte,
1149 { "Port Status", "cp2179.portstatus",
1150 FT_UINT8, BASE_DEC,
1151 0x0, 0x0,
1152 NULL, HFILL }
1154 { &hf_cp2179_sbo_request_point,
1155 { "SBO Request Point", "cp2179.sbo_requestpoint",
1156 FT_UINT8, BASE_DEC,
1157 0x0, 0x0,
1158 NULL, HFILL }
1160 { &hf_cp2179_resetacc_request_point,
1161 { "Reset Accumulator Request Point", "cp2179.resetacc_requestpoint",
1162 FT_UINT8, BASE_DEC,
1163 0x0, 0x0,
1164 NULL, HFILL }
1166 { &hf_cp2179_speccalc_request_point,
1167 { "Special Calc Request Point", "cp2179.speccalc_requestpoint",
1168 FT_UINT8, BASE_DEC,
1169 0x0, 0x0,
1170 NULL, HFILL }
1172 { &hf_cp2179_scaninc_startreq_point,
1173 { "Start Request Point", "cp2179.scaninc_startreq_point",
1174 FT_UINT8, BASE_DEC,
1175 0x0, 0x0,
1176 NULL, HFILL }
1178 { &hf_cp2179_scaninc_stopreq_point,
1179 { "Stop Request Point", "cp2179.scaninc_stopreq_point",
1180 FT_UINT8, BASE_DEC,
1181 0x0, 0x0,
1182 NULL, HFILL }
1184 { &hf_cp2179_number_characters,
1185 { "Number of Characters", "cp2179.numberofcharacters",
1186 FT_UINT16, BASE_DEC,
1187 0x0, 0x0,
1188 NULL, HFILL }
1190 { &hf_cp2179_crc,
1191 { "CRC", "cp2179.crc",
1192 FT_UINT16, BASE_HEX,
1193 0x0, 0x0,
1194 NULL, HFILL }
1196 #if 0
1197 { &hf_cp2179_data_field,
1198 { "Data Field", "cp2179.datafield",
1199 FT_UINT8, BASE_DEC,
1200 0x0, 0x0,
1201 NULL, HFILL }
1203 #endif
1204 { &hf_cp2179_accumulator,
1205 { "Accumulator", "cp2179.accumulator",
1206 FT_UINT16, BASE_DEC,
1207 0x0, 0x0,
1208 NULL, HFILL }
1211 { &hf_cp2179_specialcalc,
1212 { "Special Calc", "cp2179.specialcalc",
1213 FT_FLOAT, BASE_NONE,
1214 0x0, 0x0,
1215 NULL, HFILL }
1218 { &hf_cp2179_analog_16bit,
1219 { "Analog 16-bit", "cp2179.analogdata",
1220 FT_INT16, BASE_DEC,
1221 0x0, 0x0,
1222 NULL, HFILL }
1224 { &hf_cp2179_simplestatusbit,
1225 { "Simple Status Bit", "cp2179.simplestatusbit",
1226 FT_UINT16, BASE_HEX,
1227 NULL, 0x0,
1228 NULL, HFILL }
1230 { &hf_cp2179_simplestatusbit0,
1231 { "Simple Status bit 0", "cp2179.simplestatusbit0",
1232 FT_BOOLEAN, 16,
1233 NULL, 0x0001,
1234 NULL, HFILL }
1236 { &hf_cp2179_simplestatusbit1,
1237 { "Simple Status bit 1", "cp2179.simplestatusbit1",
1238 FT_BOOLEAN, 16,
1239 NULL, 0x0002,
1240 NULL, HFILL }
1242 { &hf_cp2179_simplestatusbit2,
1243 { "Simple Status bit 2", "cp2179.simplestatusbit2",
1244 FT_BOOLEAN, 16,
1245 NULL, 0x0004,
1246 NULL, HFILL }
1248 { &hf_cp2179_simplestatusbit3,
1249 { "Simple Status bit 3", "cp2179.simplestatusbit3",
1250 FT_BOOLEAN, 16,
1251 NULL, 0x0008,
1252 NULL, HFILL }
1254 { &hf_cp2179_simplestatusbit4,
1255 { "Simple Status bit 4", "cp2179.simplestatusbit4",
1256 FT_BOOLEAN, 16,
1257 NULL, 0x0010,
1258 NULL, HFILL }
1261 { &hf_cp2179_simplestatusbit5,
1262 { "Simple Status bit 5", "cp2179.simplestatusbit5",
1263 FT_BOOLEAN, 16,
1264 NULL, 0x0020,
1265 NULL, HFILL }
1268 { &hf_cp2179_simplestatusbit6,
1269 { "Simple Status bit 6", "cp2179.simplestatusbit6",
1270 FT_BOOLEAN, 16,
1271 NULL, 0x0040,
1272 NULL, HFILL }
1274 { &hf_cp2179_simplestatusbit7,
1275 { "Simple Status bit 7", "cp2179.simplestatusbit7",
1276 FT_BOOLEAN, 16,
1277 NULL, 0x0080,
1278 NULL, HFILL }
1280 { &hf_cp2179_simplestatusbit8,
1281 { "Simple Status bit 8", "cp2179.simplestatusbit8",
1282 FT_BOOLEAN, 16,
1283 NULL, 0x0100,
1284 NULL, HFILL }
1286 { &hf_cp2179_simplestatusbit9,
1287 { "Simple Status bit 9", "cp2179.simplestatusbit9",
1288 FT_BOOLEAN, 16,
1289 NULL, 0x0200,
1290 NULL, HFILL }
1292 { &hf_cp2179_simplestatusbit10,
1293 { "Simple Status bit 10", "cp2179.simplestatusbit10",
1294 FT_BOOLEAN, 16,
1295 NULL, 0x0400,
1296 NULL, HFILL }
1298 { &hf_cp2179_simplestatusbit11,
1299 { "Simple Status bit 11", "cp2179.simplestatusbit11",
1300 FT_BOOLEAN, 16,
1301 NULL, 0x0800,
1302 NULL, HFILL }
1304 { &hf_cp2179_simplestatusbit12,
1305 { "Simple Status bit 12", "cp2179.simplestatusbit12",
1306 FT_BOOLEAN, 16,
1307 NULL, 0x1000,
1308 NULL, HFILL }
1310 { &hf_cp2179_simplestatusbit13,
1311 { "Simple Status bit 13", "cp2179.simplestatusbit13",
1312 FT_BOOLEAN, 16,
1313 NULL, 0x2000,
1314 NULL, HFILL }
1317 { &hf_cp2179_simplestatusbit14,
1318 { "Simple Status bit 14", "cp2179.simplestatusbit14",
1319 FT_BOOLEAN, 16,
1320 NULL, 0x4000,
1321 NULL, HFILL }
1324 { &hf_cp2179_simplestatusbit15,
1325 { "Simple Status bit 15", "cp2179.simplestatusbit15",
1326 FT_BOOLEAN, 16,
1327 NULL, 0x8000,
1328 NULL, HFILL }
1330 { &hf_cp2179_2bitstatus,
1331 { "2 Bit Status", "cp2179.twobitstatus",
1332 FT_UINT16, BASE_HEX,
1333 NULL, 0x0,
1334 NULL, HFILL }
1336 { &hf_cp2179_2bitstatuschg0,
1337 { "2 Bit Status Change 0", "cp2179.twobitstatuschg0",
1338 FT_BOOLEAN, 16,
1339 NULL, 0x0001,
1340 NULL, HFILL }
1342 { &hf_cp2179_2bitstatuschg1,
1343 { "2 Bit Status Change 1", "cp2179.twobitstatuschg1",
1344 FT_BOOLEAN, 16,
1345 NULL, 0x0002,
1346 NULL, HFILL }
1348 { &hf_cp2179_2bitstatuschg2,
1349 { "2 Bit Status Change 2", "cp2179.twobitstatuschg2",
1350 FT_BOOLEAN, 16,
1351 NULL, 0x0004,
1352 NULL, HFILL }
1354 { &hf_cp2179_2bitstatuschg3,
1355 { "2 Bit Status Change 3", "cp2179.twobitstatuschg3",
1356 FT_BOOLEAN, 16,
1357 NULL, 0x0008,
1358 NULL, HFILL }
1360 { &hf_cp2179_2bitstatuschg4,
1361 { "2 Bit Status Change 4", "cp2179.twobitstatuschg4",
1362 FT_BOOLEAN, 16,
1363 NULL, 0x0010,
1364 NULL, HFILL }
1366 { &hf_cp2179_2bitstatuschg5,
1367 { "2 Bit Status Change 5", "cp2179.twobitstatuschg5",
1368 FT_BOOLEAN, 16,
1369 NULL, 0x0020,
1370 NULL, HFILL }
1373 { &hf_cp2179_2bitstatuschg6,
1374 { "2 Bit Status Change 6", "cp2179.twobitstatuschg6",
1375 FT_BOOLEAN, 16,
1376 NULL, 0x0040,
1377 NULL, HFILL }
1380 { &hf_cp2179_2bitstatuschg7,
1381 { "2 Bit Status Change 7", "cp2179.twobitstatuschg7",
1382 FT_BOOLEAN, 16,
1383 NULL, 0x0080,
1384 NULL, HFILL }
1387 { &hf_cp2179_2bitstatusstatus0,
1388 { "2 Bit Status bit 0", "cp2179.twobitstatusbit0",
1389 FT_BOOLEAN, 16,
1390 NULL, 0x0100,
1391 NULL, HFILL }
1393 { &hf_cp2179_2bitstatusstatus1,
1394 { "2 Bit Status bit 1", "cp2179.twobitstatusbit1",
1395 FT_BOOLEAN, 16,
1396 NULL, 0x0200,
1397 NULL, HFILL }
1399 { &hf_cp2179_2bitstatusstatus2,
1400 { "2 Bit Status bit 2", "cp2179.twobitstatusbit2",
1401 FT_BOOLEAN, 16,
1402 NULL, 0x0400,
1403 NULL, HFILL }
1405 { &hf_cp2179_2bitstatusstatus3,
1406 { "2 Bit Status bit 3", "cp2179.twobitstatusbit3",
1407 FT_BOOLEAN, 16,
1408 NULL, 0x0800,
1409 NULL, HFILL }
1411 { &hf_cp2179_2bitstatusstatus4,
1412 { "2 Bit Status bit 4", "cp2179.twobitstatusbit4",
1413 FT_BOOLEAN, 16,
1414 NULL, 0x1000,
1415 NULL, HFILL }
1417 { &hf_cp2179_2bitstatusstatus5,
1418 { "2 Bit Status bit 5", "cp2179.twobitstatusbit5",
1419 FT_BOOLEAN, 16,
1420 NULL, 0x2000,
1421 NULL, HFILL }
1423 { &hf_cp2179_2bitstatusstatus6,
1424 { "2 Bit Status bit 6", "cp2179.twobitstatusbit6",
1425 FT_BOOLEAN, 16,
1426 NULL, 0x4000,
1427 NULL, HFILL }
1429 { &hf_cp2179_2bitstatusstatus7,
1430 { "2 Bit Status bit 7", "cp2179.twobitstatusbit7",
1431 FT_BOOLEAN, 16,
1432 NULL, 0x8000,
1433 NULL, HFILL }
1435 { &hf_cp2179_timetag_moredata,
1436 { "Additional Records Available", "cp2179.timetag.moredata",
1437 FT_UINT8, BASE_DEC,
1438 NULL, 0x80,
1439 NULL, HFILL }
1441 { &hf_cp2179_timetag_numsets,
1442 { "Number of Sets", "cp2179.timetag.numsets",
1443 FT_UINT8, BASE_DEC,
1444 NULL, 0x7F,
1445 NULL, HFILL }
1447 { &hf_cp2179_timetag_event_type,
1448 { "Event Type", "cp2179.timetag.event.type",
1449 FT_UINT8, BASE_DEC,
1450 NULL, 0x0,
1451 NULL, HFILL }
1453 { &hf_cp2179_timetag_event_date_hundreds,
1454 { "Julian Date (Hundreds)", "cp2179.timetag.event.date.hundreds",
1455 FT_UINT8, BASE_DEC,
1456 NULL, 0x0F,
1457 NULL, HFILL }
1459 { &hf_cp2179_timetag_event_date_tens,
1460 { "Julian Date (Tens)", "cp2179.timetag.event.date.tens",
1461 FT_UINT8, BASE_DEC,
1462 NULL, 0x0,
1463 NULL, HFILL }
1465 { &hf_cp2179_timetag_event_hour,
1466 { "Hour", "cp2179.timetag.event.hour",
1467 FT_UINT8, BASE_DEC,
1468 NULL, 0x0,
1469 NULL, HFILL }
1471 { &hf_cp2179_timetag_event_minute,
1472 { "Minute", "cp2179.timetag.event.minute",
1473 FT_UINT8, BASE_DEC,
1474 NULL, 0x0,
1475 NULL, HFILL }
1477 { &hf_cp2179_timetag_event_second,
1478 { "Second", "cp2179.timetag.event.second",
1479 FT_UINT8, BASE_DEC,
1480 NULL, 0x0,
1481 NULL, HFILL }
1485 /* Setup protocol subtree array */
1486 static int *ett[] = {
1487 &ett_cp2179,
1488 &ett_cp2179_header,
1489 &ett_cp2179_addr,
1490 &ett_cp2179_fc,
1491 &ett_cp2179_data,
1492 &ett_cp2179_subdata,
1493 &ett_cp2179_event
1497 module_t *cp2179_module;
1499 proto_cp2179 = proto_register_protocol ("CP2179 Protocol", "CP2179", "cp2179");
1500 cp2179_handle = register_dissector("cp2179", dissect_cp2179, proto_cp2179);
1501 proto_register_field_array(proto_cp2179, hf, array_length(hf));
1502 proto_register_subtree_array(ett, array_length(ett));
1504 /* Register required preferences for CP2179 Encapsulated-over-TCP decoding */
1505 cp2179_module = prefs_register_protocol(proto_cp2179, NULL);
1507 /* Telnet protocol IAC (0xFF) processing; defaults to true to allow Telnet Encapsulated Data */
1508 prefs_register_bool_preference(cp2179_module, "telnetclean",
1509 "Remove extra 0xFF (IAC) bytes from Telnet-encapsulated data",
1510 "Whether the SEL Protocol dissector should automatically pre-process Telnet data to remove IAC bytes",
1511 &cp2179_telnet_clean);
1517 void
1518 proto_reg_handoff_cp2179(void)
1520 dissector_add_for_decode_as_with_preference("tcp.port", cp2179_handle);
1521 dissector_add_for_decode_as("rtacser.data", cp2179_handle);
1525 * Editor modelines - https://www.wireshark.org/tools/modelines.html
1527 * Local variables:
1528 * c-basic-offset: 4
1529 * tab-width: 8
1530 * indent-tabs-mode: nil
1531 * End:
1533 * vi: set shiftwidth=4 tabstop=8 expandtab:
1534 * :indentSize=4:tabSize=8:noTabs=true: