2 * Routines for IEC-60870-5-101 & 104 Protocol disassembly
4 * Copyright (c) 2008 by Joan Ramio <joan@ramio.cat>
5 * Joan is a masculine catalan name. Search the Internet for Joan Pujol (alias Garbo).
7 * Copyright (c) 2009 by Kjell Hultman <kjell.hultman@gmail.com>
8 * Added dissection of signal (ASDU) information.
9 * Kjell is also a masculine name, but a Scandinavian one.
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1999 Gerald Combs
15 * SPDX-License-Identifier: GPL-2.0-or-later
20 #include <math.h> /* floor */
22 #include <epan/packet.h>
23 #include <epan/prefs.h>
24 #include <epan/expert.h>
25 #include <epan/reassemble.h>
27 #include <wsutil/array.h>
28 #include <wsutil/str_util.h>
29 #include "packet-tcp.h"
31 void proto_register_iec60870_104(void);
32 void proto_reg_handoff_iec60870_104(void);
34 void proto_register_iec60870_101(void);
35 void proto_reg_handoff_iec60870_101(void);
37 void proto_register_iec60870_5_103(void);
38 void proto_reg_handoff_iec60870_5_103(void);
40 void proto_register_iec60870_asdu(void);
42 static int dissect_iec60870_asdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
);
44 static dissector_handle_t iec60870_asdu_handle
;
45 static dissector_handle_t iec60870_104_handle
;
46 static dissector_handle_t iec60870_101_handle
;
47 static dissector_handle_t iec60870_5_103_handle
;
49 static reassembly_table iec60870_reassemble_table
;
51 /* the asdu header structure */
65 unsigned asdu_addr_len
;
69 /* ASDU command value/status structure */
77 /* QOC qualifier-bits */
78 uint16_t QU
; /* qualifier-value */
79 bool ZeroP
; /* No pulse */
80 bool ShortP
; /* Short Pulse */
81 bool LongP
; /* Long Pulse */
82 bool Persist
; /* Persistent output */
83 bool SE
; /* Select (1) / Execute (0) */
86 #define IEC104_PORT 2404
88 /* Define the iec101/103/104 protos */
89 static int proto_iec60870_101
;
90 static int proto_iec60870_5_103
;
91 static int proto_iec60870_104
;
92 static int proto_iec60870_asdu
;
94 /* Protocol constants */
95 #define APCI_START 0x68
97 #define APCI_START_LEN 2
98 #define APCI_DATA_LEN (APCI_LEN - APCI_START_LEN)
99 #define APDU_MIN_LEN 4
100 #define APDU_MAX_LEN 253
102 /* ASDU_HEAD_LEN: Includes Asdu head and first IOA */
103 #define ASDU_HEAD_LEN 9
111 /* Type I is only lowest bit set to 0 */
115 #define APCI_TYPE_UNKNOWN 4
117 static const value_string apci_types
[] = {
124 /* Constants relative to the field, independent of the field position in the byte */
125 /* U (Unnumbered) constants */
126 #define U_STARTDT_ACT 0x01
127 #define U_STARTDT_CON 0x02
128 #define U_STOPDT_ACT 0x04
129 #define U_STOPDT_CON 0x08
130 #define U_TESTFR_ACT 0x10
131 #define U_TESTFR_CON 0x20
132 static const value_string u_types
[] = {
133 { U_STARTDT_ACT
, "STARTDT act" },
134 { U_STARTDT_CON
, "STARTDT con" },
135 { U_STOPDT_ACT
, "STOPDT act" },
136 { U_STOPDT_CON
, "STOPDT con" },
137 { U_TESTFR_ACT
, "TESTFR act" },
138 { U_TESTFR_CON
, "TESTFR con" },
142 /* ASDU types (TypeId) */
143 #define M_SP_NA_1 1 /* single-point information */
144 #define M_SP_TA_1 2 /* single-point information with time tag */
145 #define M_DP_NA_1 3 /* double-point information */
146 #define M_DP_TA_1 4 /* double-point information with time tag */
147 #define M_ST_NA_1 5 /* step position information */
148 #define M_ST_TA_1 6 /* step position information with time tag */
149 #define M_BO_NA_1 7 /* bitstring of 32 bits */
150 #define M_BO_TA_1 8 /* bitstring of 32 bits with time tag */
151 #define M_ME_NA_1 9 /* measured value, normalized value */
152 #define M_ME_TA_1 10 /* measured value, normalized value with time tag */
153 #define M_ME_NB_1 11 /* measured value, scaled value */
154 #define M_ME_TB_1 12 /* measured value, scaled value with time tag */
155 #define M_ME_NC_1 13 /* measured value, short floating point number */
156 #define M_ME_TC_1 14 /* measured value, short floating point number with time tag */
157 #define M_IT_NA_1 15 /* integrated totals */
158 #define M_IT_TA_1 16 /* integrated totals with time tag */
159 #define M_PS_NA_1 20 /* packed single-point information with status change detection */
160 #define M_ME_ND_1 21 /* measured value, normalized value without quality descriptor */
161 #define M_SP_TB_1 30 /* single-point information with time tag CP56Time2a */
162 #define M_DP_TB_1 31 /* double-point information with time tag CP56Time2a */
163 #define M_ST_TB_1 32 /* step position information with time tag CP56Time2a */
164 #define M_BO_TB_1 33 /* bitstring of 32 bit with time tag CP56Time2a */
165 #define M_ME_TD_1 34 /* measured value, normalized value with time tag CP56Time2a */
166 #define M_ME_TE_1 35 /* measured value, scaled value with time tag CP56Time2a */
167 #define M_ME_TF_1 36 /* measured value, short floating point number with time tag CP56Time2a */
168 #define M_IT_TB_1 37 /* integrated totals with time tag CP56Time2a */
169 #define M_EP_TD_1 38 /* event of protection equipment with time tag CP56Time2a */
170 #define M_EP_TE_1 39 /* packed start events of protection equipment with time tag CP56Time2a */
171 #define M_EP_TF_1 40 /* packed output circuit information of protection equipment with time tag CP56Time2a */
172 #define S_IT_TC_1 41 /* integrated totals containing time tagged security statistics */
173 #define C_SC_NA_1 45 /* single command */
174 #define C_DC_NA_1 46 /* double command */
175 #define C_RC_NA_1 47 /* regulating step command */
176 #define C_SE_NA_1 48 /* set point command, normalized value */
177 #define C_SE_NB_1 49 /* set point command, scaled value */
178 #define C_SE_NC_1 50 /* set point command, short floating point number */
179 #define C_BO_NA_1 51 /* bitstring of 32 bits */
180 #define C_SC_TA_1 58 /* single command with time tag CP56Time2a */
181 #define C_DC_TA_1 59 /* double command with time tag CP56Time2a */
182 #define C_RC_TA_1 60 /* regulating step command with time tag CP56Time2a */
183 #define C_SE_TA_1 61 /* set point command, normalized value with time tag CP56Time2a */
184 #define C_SE_TB_1 62 /* set point command, scaled value with time tag CP56Time2a */
185 #define C_SE_TC_1 63 /* set point command, short floating-point number with time tag CP56Time2a */
186 #define C_BO_TA_1 64 /* bitstring of 32 bits with time tag CP56Time2a */
187 #define M_EI_NA_1 70 /* end of initialization */
188 #define S_CH_NA_1 81 /* authentication challenge */
189 #define S_RP_NA_1 82 /* authentication reply */
190 #define S_AR_NA_1 83 /* aggressive mode authentication request session key status request */
191 #define S_KR_NA_1 84 /* session key status request */
192 #define S_KS_NA_1 85 /* session key status */
193 #define S_KC_NA_1 86 /* session key change */
194 #define S_ER_NA_1 87 /* authentication error */
195 #define S_US_NA_1 90 /* user status change */
196 #define S_UQ_NA_1 91 /* update key change request */
197 #define S_UR_NA_1 92 /* update key change reply */
198 #define S_UK_NA_1 93 /* update key change symmetric */
199 #define S_UA_NA_1 94 /* update key change asymmetric */
200 #define S_UC_NA_1 95 /* update key change confirmation */
201 #define C_IC_NA_1 100 /* interrogation command */
202 #define C_CI_NA_1 101 /* counter interrogation command */
203 #define C_RD_NA_1 102 /* read command */
204 #define C_CS_NA_1 103 /* clock synchronization command */
205 #define C_RP_NA_1 105 /* reset process command */
206 #define C_TS_TA_1 107 /* test command with time tag CP56Time2a */
207 #define P_ME_NA_1 110 /* parameter of measured value, normalized value */
208 #define P_ME_NB_1 111 /* parameter of measured value, scaled value */
209 #define P_ME_NC_1 112 /* parameter of measured value, short floating-point number */
210 #define P_AC_NA_1 113 /* parameter activation */
211 #define F_FR_NA_1 120 /* file ready */
212 #define F_SR_NA_1 121 /* section ready */
213 #define F_SC_NA_1 122 /* call directory, select file, call file, call section */
214 #define F_LS_NA_1 123 /* last section, last segment */
215 #define F_AF_NA_1 124 /* ack file, ack section */
216 #define F_SG_NA_1 125 /* segment */
217 #define F_DR_TA_1 126 /* directory */
218 #define F_SC_NB_1 127 /* Query Log - Request archive file */
219 static const value_string asdu_types
[] = {
220 { M_SP_NA_1
, "M_SP_NA_1" },
221 { M_SP_TA_1
, "M_SP_TA_1" },
222 { M_DP_NA_1
, "M_DP_NA_1" },
223 { M_DP_TA_1
, "M_DP_TA_1" },
224 { M_ST_NA_1
, "M_ST_NA_1" },
225 { M_ST_TA_1
, "M_ST_TA_1" },
226 { M_BO_NA_1
, "M_BO_NA_1" },
227 { M_BO_TA_1
, "M_BO_TA_1" },
228 { M_ME_NA_1
, "M_ME_NA_1" },
229 { M_ME_TA_1
, "M_ME_TA_1" },
230 { M_ME_NB_1
, "M_ME_NB_1" },
231 { M_ME_TB_1
, "M_ME_TB_1" },
232 { M_ME_NC_1
, "M_ME_NC_1" },
233 { M_ME_TC_1
, "M_ME_TC_1" },
234 { M_IT_NA_1
, "M_IT_NA_1" },
235 { M_IT_TA_1
, "M_IT_TA_1" },
236 { M_PS_NA_1
, "M_PS_NA_1" },
237 { M_ME_ND_1
, "M_ME_ND_1" },
238 { M_SP_TB_1
, "M_SP_TB_1" },
239 { M_DP_TB_1
, "M_DP_TB_1" },
240 { M_ST_TB_1
, "M_ST_TB_1" },
241 { M_BO_TB_1
, "M_BO_TB_1" },
242 { M_ME_TD_1
, "M_ME_TD_1" },
243 { M_ME_TE_1
, "M_ME_TE_1" },
244 { M_ME_TF_1
, "M_ME_TF_1" },
245 { M_IT_TB_1
, "M_IT_TB_1" },
246 { M_EP_TD_1
, "M_EP_TD_1" },
247 { M_EP_TE_1
, "M_EP_TE_1" },
248 { M_EP_TF_1
, "M_EP_TF_1" },
249 { S_IT_TC_1
, "S_IT_TC_1" },
250 { C_SC_NA_1
, "C_SC_NA_1" },
251 { C_DC_NA_1
, "C_DC_NA_1" },
252 { C_RC_NA_1
, "C_RC_NA_1" },
253 { C_SE_NA_1
, "C_SE_NA_1" },
254 { C_SE_NB_1
, "C_SE_NB_1" },
255 { C_SE_NC_1
, "C_SE_NC_1" },
256 { C_BO_NA_1
, "C_BO_NA_1" },
257 { C_SC_TA_1
, "C_SC_TA_1" },
258 { C_DC_TA_1
, "C_DC_TA_1" },
259 { C_RC_TA_1
, "C_RC_TA_1" },
260 { C_SE_TA_1
, "C_SE_TA_1" },
261 { C_SE_TB_1
, "C_SE_TB_1" },
262 { C_SE_TC_1
, "C_SE_TC_1" },
263 { C_BO_TA_1
, "C_BO_TA_1" },
264 { M_EI_NA_1
, "M_EI_NA_1" },
265 { S_CH_NA_1
, "S_CH_NA_1" },
266 { S_RP_NA_1
, "S_RP_NA_1" },
267 { S_AR_NA_1
, "S_AR_NA_1" },
268 { S_KR_NA_1
, "S_KR_NA_1" },
269 { S_KS_NA_1
, "S_KS_NA_1" },
270 { S_KC_NA_1
, "S_KC_NA_1" },
271 { S_ER_NA_1
, "S_ER_NA_1" },
272 { S_US_NA_1
, "S_US_NA_1" },
273 { S_UQ_NA_1
, "S_UQ_NA_1" },
274 { S_UR_NA_1
, "S_UR_NA_1" },
275 { S_UK_NA_1
, "S_UK_NA_1" },
276 { S_UA_NA_1
, "S_UA_NA_1" },
277 { S_UC_NA_1
, "S_UC_NA_1" },
278 { C_IC_NA_1
, "C_IC_NA_1" },
279 { C_CI_NA_1
, "C_CI_NA_1" },
280 { C_RD_NA_1
, "C_RD_NA_1" },
281 { C_CS_NA_1
, "C_CS_NA_1" },
282 { C_RP_NA_1
, "C_RP_NA_1" },
283 { C_TS_TA_1
, "C_TS_TA_1" },
284 { P_ME_NA_1
, "P_ME_NA_1" },
285 { P_ME_NB_1
, "P_ME_NB_1" },
286 { P_ME_NC_1
, "P_ME_NC_1" },
287 { P_AC_NA_1
, "P_AC_NA_1" },
288 { F_FR_NA_1
, "F_FR_NA_1" },
289 { F_SR_NA_1
, "F_SR_NA_1" },
290 { F_SC_NA_1
, "F_SC_NA_1" },
291 { F_LS_NA_1
, "F_LS_NA_1" },
292 { F_AF_NA_1
, "F_AF_NA_1" },
293 { F_SG_NA_1
, "F_SG_NA_1" },
294 { F_DR_TA_1
, "F_DR_TA_1" },
295 { F_SC_NB_1
, "F_SC_NB_1" },
299 static const value_string asdu_lngtypes
[] = {
300 { M_SP_NA_1
, "single-point information" },
301 { M_SP_TA_1
, "single-point information with time tag" },
302 { M_DP_NA_1
, "double-point information" },
303 { M_DP_TA_1
, "double-point information with time tag" },
304 { M_ST_NA_1
, "step position information" },
305 { M_ST_TA_1
, "step position information with time tag" },
306 { M_BO_NA_1
, "bitstring of 32 bits" },
307 { M_BO_TA_1
, "bitstring of 32 bits with time tag" },
308 { M_ME_NA_1
, "measured value, normalized value" },
309 { M_ME_TA_1
, "measured value, normalized value with time tag" },
310 { M_ME_NB_1
, "measured value, scaled value" },
311 { M_ME_TB_1
, "measured value, scaled value with time tag" },
312 { M_ME_NC_1
, "measured value, short floating point number" },
313 { M_ME_TC_1
, "measured value, short floating point number with time tag" },
314 { M_IT_NA_1
, "integrated totals" },
315 { M_IT_TA_1
, "integrated totals with time tag" },
316 { M_PS_NA_1
, "packed single-point information with status change detection" },
317 { M_ME_ND_1
, "measured value, normalized value without quality descriptor" },
318 { M_SP_TB_1
, "single-point information with time tag CP56Time2a" },
319 { M_DP_TB_1
, "double-point information with time tag CP56Time2a" },
320 { M_ST_TB_1
, "step position information with time tag CP56Time2a" },
321 { M_BO_TB_1
, "bitstring of 32 bit with time tag CP56Time2a" },
322 { M_ME_TD_1
, "measured value, normalized value with time tag CP56Time2a" },
323 { M_ME_TE_1
, "measured value, scaled value with time tag CP56Time2a" },
324 { M_ME_TF_1
, "measured value, short floating point number with time tag CP56Time2a" },
325 { M_IT_TB_1
, "integrated totals with time tag CP56Time2a" },
326 { M_EP_TD_1
, "event of protection equipment with time tag CP56Time2a" },
327 { M_EP_TE_1
, "packed start events of protection equipment with time tag CP56Time2a" },
328 { M_EP_TF_1
, "packed output circuit information of protection equipment with time tag CP56Time2a" },
329 { S_IT_TC_1
, "integrated totals containing time tagged security statistics" },
330 { C_SC_NA_1
, "single command" },
331 { C_DC_NA_1
, "double command" },
332 { C_RC_NA_1
, "regulating step command" },
333 { C_SE_NA_1
, "set point command, normalized value" },
334 { C_SE_NB_1
, "set point command, scaled value" },
335 { C_SE_NC_1
, "set point command, short floating point number" },
336 { C_BO_NA_1
, "bitstring of 32 bits" },
337 { C_SC_TA_1
, "single command with time tag CP56Time2a" },
338 { C_DC_TA_1
, "double command with time tag CP56Time2a" },
339 { C_RC_TA_1
, "regulating step command with time tag CP56Time2a" },
340 { C_SE_TA_1
, "set point command, normalized value with time tag CP56Time2a" },
341 { C_SE_TB_1
, "set point command, scaled value with time tag CP56Time2a" },
342 { C_SE_TC_1
, "set point command, short floating-point number with time tag CP56Time2a" },
343 { C_BO_TA_1
, "bitstring of 32 bits with time tag CP56Time2a" },
344 { M_EI_NA_1
, "end of initialization" },
345 { S_CH_NA_1
, "authentication challenge" },
346 { S_RP_NA_1
, "authentication reply" },
347 { S_AR_NA_1
, "aggressive mode authentication request session key status request" },
348 { S_KR_NA_1
, "session key status request" },
349 { S_KS_NA_1
, "session key status" },
350 { S_KC_NA_1
, "session key change" },
351 { S_ER_NA_1
, "authentication error" },
352 { S_US_NA_1
, "user status change" },
353 { S_UQ_NA_1
, "update key change request" },
354 { S_UR_NA_1
, "update key change reply" },
355 { S_UK_NA_1
, "update key change symmetric" },
356 { S_UA_NA_1
, "update key change asymmetric" },
357 { S_UC_NA_1
, "update key change confirmation" },
358 { C_IC_NA_1
, "interrogation command" },
359 { C_CI_NA_1
, "counter interrogation command" },
360 { C_RD_NA_1
, "read command" },
361 { C_CS_NA_1
, "clock synchronization command" },
362 { C_RP_NA_1
, "reset process command" },
363 { C_TS_TA_1
, "test command with time tag CP56Time2a" },
364 { P_ME_NA_1
, "parameter of measured value, normalized value" },
365 { P_ME_NB_1
, "parameter of measured value, scaled value" },
366 { P_ME_NC_1
, "parameter of measured value, short floating-point number" },
367 { P_AC_NA_1
, "parameter activation" },
368 { F_FR_NA_1
, "file ready" },
369 { F_SR_NA_1
, "section ready" },
370 { F_SC_NA_1
, "call directory, select file, call file, call section" },
371 { F_LS_NA_1
, "last section, last segment" },
372 { F_AF_NA_1
, "ack file, ack section" },
373 { F_SG_NA_1
, "segment" },
374 { F_DR_TA_1
, "directory" },
375 { F_SC_NB_1
, "Query Log - Request archive file" },
384 static const td_asdu_length asdu_length
[] = {
464 /* Cause of Transmission (CauseTx) */
505 #define UkComAdrASDU 46
508 static const value_string causetx_types
[] = {
509 { Per_Cyc
,"Per/Cyc" },
515 { ActCon
,"ActCon" },
517 { DeactCon
,"DeactCon" },
518 { ActTerm
,"ActTerm" },
519 { Retrem
,"Retrem" },
520 { Retloc
,"Retloc" },
523 { Seskey
,"Seskey" },
524 { Usrkey
,"Usrkey" },
525 { Inrogen
,"Inrogen" },
535 { Inro10
,"Inro10" },
536 { Inro11
,"Inro11" },
537 { Inro12
,"Inro12" },
538 { Inro13
,"Inro13" },
539 { Inro14
,"Inro14" },
540 { Inro15
,"Inro15" },
541 { Inro16
,"Inro16" },
542 { Reqcogen
,"Reqcogen" },
543 { Reqco1
,"Reqco1" },
544 { Reqco2
,"Reqco2" },
545 { Reqco3
,"Reqco3" },
546 { Reqco4
,"Reqco4" },
547 { UkTypeId
,"UkTypeId" },
548 { UkCauseTx
,"UkCauseTx" },
549 { UkComAdrASDU
,"UkComAdrASDU" },
554 static const value_string diq_types
[] = {
555 { 0, "Indeterminate or Intermediate" },
558 { 3, "Indeterminate" },
562 static const value_string qos_qu_types
[] = {
563 { 0, "No pulse defined" },
564 { 1, "Short Pulse" },
566 { 3, "Persistent Output" },
570 static const value_string dco_on_types
[] = {
574 { 3, "Error: On/Off not defined" },
578 static const value_string rco_up_types
[] = {
582 { 3, "Error: Up/Down not defined" },
586 static const value_string qpm_kpa_types
[] = {
588 { 1, "Threshold value" },
589 { 2, "Smoothing factor (filter time constant)" },
593 static const value_string qpm_lpc_types
[] = {
599 static const value_string qpm_pop_types
[] = {
601 { 1, "Not in operation" },
605 static const value_string coi_r_types
[] = {
606 { 0, "Local power switch on" },
607 { 1, "Local manual reset" },
608 { 2, "Remote reset" },
612 static const value_string qoi_r_types
[] = {
613 { 0, "Not specified" },
614 { 20, "Station interrogation (global)" },
615 { 21, "Group 1 interrogation" },
616 { 22, "Group 2 interrogation" },
617 { 23, "Group 3 interrogation" },
618 { 24, "Group 4 interrogation" },
619 { 25, "Group 5 interrogation" },
620 { 26, "Group 6 interrogation" },
621 { 27, "Group 7 interrogation" },
622 { 28, "Group 8 interrogation" },
623 { 29, "Group 9 interrogation" },
624 { 30, "Group 10 interrogation" },
625 { 31, "Group 11 interrogation" },
626 { 32, "Group 12 interrogation" },
627 { 33, "Group 13 interrogation" },
628 { 34, "Group 14 interrogation" },
629 { 35, "Group 15 interrogation" },
630 { 36, "Group 16 interrogation" },
634 static const value_string rqt_r_types
[] = {
635 { 0, "Not specified" },
636 { 1, "Group 1 counter interrogation" },
637 { 2, "Group 2 counter interrogation" },
638 { 3, "Group 3 counter interrogation" },
639 { 4, "Group 4 counter interrogation" },
640 { 5, "General counter interrogation" },
644 static const value_string frz_r_types
[] = {
645 { 0, "Read only (no freeze or reset)" },
646 { 1, "Counter freeze without reset (value frozen represents integrated total)" },
647 { 2, "Counter freeze with reset (value frozen represents incremental information)" },
648 { 3, "Counter reset" },
652 static const value_string qrp_r_types
[] = {
654 { 1, "General reset of process" },
655 { 2, "Reset of pending information with time tag of the event buffer" },
659 static const range_string usr_types
[] = {
660 { 0, 0, "(Unknown)" },
662 { 2, 65535, "Chosen by the controlling station" },
666 static const range_string mal_types
[] = {
667 { 0, 0, "(not used)" },
668 { 1, 1, "HMAC SHA-1 truncated to 4 octets (serial)" },
669 { 2, 2, "HMAC SHA-1 truncated to 10 octets (networked)" },
670 { 3, 3, "HMAC-SHA-256 truncated to 8 octets (serial)" },
671 { 4, 4, "HMAC-SHA-256 truncated to 16 octets (networked)" },
672 { 128, 255, "(vendor-specific choice)" },
676 static const value_string rsc_types
[] = {
682 static const range_string kwa_types
[] = {
683 { 0, 0, "(not used)" },
684 { 1, 1, "AES-128 Key Wrap Algorithm" },
685 { 2, 2, "AES-256 Key Wrap Algorithm" },
686 { 128, 255, "(vendor-specific choice)" },
690 static const value_string kst_types
[] = {
699 static const range_string hal_types
[] = {
700 { 0, 0, "No MAC value in this message" },
701 { 1, 1, "HMAC SHA-1 truncated to 4 octets (serial)" },
702 { 2, 2, "HMAC SHA-1 truncated to 10 octets (networked)" },
703 { 3, 3, "HMAC-SHA-256 truncated to 8 octets (serial)" },
704 { 4, 4, "HMAC-SHA-256 truncated to 16 octets (networked)" },
705 { 128, 255, "(vendor-specific choice)" },
709 static const range_string error_codes
[] = {
710 { 0, 0, "(not used)" },
711 { 1, 1, "Authentication failed" },
712 { 2, 2, "Unexpected reply" },
713 { 3, 3, "No reply" },
714 { 4, 4, "Aggressive Mode not permitted" },
715 { 5, 5, "MAC algorithm not permitted" },
716 { 6, 6, "Key Wrap algorithm not permitted" },
717 { 7, 7, "Authorization failed" },
718 { 8, 8, "Update Key Change Method not permitted" },
719 { 9, 9, "Invalid Signature" },
720 { 10, 10, "Invalid Certification Data" },
721 { 11, 11, "Unknown User" },
722 { 128, 255, "(vendor-specific choice)"},
726 static const true_false_string tfs_blocked_not_blocked
= { "Blocked", "Not blocked" };
727 static const true_false_string tfs_substituted_not_substituted
= { "Substituted", "Not Substituted" };
728 static const true_false_string tfs_not_topical_topical
= { "Not Topical", "Topical" };
729 static const true_false_string tfs_transient_not_transient
= { "Transient", "Not Transient" };
730 static const true_false_string tfs_overflow_no_overflow
= { "Overflow", "No overflow" };
731 static const true_false_string tfs_select_execute
= { "Select", "Execute" };
732 static const true_false_string tfs_local_dst
= { "DST", "Local" };
733 static const true_false_string tfs_coi_i
= { "Initialisation after change of local parameters", "Initialisation with unchanged local parameters" };
734 static const true_false_string tfs_adjusted_not_adjusted
= { "Adjusted", "Not Adjusted" };
736 static unsigned global_iec60870_link_addr_len
= 1;
737 static unsigned global_iec60870_cot_len
= 1;
738 static unsigned global_iec60870_asdu_addr_len
= 1;
739 static unsigned global_iec60870_ioa_len
= 2;
741 /* Protocol fields to be filtered */
742 static int hf_apdulen
;
743 static int hf_apcitype_i
;
744 static int hf_apcitype_s_u
;
745 static int hf_apciutype
;
746 static int hf_apcitx
;
747 static int hf_apcirx
;
748 static int hf_apcidata
;
752 static int hf_typeid
;
753 static int hf_causetx
;
759 static int hf_cp24time
;
760 static int hf_cp24time_ms
;
761 static int hf_cp24time_min
;
762 static int hf_cp24time_iv
;
763 static int hf_cp56time
;
764 static int hf_cp56time_ms
;
765 static int hf_cp56time_min
;
766 static int hf_cp56time_gen
;
767 static int hf_cp56time_iv
;
768 static int hf_cp56time_hour
;
769 static int hf_cp56time_su
;
770 static int hf_cp56time_day
;
771 static int hf_cp56time_dow
;
772 static int hf_cp56time_month
;
773 static int hf_cp56time_year
;
775 static int hf_siq_spi
;
776 static int hf_siq_bl
;
777 static int hf_siq_sb
;
778 static int hf_siq_nt
;
779 static int hf_siq_iv
;
781 static int hf_diq_dpi
;
782 static int hf_diq_bl
;
783 static int hf_diq_sb
;
784 static int hf_diq_nt
;
785 static int hf_diq_iv
;
787 static int hf_qds_ov
;
788 static int hf_qds_bl
;
789 static int hf_qds_sb
;
790 static int hf_qds_nt
;
791 static int hf_qds_iv
;
796 static int hf_qos_ql
;
797 static int hf_qos_se
;
799 static int hf_sco_on
;
800 static int hf_sco_qu
;
801 static int hf_sco_se
;
803 static int hf_dco_on
;
804 static int hf_dco_qu
;
805 static int hf_dco_se
;
807 static int hf_rco_up
;
808 static int hf_rco_qu
;
809 static int hf_rco_se
;
811 static int hf_qpm_kpa
;
812 static int hf_qpm_lpc
;
813 static int hf_qpm_pop
;
816 static int hf_iec60870_segment_data
;
819 static int hf_asn_fin
;
820 static int hf_asn_fir
;
829 static int hf_prcd_raw_data
;
830 static int hf_hmac_raw_data
;
831 static int hf_wkd_raw_data
;
835 static int hf_etm_ms
;
836 static int hf_etm_min
;
837 static int hf_etm_iv
;
838 static int hf_etm_hour
;
839 static int hf_etm_su
;
840 static int hf_etm_day
;
841 static int hf_etm_dow
;
842 static int hf_etm_month
;
843 static int hf_etm_year
;
845 static int hf_error_text
;
851 static int hf_qcc_rqt
;
852 static int hf_qcc_frz
;
855 static int hf_bcr_count
;
856 static int hf_bcr_sq
;
857 static int hf_bcr_cy
;
858 static int hf_bcr_ca
;
859 static int hf_bcr_iv
;
862 static int hf_asdu_bitstring
;
863 static int hf_asdu_float
;
864 static int hf_asdu_normval
;
865 static int hf_asdu_scalval
;
866 static int hf_asdu_tsc
;
867 static int hf_asdu_raw_data
;
871 static int ett_asdu_objects
;
883 static int ett_cp24time
;
884 static int ett_cp56time
;
887 static int ett_iec60870_segment
;
888 static int ett_iec60870_segments
;
890 static expert_field ei_iec104_short_asdu
;
891 static expert_field ei_iec104_apdu_min_len
;
892 static expert_field ei_iec104_apdu_invalid_len
;
895 /* Initialize the protocol and registered fields */
896 static int hf_iec60870_101_frame
;
897 static int hf_iec60870_101_length
;
898 static int hf_iec60870_101_num_user_octets
;
899 static int hf_iec60870_101_ctrlfield
;
900 static int hf_iec60870_101_ctrl_prm
;
901 static int hf_iec60870_101_ctrl_fcb
;
902 static int hf_iec60870_101_ctrl_fcv
;
903 static int hf_iec60870_101_ctrl_dfc
;
904 static int hf_iec60870_101_ctrl_func_pri_to_sec
;
905 static int hf_iec60870_101_ctrl_func_sec_to_pri
;
906 static int hf_iec60870_101_linkaddr
;
907 static int hf_iec60870_101_checksum
;
908 static int hf_iec60870_101_stopchar
;
910 static int hf_iec60870_segments
;
911 static int hf_iec60870_segment
;
912 static int hf_iec60870_segment_overlap
;
913 static int hf_iec60870_segment_overlap_conflict
;
914 static int hf_iec60870_segment_multiple_tails
;
915 static int hf_iec60870_segment_too_long_segment
;
916 static int hf_iec60870_segment_error
;
917 static int hf_iec60870_segment_count
;
918 static int hf_iec60870_reassembled_in
;
919 static int hf_iec60870_reassembled_length
;
921 static const fragment_items iec60870_frag_items
= {
922 &ett_iec60870_segment
,
923 &ett_iec60870_segments
,
924 &hf_iec60870_segments
,
925 &hf_iec60870_segment
,
926 &hf_iec60870_segment_overlap
,
927 &hf_iec60870_segment_overlap_conflict
,
928 &hf_iec60870_segment_multiple_tails
,
929 &hf_iec60870_segment_too_long_segment
,
930 &hf_iec60870_segment_error
,
931 &hf_iec60870_segment_count
,
932 &hf_iec60870_reassembled_in
,
933 &hf_iec60870_reassembled_length
,
934 /* Reassembled data field */
939 /* Initialize the subtree pointers */
940 static int ett_iec60870_101
;
941 static int ett_iec60870_101_ctrlfield
;
943 static expert_field ei_iec101_frame_mismatch
;
944 static expert_field ei_iec101_length_mismatch
;
945 static expert_field ei_iec101_stopchar_invalid
;
948 #define IEC101_VAR_LEN 0x68
949 #define IEC101_FIXED_LEN 0x10
950 #define IEC101_SINGLE_CHAR 0xE5
951 #define IEC101_STOP_CHAR 0x16
953 static const value_string iec60870_101_frame_vals
[] = {
954 { IEC101_VAR_LEN
, "Variable Length" },
955 { IEC101_FIXED_LEN
, "Fixed Length" },
956 { IEC101_SINGLE_CHAR
, "Single Character" },
960 static const value_string iec60870_101_ctrl_prm_values
[] = {
961 { 0, "Message from Secondary (Responding) Station" },
962 { 1, "Message from Primary (Initiating) Station" },
966 static const value_string iec60870_101_ctrl_func_pri_to_sec_values
[] = {
967 { 0, "Reset of Remote Link" },
968 { 1, "Reset of User Process" },
969 { 2, "Reserved for Balanced Mode" },
975 { 8, "Expected Response Specifies Access Demand" },
976 { 9, "Request Status of Link" },
977 { 10, "Request User Data Class 1" },
978 { 11, "Request User Data Class 2" },
986 static const value_string iec60870_101_ctrl_func_sec_to_pri_values
[] = {
987 { 0, "ACK: Positive Acknowledgement" },
988 { 1, "NACK: Message Not Accepted, Link Busy" },
996 { 9, "NACK: Requested Data not Available" },
998 { 11, "Status of Link" },
1001 { 14, "Link Service not Functioning" },
1002 { 15, "Link Service not Implemented" },
1006 /* IEC 60870-5-103 Variables */
1007 /* Initialize the protocol and registered fields */
1008 static int hf_iec60870_5_103_areva_cmd
;
1009 static int hf_iec60870_5_103_asdu_address
;
1010 static int hf_iec60870_5_103_asdu_typeid_mon
;
1011 static int hf_iec60870_5_103_asdu_typeid_ctrl
;
1012 static int hf_iec60870_5_103_asdu205_ms
;
1013 static int hf_iec60870_5_103_asdu205_min
;
1014 static int hf_iec60870_5_103_asdu205_h
;
1015 static int hf_iec60870_5_103_asdu205_value
;
1016 static int hf_iec60870_5_103_checksum
;
1017 static int hf_iec60870_5_103_col
;
1018 static int hf_iec60870_5_103_cot_mon
;
1019 static int hf_iec60870_5_103_cot_ctrl
;
1020 static int hf_iec60870_5_103_cp32time2a
;
1021 static int hf_iec60870_5_103_cp32time2a_ms
;
1022 static int hf_iec60870_5_103_cp32time2a_min
;
1023 static int hf_iec60870_5_103_cp32time2a_res1
;
1024 static int hf_iec60870_5_103_cp32time2a_iv
;
1025 static int hf_iec60870_5_103_cp32time2a_hr
;
1026 static int hf_iec60870_5_103_cp32time2a_res2
;
1027 static int hf_iec60870_5_103_cp32time2a_sum
;
1028 static int hf_iec60870_5_103_ctrlfield
;
1029 static int hf_iec60870_5_103_ctrl_prm
;
1030 static int hf_iec60870_5_103_ctrl_fcb
;
1031 static int hf_iec60870_5_103_ctrl_fcv
;
1032 static int hf_iec60870_5_103_ctrl_dfc
;
1033 static int hf_iec60870_5_103_ctrl_func_pri_to_sec
;
1034 static int hf_iec60870_5_103_ctrl_func_sec_to_pri
;
1035 static int hf_iec60870_5_103_dco
;
1036 static int hf_iec60870_5_103_dpi
;
1037 static int hf_iec60870_5_103_frame
;
1038 static int hf_iec60870_5_103_func_type
;
1039 static int hf_iec60870_5_103_info_num
;
1040 static int hf_iec60870_5_103_length
;
1041 static int hf_iec60870_5_103_linkaddr
;
1042 static int hf_iec60870_5_103_mfg
;
1043 static int hf_iec60870_5_103_mfg_sw
;
1044 static int hf_iec60870_5_103_num_user_octets
;
1045 static int hf_iec60870_5_103_rii
;
1046 static int hf_iec60870_5_103_scn
;
1047 static int hf_iec60870_5_103_sin
;
1048 static int hf_iec60870_5_103_sq
;
1049 static int hf_iec60870_5_103_stopchar
;
1051 /* Initialize the subtree pointers */
1052 static int ett_iec60870_5_103
;
1053 static int ett_iec60870_5_103_ctrlfield
;
1054 static int ett_iec60870_5_103_cp32time2a
;
1057 #define IEC103_VAR_LEN 0x68
1058 #define IEC103_FIXED_LEN 0x10
1059 #define IEC103_SINGLE_CHAR 0xE5
1062 static const value_string iec60870_5_103_frame_vals
[] = {
1063 { IEC103_VAR_LEN
, "Variable Length" },
1064 { IEC103_FIXED_LEN
, "Fixed Length" },
1065 { IEC103_SINGLE_CHAR
, "Single Character" },
1069 static const value_string iec60870_5_103_ctrl_prm_values
[] = {
1070 { 0, "Message from Secondary (Responding) Station" },
1071 { 1, "Message from Primary (Initiating) Station" },
1075 static const value_string iec60870_5_103_ctrl_func_pri_to_sec_values
[] = {
1076 { 0, "Reset of Communications Unit" },
1079 { 3, "Send / Confirm Expected" },
1080 { 4, "Send / No Confirm Expected" },
1083 { 7, "Reset Frame Count Bit" },
1085 { 9, "Request Status of Link" },
1086 { 10, "Request User Data Class 1" },
1087 { 11, "Request User Data Class 2" },
1095 static const value_string iec60870_5_103_ctrl_func_sec_to_pri_values
[] = {
1096 { 0, "ACK: Positive Acknowledgement" },
1097 { 1, "NACK: Message Not Accepted, Link Busy" },
1104 { 8, "ACK: User Data" },
1105 { 9, "NACK: Requested Data not Available" },
1107 { 11, "Status of Link" },
1110 { 14, "Link Service not Functioning" },
1111 { 15, "Link Service not Implemented" },
1115 /* IEC 60870-5-103 ASDU types (TypeId); monitor direction */
1116 static const value_string iec103_asdu_types_monitor_dir
[] = {
1117 { 1, "Time tagged message" }, /* dissection implemented */
1118 { 2, "Time tagged message with relative time" },
1119 { 3, "Measurands I" },
1120 { 4, "Time tagged measurands with relative time" },
1121 { 5, "Identification" }, /* dissection implemented */
1122 { 6, "Time synchronization" }, /* dissection implemented */
1123 { 8, "General interrogation termination" }, /* dissection implemented */
1124 { 9, "Measurands II" }, /* dissection implemented */
1125 { 10, "Generic data" },
1126 { 11, "Generic identification" },
1138 { 23, "List of recorded disturbances" },
1141 { 26, "Ready for transmission of disturbance data" },
1142 { 27, "Ready for transmission of a channel" },
1143 { 28, "Ready for transmission of tags" },
1144 { 29, "Transmission of tags" },
1145 { 30, "Transmission of disturbance values" },
1146 { 31, "End of transmission" },
1147 { 205, "Private, Siemens energy counters"}, /* dissection implemented */
1151 /* IEC 60870-5-103 ASDU types (TypeId); control direction */
1152 static const value_string iec103_asdu_types_control_dir
[] = {
1158 { 6, "Time synchronization" }, /* dissection implemented */
1159 { 7, "General interrogation" }, /* dissection implemented */
1162 { 10, "Generic data" },
1172 { 20, "General command" }, /* dissection implemented */
1173 { 21, "Generic command" },
1176 { 24, "Order for disturbance data transmission" },
1177 { 25, "Acknowledgement for disturbance data transmission" },
1184 { 45, "Private, Areva Single Command" }, /* dissection implemented */
1185 { 46, "Private, Areva Double Command" }, /* dissection implemented */
1189 static const value_string iec60870_5_103_cot_monitor_dir
[] = {
1190 { 1, "Spontaneous" },
1192 { 3, "Reset frame count bit (FCB)" },
1193 { 4, "Reset communication unit (CU)" },
1194 { 5, "Start / restart" },
1197 { 8, "Time synchronization" },
1198 { 9, "General interrogation" },
1199 { 10, "Termination of general interrogation" },
1200 { 11, "Local operation" },
1201 { 12, "Remote operation" },
1202 { 20, "Positive acknowledgement of command" },
1203 { 21, "Negative acknowledgement of command" },
1204 { 31, "Transmission of disturbance data" },
1205 { 40, "Positive acknowledgement of generic write command" },
1206 { 41, "Negative acknowledgement of generic write command" },
1207 { 42, "Valid data response to generic read command" },
1208 { 43, "Invalid data response to generic read command" },
1209 { 44, "Generic write confirmation" },
1213 static const value_string iec60870_5_103_cot_ctrl_dir
[] = {
1214 { 8, "Time synchronization" },
1215 { 9, "Initiation of general interrogation" },
1216 { 20, "General command" },
1217 { 31, "Transmission of disturbance data" },
1218 { 40, "Generic write command" },
1219 { 42, "Generic read command" },
1224 static const value_string iec103_quadstate_types
[] = {
1232 /* Misc. functions for dissection of signal values */
1234 /* ====================================================================
1235 Dissects the CP24Time2a time (Three octet binary time)
1236 that starts 'offset' bytes in 'tvb'.
1237 ==================================================================== */
1238 static void get_CP24Time(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1244 proto_tree
* cp24time_tree
;
1246 ms
= tvb_get_letohs(tvb
, *offset
);
1247 nstime
.nsecs
= (ms
% 1000) * 1000000;
1248 nstime
.secs
= ms
/ 1000;
1251 min
= tvb_get_uint8(tvb
, *offset
);
1252 nstime
.secs
+= (min
& 0x3F) * 60;
1257 ti
= proto_tree_add_time(iec104_header_tree
, hf_cp24time
, tvb
, *offset
, 3, &nstime
);
1258 cp24time_tree
= proto_item_add_subtree(ti
, ett_cp24time
);
1260 proto_tree_add_item(cp24time_tree
, hf_cp24time_ms
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1263 proto_tree_add_item(cp24time_tree
, hf_cp24time_min
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1264 proto_tree_add_item(cp24time_tree
, hf_cp24time_iv
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1268 /* ====================================================================
1269 Dissect a CP32Time2a (four octet binary time), add to proto tree
1270 ==================================================================== */
1271 static void get_CP32TimeA(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*tree
)
1278 proto_tree
* cp32time2a_tree
;
1280 ms
= tvb_get_letohs(tvb
, *offset
);
1281 tm
.tm_sec
= ms
/ 1000;
1282 datetime
.nsecs
= (ms
% 1000) * 1000000;
1284 value
= tvb_get_uint8(tvb
, *offset
+2);
1285 tm
.tm_min
= value
& 0x3F;
1287 value
= tvb_get_uint8(tvb
, *offset
+3);
1288 tm
.tm_hour
= value
& 0x1F;
1290 /* The CP32Time2a structure does not contain any mm/dd/yyyy information. Set these as default to 1/1/2000 */
1295 datetime
.secs
= mktime(&tm
);
1297 ti
= proto_tree_add_time(tree
, hf_iec60870_5_103_cp32time2a
, tvb
, *offset
, 4, &datetime
);
1298 cp32time2a_tree
= proto_item_add_subtree(ti
, ett_iec60870_5_103_cp32time2a
);
1300 proto_tree_add_item(cp32time2a_tree
, hf_iec60870_5_103_cp32time2a_ms
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1301 proto_tree_add_item(cp32time2a_tree
, hf_iec60870_5_103_cp32time2a_min
, tvb
, *offset
+2, 1, ENC_LITTLE_ENDIAN
);
1302 proto_tree_add_item(cp32time2a_tree
, hf_iec60870_5_103_cp32time2a_res1
, tvb
, *offset
+2, 1, ENC_LITTLE_ENDIAN
);
1303 proto_tree_add_item(cp32time2a_tree
, hf_iec60870_5_103_cp32time2a_iv
, tvb
, *offset
+2, 1, ENC_LITTLE_ENDIAN
);
1304 proto_tree_add_item(cp32time2a_tree
, hf_iec60870_5_103_cp32time2a_hr
, tvb
, *offset
+3, 1, ENC_LITTLE_ENDIAN
);
1305 proto_tree_add_item(cp32time2a_tree
, hf_iec60870_5_103_cp32time2a_res2
, tvb
, *offset
+3, 1, ENC_LITTLE_ENDIAN
);
1306 proto_tree_add_item(cp32time2a_tree
, hf_iec60870_5_103_cp32time2a_sum
, tvb
, *offset
+3, 1, ENC_LITTLE_ENDIAN
);
1311 /* ====================================================================
1312 Dissects the CP56Time2a time (Seven octet binary time)
1313 that starts 'offset' bytes in 'tvb'.
1314 ==================================================================== */
1315 static void get_CP56Time(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1323 proto_tree
* cp56time_tree
;
1325 ms
= tvb_get_letohs(tvb
, *offset
);
1326 tm
.tm_sec
= ms
/ 1000;
1327 datetime
.nsecs
= (ms
% 1000) * 1000000;
1330 value
= tvb_get_uint8(tvb
, *offset
);
1331 tm
.tm_min
= value
& 0x3F;
1334 value
= tvb_get_uint8(tvb
, *offset
);
1335 tm
.tm_hour
= value
& 0x1F;
1339 value
= tvb_get_uint8(tvb
, *offset
);
1340 tm
.tm_mday
= value
& 0x1F;
1343 value
= tvb_get_uint8(tvb
, *offset
);
1344 tm
.tm_mon
= (value
& 0x0F) - 1;
1347 value
= tvb_get_uint8(tvb
, *offset
);
1348 tm
.tm_year
= value
& 0x7F;
1349 if (tm
.tm_year
< 70)
1357 tm
.tm_isdst
= -1; /* there's no info on whether DST was in force; assume it's
1358 * the same as currently */
1360 datetime
.secs
= mktime(&tm
);
1364 ti
= proto_tree_add_time(iec104_header_tree
, hf_cp56time
, tvb
, *offset
, 7, &datetime
);
1365 cp56time_tree
= proto_item_add_subtree(ti
, ett_cp56time
);
1367 proto_tree_add_item(cp56time_tree
, hf_cp56time_ms
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1370 proto_tree_add_item(cp56time_tree
, hf_cp56time_min
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1371 proto_tree_add_item(cp56time_tree
, hf_cp56time_gen
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1372 proto_tree_add_item(cp56time_tree
, hf_cp56time_iv
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1375 proto_tree_add_item(cp56time_tree
, hf_cp56time_hour
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1376 proto_tree_add_item(cp56time_tree
, hf_cp56time_su
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1379 proto_tree_add_item(cp56time_tree
, hf_cp56time_day
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1380 proto_tree_add_item(cp56time_tree
, hf_cp56time_dow
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1383 proto_tree_add_item(cp56time_tree
, hf_cp56time_month
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1386 proto_tree_add_item(cp56time_tree
, hf_cp56time_year
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1390 /* ====================================================================
1391 Information object address (Identifier)
1392 ASDU -> Inform Object #1 -> Information object address
1393 ==================================================================== */
1394 static proto_item
* get_InfoObjectAddress(uint32_t *asdu_info_obj_addr
, tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
, unsigned ioa_len
)
1396 proto_item
* ti
= NULL
;
1398 /* Information object address */
1399 /* Support both 16 and 24-bit IOA addresses */
1400 ti
= proto_tree_add_item_ret_uint(iec104_header_tree
, hf_ioa
, tvb
, *offset
, ioa_len
, ENC_LITTLE_ENDIAN
, asdu_info_obj_addr
);
1401 (*offset
) += ioa_len
;
1406 /* ====================================================================
1408 ==================================================================== */
1409 static uint8_t get_TypeIdLength(uint8_t TypeId
)
1412 const td_asdu_length
*item
;
1417 if (item
->value
== TypeId
)
1428 /* ====================================================================
1429 SIQ: Single-point information (IEV 371-02-07) w quality descriptor
1430 ==================================================================== */
1431 static void get_SIQ(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1434 proto_tree
* siq_tree
;
1436 ti
= proto_tree_add_item(iec104_header_tree
, hf_siq
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1437 siq_tree
= proto_item_add_subtree(ti
, ett_siq
);
1439 proto_tree_add_item(siq_tree
, hf_siq_spi
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1440 proto_tree_add_item(siq_tree
, hf_siq_bl
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1441 proto_tree_add_item(siq_tree
, hf_siq_sb
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1442 proto_tree_add_item(siq_tree
, hf_siq_nt
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1443 proto_tree_add_item(siq_tree
, hf_siq_iv
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1448 /* ====================================================================
1449 DIQ: Double-point information (IEV 371-02-08) w quality descriptor
1450 ==================================================================== */
1451 static void get_DIQ(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1454 proto_tree
* diq_tree
;
1456 ti
= proto_tree_add_item(iec104_header_tree
, hf_diq
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1457 diq_tree
= proto_item_add_subtree(ti
, ett_diq
);
1459 proto_tree_add_item(diq_tree
, hf_diq_dpi
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1460 proto_tree_add_item(diq_tree
, hf_diq_bl
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1461 proto_tree_add_item(diq_tree
, hf_diq_sb
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1462 proto_tree_add_item(diq_tree
, hf_diq_nt
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1463 proto_tree_add_item(diq_tree
, hf_diq_iv
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1468 /* ====================================================================
1469 QDS: Quality descriptor (separate octet)
1470 ==================================================================== */
1471 static void get_QDS(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1474 proto_tree
* qds_tree
;
1476 ti
= proto_tree_add_item(iec104_header_tree
, hf_qds
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1477 qds_tree
= proto_item_add_subtree(ti
, ett_qds
);
1479 proto_tree_add_item(qds_tree
, hf_qds_ov
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1480 proto_tree_add_item(qds_tree
, hf_qds_bl
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1481 proto_tree_add_item(qds_tree
, hf_qds_sb
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1482 proto_tree_add_item(qds_tree
, hf_qds_nt
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1483 proto_tree_add_item(qds_tree
, hf_qds_iv
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1488 /* ====================================================================
1489 QDP: Quality descriptor for events of protection equipment
1491 ==================================================================== */
1493 static void get_QDP(tvbuff_t
*tvb _U_
, uint8_t *offset _U_
, proto_tree
*iec104_header_tree _U_
)
1500 /* ====================================================================
1501 VTI: Value with transient state indication
1502 ==================================================================== */
1503 static void get_VTI(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1506 proto_tree
* vti_tree
;
1508 ti
= proto_tree_add_item(iec104_header_tree
, hf_vti
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1509 vti_tree
= proto_item_add_subtree(ti
, ett_vti
);
1511 proto_tree_add_item(vti_tree
, hf_vti_v
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1512 proto_tree_add_item(vti_tree
, hf_vti_t
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1517 /* ====================================================================
1518 NVA: Normalized value
1519 ==================================================================== */
1520 static void get_NVA(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1525 value
= tvb_get_letohis(tvb
, *offset
);
1526 fvalue
= (float)value
/ 32768;
1528 /* Normalized value F16[1..16]<-1..+1-2^-15> */
1529 proto_tree_add_float_format_value(iec104_header_tree
, hf_asdu_normval
, tvb
, *offset
, 2, fvalue
, "%." G_STRINGIFY(FLT_DIG
) "g (%d)", fvalue
, value
);
1534 static void get_NVAspt(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1539 value
= tvb_get_letohis(tvb
, *offset
);
1540 fvalue
= (float)value
/ 32768;
1542 /* Normalized value F16[1..16]<-1..+1-2^-15> */
1543 proto_tree_add_float_format_value(iec104_header_tree
, hf_asdu_normval
, tvb
, *offset
, 2, fvalue
, "%." G_STRINGIFY(FLT_DIG
) "g (%d)", fvalue
, value
);
1548 /* ====================================================================
1550 ==================================================================== */
1551 static void get_SVA(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1553 /* Scaled value I16[1..16]<-2^15..+2^15-1> */
1554 proto_tree_add_item(iec104_header_tree
, hf_asdu_scalval
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1559 static void get_SVAspt(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1561 /* Scaled value I16[1..16]<-2^15..+2^15-1> */
1562 proto_tree_add_item(iec104_header_tree
, hf_asdu_scalval
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1567 /* ====================================================================
1568 TSC: Test sequence counter
1569 ==================================================================== */
1570 static void get_TSC(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1572 proto_tree_add_item(iec104_header_tree
, hf_asdu_tsc
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1577 /* ====================================================================
1578 "FLT": Short floating point number
1579 ==================================================================== */
1580 static void get_FLT(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1582 /* -------- IEEE 754 float value */
1583 proto_tree_add_item(iec104_header_tree
, hf_asdu_float
, tvb
, *offset
, 4, ENC_LITTLE_ENDIAN
);
1588 static void get_FLTspt(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1590 /* -------- IEEE 754 float value */
1591 proto_tree_add_item(iec104_header_tree
, hf_asdu_float
, tvb
, *offset
, 4, ENC_LITTLE_ENDIAN
);
1596 /* ====================================================================
1597 "BSI": Binary state information, 32 bit
1598 ==================================================================== */
1599 static void get_BSI(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1601 proto_tree_add_bits_item(iec104_header_tree
, hf_asdu_bitstring
, tvb
, *offset
*8, 32, ENC_BIG_ENDIAN
);
1606 static void get_BSIspt(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1608 proto_tree_add_bits_item(iec104_header_tree
, hf_asdu_bitstring
, tvb
, *offset
*8, 32, ENC_BIG_ENDIAN
);
1613 /* ====================================================================
1614 BCR: Binary counter reading
1615 ==================================================================== */
1616 static void get_BCR(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1619 proto_tree
* bcr_tree
;
1621 ti
= proto_tree_add_item(iec104_header_tree
, hf_bcr
, tvb
, *offset
, 4, ENC_LITTLE_ENDIAN
);
1622 bcr_tree
= proto_item_add_subtree(ti
, ett_vti
);
1624 proto_tree_add_item(bcr_tree
, hf_bcr_count
, tvb
, *offset
, 4, ENC_LITTLE_ENDIAN
);
1627 proto_tree_add_item(bcr_tree
, hf_bcr_sq
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1628 proto_tree_add_item(bcr_tree
, hf_bcr_cy
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1629 proto_tree_add_item(bcr_tree
, hf_bcr_ca
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1630 proto_tree_add_item(bcr_tree
, hf_bcr_iv
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1634 /* ====================================================================
1635 todo -- SEP: Single event of protection equipment
1636 ==================================================================== */
1638 static void get_SEP(tvbuff_t
*tvb _U_
, uint8_t *offset _U_
, proto_tree
*iec104_header_tree _U_
)
1645 /* ====================================================================
1646 QOS: Qualifier Of Set-point command
1647 ==================================================================== */
1648 static void get_QOS(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1651 proto_tree
* qos_tree
;
1653 ti
= proto_tree_add_item(iec104_header_tree
, hf_qos
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1654 qos_tree
= proto_item_add_subtree(ti
, ett_qos
);
1656 proto_tree_add_item(qos_tree
, hf_qos_ql
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1657 proto_tree_add_item(qos_tree
, hf_qos_se
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1662 /* ====================================================================
1663 SCO: Single Command (IEV 371-03-02)
1664 ==================================================================== */
1665 static void get_SCO(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1668 proto_tree
* sco_tree
;
1670 ti
= proto_tree_add_item(iec104_header_tree
, hf_sco
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1671 sco_tree
= proto_item_add_subtree(ti
, ett_sco
);
1673 proto_tree_add_item(sco_tree
, hf_sco_on
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1674 proto_tree_add_item(sco_tree
, hf_sco_qu
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1675 proto_tree_add_item(sco_tree
, hf_sco_se
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1680 /* ====================================================================
1681 DCO: Double Command (IEV 371-03-03)
1682 ==================================================================== */
1683 static void get_DCO(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1686 proto_tree
* dco_tree
;
1688 ti
= proto_tree_add_item(iec104_header_tree
, hf_dco
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1689 dco_tree
= proto_item_add_subtree(ti
, ett_dco
);
1691 proto_tree_add_item(dco_tree
, hf_dco_on
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1692 proto_tree_add_item(dco_tree
, hf_dco_qu
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1693 proto_tree_add_item(dco_tree
, hf_dco_se
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1698 /* ====================================================================
1699 RCO: Regulating step command (IEV 371-03-13)
1700 ==================================================================== */
1701 static void get_RCO(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1704 proto_tree
* rco_tree
;
1706 ti
= proto_tree_add_item(iec104_header_tree
, hf_rco
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1707 rco_tree
= proto_item_add_subtree(ti
, ett_rco
);
1709 proto_tree_add_item(rco_tree
, hf_rco_up
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1710 proto_tree_add_item(rco_tree
, hf_rco_qu
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1711 proto_tree_add_item(rco_tree
, hf_rco_se
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1716 /* ====================================================================
1717 QPM: Qualifier of parameter of measured value
1718 ==================================================================== */
1719 static void get_QPM(tvbuff_t
* tvb
, uint8_t* offset
, proto_tree
* iec104_header_tree
)
1722 proto_tree
* qpm_tree
;
1724 ti
= proto_tree_add_item(iec104_header_tree
, hf_qpm
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1725 qpm_tree
= proto_item_add_subtree(ti
, ett_qpm
);
1727 proto_tree_add_item(qpm_tree
, hf_qpm_kpa
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1728 proto_tree_add_item(qpm_tree
, hf_qpm_lpc
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1729 proto_tree_add_item(qpm_tree
, hf_qpm_pop
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1734 /* ====================================================================
1736 ==================================================================== */
1737 static void get_USR(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1739 proto_tree_add_item(iec104_header_tree
, hf_usr
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1744 /* ====================================================================
1746 ==================================================================== */
1747 static void get_MAL(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1749 proto_tree_add_item(iec104_header_tree
, hf_mal
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1754 /* ====================================================================
1755 RSC: Reason for challenge
1756 ==================================================================== */
1757 static void get_RSC(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1759 proto_tree_add_item(iec104_header_tree
, hf_rsc
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1764 /* ====================================================================
1765 CSQ: Challenge sequence number
1766 ==================================================================== */
1767 static void get_CSQ(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1769 proto_tree_add_item(iec104_header_tree
, hf_csq
, tvb
, *offset
, 4, ENC_LITTLE_ENDIAN
);
1774 /* ====================================================================
1775 KSQ: Key change sequence number
1776 ==================================================================== */
1777 static void get_KSQ(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1779 proto_tree_add_item(iec104_header_tree
, hf_ksq
, tvb
, *offset
, 4, ENC_LITTLE_ENDIAN
);
1784 /* ====================================================================
1785 KWA: Key wrap algorithm
1786 ==================================================================== */
1787 static void get_KWA(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1789 proto_tree_add_item(iec104_header_tree
, hf_kwa
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1794 /* ====================================================================
1796 ==================================================================== */
1797 static void get_KST(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1799 proto_tree_add_item(iec104_header_tree
, hf_kst
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1804 /* ====================================================================
1806 ==================================================================== */
1807 static uint16_t get_HLN(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1809 uint16_t value
= tvb_get_letohs(tvb
, *offset
);
1811 proto_tree_add_item(iec104_header_tree
, hf_hln
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1817 /* ====================================================================
1819 ==================================================================== */
1820 static uint8_t get_HAL(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1822 uint8_t hal
= tvb_get_uint8(tvb
, *offset
);
1823 proto_tree_add_item(iec104_header_tree
, hf_hal
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1828 case 3: /* HMAC-SHA-256 truncated to 8 octets (serial) */
1830 case 4: /* HMAC-SHA-256 truncated to 16 octets (networked) */
1832 case 6: /* AES-GMAC (output is 12 octets) */
1838 /* ====================================================================
1839 CLN: Challenge data length
1840 ==================================================================== */
1841 static uint16_t get_CLN(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1843 uint16_t value
= tvb_get_letohs(tvb
, *offset
);
1845 proto_tree_add_item(iec104_header_tree
, hf_cln
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1851 /* ====================================================================
1852 WKL: Wrapped key data length
1853 ==================================================================== */
1854 static uint16_t get_WKL(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1856 uint16_t value
= tvb_get_letohs(tvb
, *offset
);
1858 proto_tree_add_item(iec104_header_tree
, hf_wkl
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1864 /* ====================================================================
1865 Pseudo-random challenge data
1866 ==================================================================== */
1867 static void get_PRCD(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
, int length
)
1869 proto_tree_add_item(iec104_header_tree
, hf_prcd_raw_data
, tvb
, *offset
, length
, ENC_NA
);
1870 (*offset
) += length
;
1873 /* ====================================================================
1875 ==================================================================== */
1876 static void get_HMAC(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
, int length
)
1880 proto_tree_add_item(iec104_header_tree
, hf_hmac_raw_data
, tvb
, *offset
, length
, ENC_NA
);
1881 (*offset
) += length
;
1885 /* ====================================================================
1887 ==================================================================== */
1888 static void get_WKD(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
, int length
)
1890 proto_tree_add_item(iec104_header_tree
, hf_wkd_raw_data
, tvb
, *offset
, length
, ENC_NA
);
1891 (*offset
) += length
;
1894 /* ====================================================================
1896 ==================================================================== */
1897 static void get_AID(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1899 proto_tree_add_item(iec104_header_tree
, hf_aid
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1904 /* ====================================================================
1906 ==================================================================== */
1907 static void get_ERR(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1909 proto_tree_add_item(iec104_header_tree
, hf_err
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1914 /* ====================================================================
1915 ETM: Error time stamp (7-octet binary time)
1916 ==================================================================== */
1917 static void get_ETM(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1925 proto_tree
* etm_tree
;
1927 ms
= tvb_get_letohs(tvb
, *offset
);
1928 tm
.tm_sec
= ms
/ 1000;
1929 datetime
.nsecs
= (ms
% 1000) * 1000000;
1932 value
= tvb_get_uint8(tvb
, *offset
);
1933 tm
.tm_min
= value
& 0x3F;
1936 value
= tvb_get_uint8(tvb
, *offset
);
1937 tm
.tm_hour
= value
& 0x1F;
1941 value
= tvb_get_uint8(tvb
, *offset
);
1942 tm
.tm_mday
= value
& 0x1F;
1945 value
= tvb_get_uint8(tvb
, *offset
);
1946 tm
.tm_mon
= (value
& 0x0F) - 1;
1949 value
= tvb_get_uint8(tvb
, *offset
);
1950 tm
.tm_year
= value
& 0x7F;
1951 if (tm
.tm_year
< 70)
1959 tm
.tm_isdst
= -1; /* there's no info on whether DST was in force; assume it's
1960 * the same as currently */
1962 datetime
.secs
= mktime(&tm
);
1966 ti
= proto_tree_add_time(iec104_header_tree
, hf_etm
, tvb
, *offset
, 7, &datetime
);
1967 etm_tree
= proto_item_add_subtree(ti
, ett_etm
);
1969 proto_tree_add_item(etm_tree
, hf_etm_ms
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
1972 proto_tree_add_item(etm_tree
, hf_etm_min
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1973 proto_tree_add_item(etm_tree
, hf_etm_iv
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1976 proto_tree_add_item(etm_tree
, hf_etm_hour
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1977 proto_tree_add_item(etm_tree
, hf_etm_su
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1980 proto_tree_add_item(etm_tree
, hf_etm_day
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1981 proto_tree_add_item(etm_tree
, hf_etm_dow
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1984 proto_tree_add_item(etm_tree
, hf_etm_month
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1987 proto_tree_add_item(etm_tree
, hf_etm_year
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
1991 /* ====================================================================
1993 ==================================================================== */
1994 static uint16_t get_ELN(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
1996 uint16_t value
= tvb_get_letohs(tvb
, *offset
);
1997 proto_tree_add_item(iec104_header_tree
, hf_eln
, tvb
, *offset
, 2, ENC_LITTLE_ENDIAN
);
2003 /* ====================================================================
2005 ==================================================================== */
2006 static void get_ErrorText(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
, int length
)
2010 proto_tree_add_item(iec104_header_tree
, hf_error_text
, tvb
, *offset
, length
, ENC_UTF_8
| ENC_NA
);
2011 (*offset
) += length
;
2015 /* ====================================================================
2016 COI: Cause of initialisation
2017 ==================================================================== */
2018 static void get_COI(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
2021 proto_tree
* coi_tree
;
2023 ti
= proto_tree_add_item(iec104_header_tree
, hf_coi
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2024 coi_tree
= proto_item_add_subtree(ti
, ett_coi
);
2026 proto_tree_add_item(coi_tree
, hf_coi_r
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2027 proto_tree_add_item(coi_tree
, hf_coi_i
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2032 /* ====================================================================
2033 QOI: Qualifier of interrogation
2034 ==================================================================== */
2035 static void get_QOI(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
2037 proto_tree_add_item(iec104_header_tree
, hf_qoi
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2042 /* ====================================================================
2043 QCC: Qualifier of counter interrogation
2044 ==================================================================== */
2045 static void get_QCC(tvbuff_t
*tvb
, uint8_t *offset
, proto_tree
*iec104_header_tree
)
2048 proto_tree
* qcc_tree
;
2050 ti
= proto_tree_add_item(iec104_header_tree
, hf_qcc
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2051 qcc_tree
= proto_item_add_subtree(ti
, ett_qcc
);
2053 proto_tree_add_item(qcc_tree
, hf_qcc_rqt
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2054 proto_tree_add_item(qcc_tree
, hf_qcc_frz
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2059 /* ====================================================================
2060 QRP: Qualifier of reset process command
2061 ==================================================================== */
2062 static void get_QRP(tvbuff_t
* tvb
, uint8_t* offset
, proto_tree
* iec104_header_tree
)
2064 proto_tree_add_item(iec104_header_tree
, hf_qrp
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2068 /* .... end Misc. functions for dissection of signal values */
2071 /* Find the IEC60870-5-104 APDU (APDU=APCI+ASDU) length.
2072 Includes possible tvb_length-1 bytes that don't form an APDU */
2073 static unsigned get_iec104apdu_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
,
2074 int offset
, void *data _U_
)
2079 for (Off
= 0; Off
<= tvb_reported_length(tvb
) - 2; Off
++) {
2080 Val
= tvb_get_uint8(tvb
, offset
+ Off
);
2081 if (Val
== APCI_START
) {
2082 return (unsigned)(Off
+ tvb_get_uint8(tvb
, offset
+ Off
+ 1) + 2);
2086 return (unsigned)(tvb_reported_length(tvb
));
2089 /* Dissect reassembled extended IEC60870-5-7 secure authentication ASDUs */
2090 // NOLINTNEXTLINE(misc-no-recursion)
2091 static int dissect_iec60870_asdu_segment(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*it_segment_tree
, uint8_t type_id
, struct asdu_parms
* parms
)
2093 unsigned Len
= tvb_reported_length(tvb
);
2095 uint8_t i
, encapsulated_type
, encapsulated_length
;
2097 tvbuff_t
*encapsulated_tvb
= NULL
;
2100 case S_CH_NA_1
: /* 81 authentication challenge */
2101 get_CSQ(tvb
, &offset
, it_segment_tree
);
2102 get_USR(tvb
, &offset
, it_segment_tree
);
2103 get_MAL(tvb
, &offset
, it_segment_tree
);
2104 get_RSC(tvb
, &offset
, it_segment_tree
);
2105 j
= get_CLN(tvb
, &offset
, it_segment_tree
);
2106 get_PRCD(tvb
, &offset
, it_segment_tree
, j
);
2108 case S_RP_NA_1
: /* 82 authentication reply */
2109 get_CSQ(tvb
, &offset
, it_segment_tree
);
2110 get_USR(tvb
, &offset
, it_segment_tree
);
2111 j
= get_HLN(tvb
, &offset
, it_segment_tree
);
2112 get_HMAC(tvb
, &offset
, it_segment_tree
, j
);
2114 case S_AR_NA_1
: /* 83 Aggressive mode authentication request */
2115 encapsulated_type
= tvb_get_uint8(tvb
, offset
);
2116 encapsulated_length
= 1 + 1 + parms
->cot_len
+ parms
->asdu_addr_len
+ parms
->ioa_len
+ get_TypeIdLength(encapsulated_type
);
2117 encapsulated_tvb
= tvb_new_subset_length_caplen(tvb
, offset
, -1, encapsulated_length
);
2118 dissect_iec60870_asdu(encapsulated_tvb
, pinfo
, it_segment_tree
, parms
);
2119 offset
= tvb_reported_length(encapsulated_tvb
);
2120 get_CSQ(tvb
, &offset
, it_segment_tree
);
2121 get_USR(tvb
, &offset
, it_segment_tree
);
2122 get_HMAC(tvb
, &offset
, it_segment_tree
, tvb_reported_length_remaining(tvb
, offset
));
2124 case S_KS_NA_1
: /* 85 session key status */
2125 get_KSQ(tvb
, &offset
, it_segment_tree
);
2126 get_USR(tvb
, &offset
, it_segment_tree
);
2127 get_KWA(tvb
, &offset
, it_segment_tree
);
2128 get_KST(tvb
, &offset
, it_segment_tree
);
2129 i
= get_HAL(tvb
, &offset
, it_segment_tree
);
2130 j
= get_CLN(tvb
, &offset
, it_segment_tree
);
2131 get_PRCD(tvb
, &offset
, it_segment_tree
, j
);
2132 get_HMAC(tvb
, &offset
, it_segment_tree
, i
);
2134 case S_KC_NA_1
: /* 86 session key change */
2135 get_KSQ(tvb
, &offset
, it_segment_tree
);
2136 get_USR(tvb
, &offset
, it_segment_tree
);
2137 j
= get_WKL(tvb
, &offset
, it_segment_tree
);
2138 get_WKD(tvb
, &offset
, it_segment_tree
, j
);
2140 case S_ER_NA_1
: /* 87 Authentication error */
2141 get_CSQ(tvb
, &offset
, it_segment_tree
);
2142 get_USR(tvb
, &offset
, it_segment_tree
);
2143 get_AID(tvb
, &offset
, it_segment_tree
);
2144 get_ERR(tvb
, &offset
, it_segment_tree
);
2145 get_ETM(tvb
, &offset
, it_segment_tree
);
2146 j
= get_ELN(tvb
, &offset
, it_segment_tree
);
2147 get_ErrorText(tvb
, &offset
, it_segment_tree
, j
);
2150 proto_tree_add_item(it_segment_tree
, hf_ioa
, tvb
, offset
, 3, ENC_LITTLE_ENDIAN
);
2153 if (Len
- offset
> 0)
2154 proto_tree_add_item(it_segment_tree
, hf_asdu_raw_data
, tvb
, offset
, Len
- offset
, ENC_NA
);
2159 /* check correct apdu length */
2160 if (Len
!= offset
) {
2161 expert_add_info(pinfo
, it_segment_tree
, &ei_iec104_apdu_invalid_len
);
2168 /* Handle segmentation of IEC60870-5-7 secure authentication APDUs */
2169 // NOLINTNEXTLINE(misc-no-recursion)
2170 static void dissect_iec60870_segment(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, uint8_t *offset
, uint8_t typeId
, struct asdu_parms
* parms
)
2172 uint32_t msg_seqid
= 0;
2174 bool final_segment
= tvb_get_bits(tvb
, (*offset
<< 3) + 0, 1, ENC_LITTLE_ENDIAN
) == 1;
2176 proto_tree_add_item(tree
, hf_asn_fin
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2177 proto_tree_add_item(tree
, hf_asn_fir
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2178 proto_tree_add_item(tree
, hf_asn
, tvb
, *offset
, 1, ENC_LITTLE_ENDIAN
);
2182 tvbuff_t
*next_tvb
= tvb_new_subset_remaining(tvb
, *offset
);
2183 uint32_t fragment_length
= tvb_captured_length(next_tvb
);
2184 if (!final_segment
) {
2185 col_append_fstr(pinfo
->cinfo
, COL_INFO
, " [ASDU fragment, %u byte%s]",
2186 fragment_length
, plurality(fragment_length
, "", "s"));
2188 col_append_str(pinfo
->cinfo
, COL_INFO
, " EOA");
2191 fragment_head
*fd_head
= fragment_add_seq_next(&iec60870_reassemble_table
, next_tvb
, 0, pinfo
,msg_seqid
, NULL
,
2192 fragment_length
, !final_segment
);
2194 if (fd_head
&& fd_head
->next
) {
2195 /* don't use -1 if fragment length is zero (throws Exception) */
2196 proto_tree_add_bytes_format(tree
, hf_iec60870_segment_data
, tvb
, *offset
, (fragment_length
) ? -1 : 0,
2197 NULL
, "ASDU segment data (%u byte%s)", fragment_length
,
2198 plurality(fragment_length
, "", "s"));
2200 if (final_segment
) {
2201 next_tvb
= process_reassembled_data(next_tvb
, *offset
, pinfo
,
2202 "Reassembled ASDU", fd_head
,
2203 &iec60870_frag_items
, NULL
, tree
);
2204 } else if (pinfo
->num
!= fd_head
->reassembled_in
) {
2205 /* Add a "Reassembled in" link if not reassembled in this frame */
2206 proto_tree_add_uint(tree
, *(iec60870_frag_items
.hf_reassembled_in
),
2207 next_tvb
, 0, 0, fd_head
->reassembled_in
);
2209 pinfo
->fragmented
= !final_segment
;
2212 if (final_segment
) {
2213 dissect_iec60870_asdu_segment(next_tvb
, pinfo
, tree
, typeId
, parms
);
2216 *offset
+= tvb_captured_length_remaining(tvb
, *offset
);
2219 /* Is is called twice: For 'Packet List' and for 'Packet Details' */
2220 /* This dissection is shared by the IEC '101 and '104 dissectors */
2221 // NOLINTNEXTLINE(misc-no-recursion)
2222 static int dissect_iec60870_asdu(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data
)
2224 unsigned Len
= tvb_reported_length(tvb
);
2226 const char *cause_str
;
2228 struct asduheader asduh
= { .OA
= 0, .Addr
= 0, .IOA
= 0};
2229 struct asdu_parms
* parms
= (struct asdu_parms
*)data
;
2231 proto_tree
*it104tree
;
2232 wmem_strbuf_t
* res
;
2234 uint8_t offset
= 0; /* byte offset, signal dissection */
2236 uint32_t asdu_info_obj_addr
= 0;
2237 proto_item
* itSignal
= NULL
;
2238 proto_tree
* trSignal
;
2240 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "IEC 60870-5 ASDU");
2242 it104
= proto_tree_add_item(tree
, proto_iec60870_asdu
, tvb
, offset
, -1, ENC_NA
);
2243 it104tree
= proto_item_add_subtree(it104
, ett_asdu
);
2245 res
= wmem_strbuf_create(pinfo
->pool
);
2247 /* Type identification */
2248 asduh
.TypeId
= tvb_get_uint8(tvb
, offset
);
2249 proto_tree_add_item(it104tree
, hf_typeid
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2250 asduh
.DataLength
= get_TypeIdLength(asduh
.TypeId
);
2253 /* Variable structure qualifier */
2254 Bytex
= tvb_get_uint8(tvb
, 1);
2255 asduh
.SQ
= Bytex
& F_SQ
;
2256 asduh
.NumIx
= Bytex
& 0x7F;
2257 proto_tree_add_item(it104tree
, hf_sq
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2258 proto_tree_add_item(it104tree
, hf_numix
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2261 /* Cause of transmission */
2262 asduh
.TNCause
= tvb_get_uint8(tvb
, offset
);
2263 proto_tree_add_item(it104tree
, hf_causetx
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2264 proto_tree_add_item(it104tree
, hf_nega
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2265 proto_tree_add_item(it104tree
, hf_test
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2268 /* Originator address */
2269 /* This is only present if the Cause of Tx field is 2 octets */
2270 if (parms
->cot_len
== 2) {
2271 asduh
.OA
= tvb_get_uint8(tvb
, offset
);
2272 proto_tree_add_item(it104tree
, hf_oa
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2276 /* Common address of ASDU */
2277 proto_tree_add_item_ret_uint(it104tree
, hf_addr
, tvb
, offset
, parms
->asdu_addr_len
, ENC_LITTLE_ENDIAN
, &asduh
.Addr
);
2278 offset
+= parms
->asdu_addr_len
;
2280 /* Information object address */
2281 /* Support both 16 and 24-bit IOA addresses */
2282 /* Don't increment offset, as we'll want to be at this position later */
2283 if (asduh
.TypeId
< S_CH_NA_1
|| asduh
.TypeId
> S_UC_NA_1
) {
2284 if (parms
->ioa_len
== 3) {
2285 asduh
.IOA
= tvb_get_letoh24(tvb
, offset
);
2287 else if (parms
->ioa_len
== 2) {
2288 asduh
.IOA
= tvb_get_letohs(tvb
, offset
);
2292 cause_str
= val_to_str(asduh
.TNCause
& F_CAUSE
, causetx_types
, " <CauseTx=%u>");
2294 wmem_strbuf_append_printf(res
, "ASDU=%u %s %s", asduh
.Addr
, val_to_str(asduh
.TypeId
, asdu_types
, "<TypeId=%u>"), cause_str
);
2296 if (asduh
.TNCause
& F_NEGA
)
2297 wmem_strbuf_append(res
, "_NEGA");
2298 if (asduh
.TNCause
& F_TEST
)
2299 wmem_strbuf_append(res
, "_TEST");
2301 if ((asduh
.TNCause
& (F_TEST
| F_NEGA
)) == 0) {
2302 for (Ind
=strlen(cause_str
); Ind
< 7; Ind
++)
2303 wmem_strbuf_append(res
, " ");
2306 if (asduh
.NumIx
> 1) {
2307 wmem_strbuf_append_printf(res
, " IOA[%d]=%d", asduh
.NumIx
, asduh
.IOA
);
2308 if (asduh
.SQ
== F_SQ
)
2309 wmem_strbuf_append_printf(res
, "-%d", asduh
.IOA
+ asduh
.NumIx
- 1);
2311 wmem_strbuf_append(res
, ",...");
2313 wmem_strbuf_append_printf(res
, " IOA=%d", asduh
.IOA
);
2316 col_append_str(pinfo
->cinfo
, COL_INFO
, wmem_strbuf_get_str(res
));
2317 col_set_fence(pinfo
->cinfo
, COL_INFO
);
2319 /* 'ASDU Details': ROOT ITEM */
2320 proto_item_append_text(it104
, ": %s '%s'", wmem_strbuf_get_str(res
),
2321 Len
>= offset
+ parms
->ioa_len
? val_to_str_const(asduh
.TypeId
, asdu_lngtypes
, "<Unknown TypeId>") : "");
2323 /* 'Signal Details': TREE */
2324 /* -------- get signal value and status based on ASDU type id */
2326 switch (asduh
.TypeId
) {
2377 /* -- object values */
2378 for(i
= 0; i
< asduh
.NumIx
; i
++)
2380 /* create subtree for the signal values ... */
2381 if (i
== 0 || !asduh
.SQ
)
2382 trSignal
= proto_tree_add_subtree(it104tree
, tvb
, offset
, asduh
.DataLength
+ parms
->ioa_len
,
2383 ett_asdu_objects
, &itSignal
, "IOA:s");
2385 trSignal
= proto_tree_add_subtree(it104tree
, tvb
, offset
, asduh
.DataLength
,
2386 ett_asdu_objects
, &itSignal
, "IOA:s");
2388 /* -------- First Information object address */
2391 /* -------- Information object address */
2393 if(Len
< (unsigned)(offset
+ 3)) {
2394 expert_add_info(pinfo
, itSignal
, &ei_iec104_short_asdu
);
2397 get_InfoObjectAddress(&asdu_info_obj_addr
, tvb
, &offset
, trSignal
, parms
->ioa_len
);
2399 /* -------- following Information object address depending on SQ */
2400 if (asduh
.SQ
) /* <=> SQ=1, info obj addr = startaddr++ */
2403 asdu_info_obj_addr
++;
2404 ti
= proto_tree_add_uint(trSignal
, hf_ioa
, tvb
, 0, 0, asdu_info_obj_addr
);
2405 proto_item_set_generated(ti
);
2406 } else { /* SQ=0, info obj addr given */
2407 /* -------- Information object address */
2409 if(Len
< (unsigned)(offset
+ 3)) {
2410 expert_add_info(pinfo
, itSignal
, &ei_iec104_short_asdu
);
2413 get_InfoObjectAddress(&asdu_info_obj_addr
, tvb
, &offset
, trSignal
, parms
->ioa_len
);
2417 proto_item_set_text(itSignal
, "IOA: %d", asdu_info_obj_addr
);
2420 if(Len
< (unsigned)(offset
+ asduh
.DataLength
)) {
2421 expert_add_info(pinfo
, itSignal
, &ei_iec104_short_asdu
);
2425 switch (asduh
.TypeId
) {
2426 case M_SP_NA_1
: /* 1 Single-point information */
2427 get_SIQ(tvb
, &offset
, trSignal
);
2429 case M_SP_TA_1
: /* 2 Single-point information with time tag */
2430 get_SIQ(tvb
, &offset
, trSignal
);
2431 get_CP24Time(tvb
, &offset
, trSignal
);
2433 case M_DP_NA_1
: /* 3 Double-point information */
2434 get_DIQ(tvb
, &offset
, trSignal
);
2436 case M_DP_TA_1
: /* 4 Double-point information with time tag */
2437 get_DIQ(tvb
, &offset
, trSignal
);
2438 get_CP24Time(tvb
, &offset
, trSignal
);
2440 case M_ST_NA_1
: /* 5 Step position information */
2441 get_VTI(tvb
, &offset
, trSignal
);
2442 get_QDS(tvb
, &offset
, trSignal
);
2444 case M_ST_TA_1
: /* 6 Step position information with time tag */
2445 get_VTI(tvb
, &offset
, trSignal
);
2446 get_QDS(tvb
, &offset
, trSignal
);
2447 get_CP24Time(tvb
, &offset
, trSignal
);
2449 case M_BO_NA_1
: /* 7 Bitstring of 32 bits */
2450 get_BSI(tvb
, &offset
, trSignal
);
2451 get_QDS(tvb
, &offset
, trSignal
);
2453 case M_BO_TA_1
: /* 8 Bitstring of 32 bits with time tag */
2454 get_BSI(tvb
, &offset
, trSignal
);
2455 get_QDS(tvb
, &offset
, trSignal
);
2456 get_CP24Time(tvb
, &offset
, trSignal
);
2458 case M_ME_NA_1
: /* 9 Measured value, normalized value */
2459 get_NVA(tvb
, &offset
, trSignal
);
2460 get_QDS(tvb
, &offset
, trSignal
);
2462 case M_ME_TA_1
: /* 10 Measured value, normalized value with time tag */
2463 get_NVA(tvb
, &offset
, trSignal
);
2464 get_QDS(tvb
, &offset
, trSignal
);
2465 get_CP24Time(tvb
, &offset
, trSignal
);
2467 case M_ME_NB_1
: /* 11 Measured value, scaled value */
2468 get_SVA(tvb
, &offset
, trSignal
);
2469 get_QDS(tvb
, &offset
, trSignal
);
2471 case M_ME_TB_1
: /* 12 Measured value, scaled value with time tag */
2472 get_SVA(tvb
, &offset
, trSignal
);
2473 get_QDS(tvb
, &offset
, trSignal
);
2474 get_CP24Time(tvb
, &offset
, trSignal
);
2476 case M_ME_NC_1
: /* 13 Measured value, short floating point value */
2477 get_FLT(tvb
, &offset
, trSignal
);
2478 get_QDS(tvb
, &offset
, trSignal
);
2480 case M_ME_TC_1
: /* 14 Measured value, short floating point value with time tag */
2481 get_FLT(tvb
, &offset
, trSignal
);
2482 get_QDS(tvb
, &offset
, trSignal
);
2483 get_CP24Time(tvb
, &offset
, trSignal
);
2485 case M_IT_NA_1
: /* 15 Integrated totals */
2486 get_BCR(tvb
, &offset
, trSignal
);
2488 case M_IT_TA_1
: /* 16 Integrated totals with time tag */
2489 get_BCR(tvb
, &offset
, trSignal
);
2490 get_CP24Time(tvb
, &offset
, trSignal
);
2492 case M_ME_ND_1
: /* 21 Measured value, normalized value without quality descriptor */
2493 get_NVA(tvb
, &offset
, trSignal
);
2495 case M_SP_TB_1
: /* 30 Single-point information with time tag CP56Time2a */
2496 get_SIQ(tvb
, &offset
, trSignal
);
2497 get_CP56Time(tvb
, &offset
, trSignal
);
2499 case M_DP_TB_1
: /* 31 Double-point information with time tag CP56Time2a */
2500 get_DIQ(tvb
, &offset
, trSignal
);
2501 get_CP56Time(tvb
, &offset
, trSignal
);
2503 case M_ST_TB_1
: /* 32 Step position information with time tag CP56Time2a */
2504 get_VTI(tvb
, &offset
, trSignal
);
2505 get_QDS(tvb
, &offset
, trSignal
);
2506 get_CP56Time(tvb
, &offset
, trSignal
);
2508 case M_BO_TB_1
: /* 33 Bitstring of 32 bit with time tag CP56Time2a */
2509 get_BSI(tvb
, &offset
, trSignal
);
2510 get_QDS(tvb
, &offset
, trSignal
);
2511 get_CP56Time(tvb
, &offset
, trSignal
);
2513 case M_ME_TD_1
: /* 34 Measured value, normalized value with time tag CP56Time2a */
2514 get_NVA(tvb
, &offset
, trSignal
);
2515 get_QDS(tvb
, &offset
, trSignal
);
2516 get_CP56Time(tvb
, &offset
, trSignal
);
2518 case M_ME_TE_1
: /* 35 Measured value, scaled value with time tag CP56Time2a */
2519 get_SVA(tvb
, &offset
, trSignal
);
2520 get_QDS(tvb
, &offset
, trSignal
);
2521 get_CP56Time(tvb
, &offset
, trSignal
);
2523 case M_ME_TF_1
: /* 36 Measured value, short floating point value with time tag CP56Time2a */
2524 get_FLT(tvb
, &offset
, trSignal
);
2525 get_QDS(tvb
, &offset
, trSignal
);
2526 get_CP56Time(tvb
, &offset
, trSignal
);
2528 case M_IT_TB_1
: /* 37 Integrated totals with time tag CP56Time2a */
2529 get_BCR(tvb
, &offset
, trSignal
);
2530 get_CP56Time(tvb
, &offset
, trSignal
);
2532 case S_IT_TC_1
: /* 41 Integrated totals containing time tagged security statistics */
2533 get_AID(tvb
, &offset
, trSignal
);
2534 get_BCR(tvb
, &offset
, trSignal
);
2535 get_CP56Time(tvb
, &offset
, trSignal
);
2537 case C_SC_NA_1
: /* 45 Single command */
2538 get_SCO(tvb
, &offset
, trSignal
);
2540 case C_DC_NA_1
: /* 46 Double command */
2541 get_DCO(tvb
, &offset
, trSignal
);
2543 case C_RC_NA_1
: /* 47 Regulating step command */
2544 get_RCO(tvb
, &offset
, trSignal
);
2546 case C_SE_NA_1
: /* 48 Set point command, normalized value */
2547 get_NVAspt(tvb
, &offset
, trSignal
);
2548 get_QOS(tvb
, &offset
, trSignal
);
2550 case C_SE_NB_1
: /* 49 Set point command, scaled value */
2551 get_SVAspt(tvb
, &offset
, trSignal
);
2552 get_QOS(tvb
, &offset
, trSignal
);
2554 case C_SE_NC_1
: /* 50 Set point command, short floating point value */
2555 get_FLTspt(tvb
, &offset
, trSignal
);
2556 get_QOS(tvb
, &offset
, trSignal
);
2558 case C_BO_NA_1
: /* 51 Bitstring of 32 bits */
2559 get_BSIspt(tvb
, &offset
, trSignal
);
2561 case C_SC_TA_1
: /* 58 Single command with time tag CP56Time2a */
2562 get_SCO(tvb
, &offset
, trSignal
);
2563 get_CP56Time(tvb
, &offset
, trSignal
);
2565 case C_DC_TA_1
: /* 59 Double command with time tag CP56Time2a */
2566 get_DCO(tvb
, &offset
, trSignal
);
2567 get_CP56Time(tvb
, &offset
, trSignal
);
2569 case C_RC_TA_1
: /* 60 Regulating step command with time tag CP56Time2a */
2570 get_RCO(tvb
, &offset
, trSignal
);
2571 get_CP56Time(tvb
, &offset
, trSignal
);
2573 case C_SE_TA_1
: /* 61 Set point command, normalized value with time tag CP56Time2a */
2574 get_NVAspt(tvb
, &offset
, trSignal
);
2575 get_QOS(tvb
, &offset
, trSignal
);
2576 get_CP56Time(tvb
, &offset
, trSignal
);
2578 case C_SE_TB_1
: /* 62 Set point command, scaled value with time tag CP56Time2a */
2579 get_SVAspt(tvb
, &offset
, trSignal
);
2580 get_QOS(tvb
, &offset
, trSignal
);
2581 get_CP56Time(tvb
, &offset
, trSignal
);
2583 case C_SE_TC_1
: /* 63 Set point command, short floating point value with time tag CP56Time2a */
2584 get_FLTspt(tvb
, &offset
, trSignal
);
2585 get_QOS(tvb
, &offset
, trSignal
);
2586 get_CP56Time(tvb
, &offset
, trSignal
);
2588 case C_BO_TA_1
: /* 64 Bitstring of 32 bits with time tag CP56Time2a */
2589 get_BSIspt(tvb
, &offset
, trSignal
);
2590 get_CP56Time(tvb
, &offset
, trSignal
);
2592 case M_EI_NA_1
: /* 70 End of initialization */
2593 get_COI(tvb
, &offset
, trSignal
);
2595 case C_IC_NA_1
: /* 100 Interrogation command */
2596 get_QOI(tvb
, &offset
, trSignal
);
2598 case C_CI_NA_1
: /* 101 Counter interrogation command */
2599 get_QCC(tvb
, &offset
, trSignal
);
2601 case C_CS_NA_1
: /* 103 Clock synchronization command */
2602 get_CP56Time(tvb
, &offset
, trSignal
);
2604 case C_RP_NA_1
: /* 105 reset process command */
2605 get_QRP(tvb
, &offset
, trSignal
);
2607 case C_TS_TA_1
: /* 107 test command with time tag CP56Time2a */
2608 get_TSC(tvb
, &offset
, trSignal
);
2609 get_CP56Time(tvb
, &offset
, trSignal
);
2611 case P_ME_NA_1
: /* 110 Parameter of measured value, normalized value */
2612 get_NVA(tvb
, &offset
, trSignal
);
2613 get_QPM(tvb
, &offset
, trSignal
);
2615 case P_ME_NB_1
: /* 111 Parameter of measured value, scaled value */
2616 get_SVA(tvb
, &offset
, trSignal
);
2617 get_QPM(tvb
, &offset
, trSignal
);
2619 case P_ME_NC_1
: /* 112 Parameter of measured value, short floating-point number */
2620 get_FLT(tvb
, &offset
, trSignal
);
2621 get_QPM(tvb
, &offset
, trSignal
);
2625 } /* end 'switch (asduh.TypeId)' */
2626 } /* end 'for(i = 0; i < dui.asdu_vsq_no_of_obj; i++)' */
2628 case S_CH_NA_1
: /* 81 authentication challenge */
2629 case S_RP_NA_1
: /* 82 authentication reply */
2630 case S_AR_NA_1
: /* 83 Aggressive mode authentication request */
2631 case S_KS_NA_1
: /* 85 session key status */
2632 case S_KC_NA_1
: /* 86 session key change */
2633 case S_ER_NA_1
: /* 87 Authentication error */
2634 dissect_iec60870_segment(tvb
, pinfo
, it104tree
, &offset
, asduh
.TypeId
, parms
);
2636 case S_KR_NA_1
: /* 84 Session key status request */
2637 proto_tree_add_item(it104tree
, hf_usr
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
2641 proto_tree_add_item(it104tree
, hf_ioa
, tvb
, offset
, 3, ENC_LITTLE_ENDIAN
);
2644 if (Len
- offset
> 0)
2645 proto_tree_add_item(it104tree
, hf_asdu_raw_data
, tvb
, offset
, Len
- offset
, ENC_NA
);
2649 } /* end 'switch (asdu_typeid)' */
2651 /* check correct apdu length */
2652 if (Len
!= offset
) {
2653 expert_add_info_format(pinfo
, it104tree
, &ei_iec104_apdu_invalid_len
, "Invalid Apdulen (%d != %d)", Len
, offset
);
2657 return tvb_captured_length(tvb
);
2662 /* Is is called twice: For 'Packet List' and for 'Packet Details' */
2663 static int dissect_iec60870_104(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
2665 unsigned TcpLen
= tvb_reported_length(tvb
);
2666 uint8_t Start
, len
, type
, temp8
;
2667 uint32_t apci_txid
, apci_rxid
, apci_u_type
;
2669 proto_item
*it104
, *ti
;
2670 proto_tree
*it104tree
;
2671 wmem_strbuf_t
* res
;
2672 struct asdu_parms parms
;
2674 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "IEC 60870-5-104");
2676 it104
= proto_tree_add_item(tree
, proto_iec60870_104
, tvb
, 0, -1, ENC_NA
);
2677 it104tree
= proto_item_add_subtree(it104
, ett_apci
);
2679 res
= wmem_strbuf_create(pinfo
->pool
);
2682 for (Off
= 0; Off
<= TcpLen
- 2; Off
++) {
2683 Start
= tvb_get_uint8(tvb
, Off
);
2685 if (Start
== APCI_START
) {
2688 proto_tree_add_item(it104tree
, hf_apcidata
, tvb
, 0, Off
, ENC_NA
);
2689 wmem_strbuf_append_printf(res
, "<ERR prefix %u bytes> ", Off
);
2692 proto_item_set_len(it104
, Off
+ APCI_LEN
);
2694 proto_tree_add_uint_format(it104tree
, hf_start
, tvb
, Off
, 1, Start
, "START");
2695 ti
= proto_tree_add_item(it104tree
, hf_apdulen
, tvb
, Off
+ 1, 1, ENC_LITTLE_ENDIAN
);
2697 len
= tvb_get_uint8(tvb
, Off
+ 1);
2698 if (len
< APDU_MIN_LEN
) {
2699 expert_add_info_format(pinfo
, ti
, &ei_iec104_apdu_min_len
, "APDU less than %d bytes", APDU_MIN_LEN
);
2700 wmem_strbuf_append_printf(res
, "<ERR ApduLen=%u bytes> ", len
);
2701 return tvb_captured_length(tvb
);
2704 temp8
= tvb_get_uint8(tvb
, Off
+ 2);
2705 if ((temp8
& 0x01) == 0)
2708 type
= temp8
& 0x03;
2711 proto_tree_add_item(it104tree
, hf_apcitype_i
, tvb
, Off
+ 2, 4, ENC_LITTLE_ENDIAN
);
2713 proto_tree_add_item(it104tree
, hf_apcitype_s_u
, tvb
, Off
+ 2, 4, ENC_LITTLE_ENDIAN
);
2715 if (len
<= APDU_MAX_LEN
) {
2716 wmem_strbuf_append_printf(res
, "%s %s ",
2717 (pinfo
->srcport
== pinfo
->match_uint
? "->" : "<-"),
2718 val_to_str_const(type
, apci_types
, "<ERR>"));
2721 wmem_strbuf_append_printf(res
, "<ERR ApduLen=%u bytes> ", len
);
2726 proto_tree_add_item_ret_uint(it104tree
, hf_apcitx
, tvb
, Off
+ 2, 4, ENC_LITTLE_ENDIAN
, &apci_txid
);
2727 proto_tree_add_item_ret_uint(it104tree
, hf_apcirx
, tvb
, Off
+ 2, 4, ENC_LITTLE_ENDIAN
, &apci_rxid
);
2728 wmem_strbuf_append_printf(res
, "(%d,%d) ", apci_txid
, apci_rxid
);
2731 proto_tree_add_item_ret_uint(it104tree
, hf_apcirx
, tvb
, Off
+ 2, 4, ENC_LITTLE_ENDIAN
, &apci_rxid
);
2732 wmem_strbuf_append_printf(res
, "(%d) ", apci_rxid
);
2735 proto_tree_add_item_ret_uint(it104tree
, hf_apciutype
, tvb
, Off
+ 2, 4, ENC_LITTLE_ENDIAN
, &apci_u_type
);
2736 wmem_strbuf_append_printf(res
, "(%s) ", val_to_str_const(apci_u_type
, u_types
, "<ERR>"));
2740 col_clear(pinfo
->cinfo
, COL_INFO
);
2741 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, " | ", wmem_strbuf_get_str(res
));
2742 col_set_fence(pinfo
->cinfo
, COL_INFO
);
2744 proto_item_append_text(it104
, ": %s", wmem_strbuf_get_str(res
));
2746 if (type
== I_TYPE
) {
2747 /* Set the field lengths to the '104 fixed values before calling the ASDU dissection */
2749 parms
.asdu_addr_len
= 2;
2752 call_dissector_with_data(iec60870_asdu_handle
, tvb_new_subset_length_caplen(tvb
, Off
+ APCI_LEN
, -1, len
- APCI_DATA_LEN
), pinfo
, tree
, &parms
);
2754 /* Don't search more the APCI_START */
2759 if (Start
!= APCI_START
) {
2760 /* Everything is bad (no APCI found) */
2761 proto_tree_add_item(it104tree
, hf_apcidata
, tvb
, 0, Off
, ENC_NA
);
2764 return tvb_captured_length(tvb
);
2767 /******************************************************************************************************/
2768 /* Code to dissect IEC 101 Protocol packets */
2769 /******************************************************************************************************/
2771 dissect_iec60870_101(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
2773 /* Set up structures needed to add the protocol subtree and manage it */
2774 proto_item
*iec101_item
, *ctrlfield_item
, *expert_item
;
2775 proto_tree
*iec101_tree
, *ctrlfield_tree
;
2776 uint8_t ctrlfield_prm
;
2777 uint32_t frametype
, linkaddr
, data_len
, stopchar
;
2779 struct asdu_parms parms
;
2781 /* Make entries in Protocol column on summary display */
2782 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "IEC 60870-5-101");
2783 col_clear(pinfo
->cinfo
, COL_INFO
);
2785 iec101_item
= proto_tree_add_item(tree
, proto_iec60870_101
, tvb
, 0, -1, ENC_NA
);
2786 iec101_tree
= proto_item_add_subtree(iec101_item
, ett_iec60870_101
);
2788 /* Add Frame Format to Protocol Tree */
2789 proto_tree_add_item_ret_uint(iec101_tree
, hf_iec60870_101_frame
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
, &frametype
);
2792 /* If this is a single character frame, there is nothing left to do... */
2793 if (frametype
== IEC101_SINGLE_CHAR
) {
2797 if (frametype
== IEC101_VAR_LEN
) {
2798 proto_tree_add_item(iec101_tree
, hf_iec60870_101_length
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2799 expert_item
= proto_tree_add_item_ret_uint(iec101_tree
, hf_iec60870_101_num_user_octets
, tvb
, offset
+1, 1, ENC_LITTLE_ENDIAN
, &data_len
);
2800 if (data_len
!= tvb_get_uint8(tvb
, offset
)) {
2801 expert_add_info(pinfo
, expert_item
, &ei_iec101_length_mismatch
);
2802 col_set_str(pinfo
->cinfo
, COL_INFO
, "Continuation");
2803 return tvb_captured_length(tvb
);
2805 /* do not include the ctrl field and link address bytes in the length passed to the asdu dissector */
2806 data_len
-= 1 + global_iec60870_link_addr_len
;
2807 expert_item
= proto_tree_add_item_ret_uint(iec101_tree
, hf_iec60870_101_frame
, tvb
, offset
+2, 1, ENC_LITTLE_ENDIAN
, &frametype
);
2808 if (frametype
!= IEC101_VAR_LEN
) {
2809 expert_add_info(pinfo
, expert_item
, &ei_iec101_frame_mismatch
);
2810 col_set_str(pinfo
->cinfo
, COL_INFO
, "Continuation");
2811 return tvb_captured_length(tvb
);
2816 /* Fields common to both variable and fixed length frames */
2817 ctrlfield_item
= proto_tree_add_item(iec101_tree
, hf_iec60870_101_ctrlfield
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2818 ctrlfield_tree
= proto_item_add_subtree(ctrlfield_item
, ett_iec60870_101_ctrlfield
);
2820 ctrlfield_prm
= tvb_get_uint8(tvb
, offset
) & 0x40;
2821 if (ctrlfield_prm
) {
2822 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ", ", "Pri->Sec");
2823 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_101_ctrl_prm
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2824 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_101_ctrl_fcb
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2825 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_101_ctrl_fcv
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2826 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_101_ctrl_func_pri_to_sec
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2829 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ", ", "Sec->Pri");
2830 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_101_ctrl_prm
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2831 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_101_ctrl_dfc
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2832 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_101_ctrl_func_sec_to_pri
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2836 if (global_iec60870_link_addr_len
) {
2837 proto_tree_add_item_ret_uint(iec101_tree
, hf_iec60870_101_linkaddr
, tvb
, offset
, global_iec60870_link_addr_len
, ENC_LITTLE_ENDIAN
, &linkaddr
);
2838 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
, "Link Address: %d ", linkaddr
);
2839 offset
+= global_iec60870_link_addr_len
;
2842 /* If this is a variable length frame, we need to call the ASDU dissector */
2843 if (frametype
== IEC101_VAR_LEN
) {
2845 /* Retrieve the user preferences */
2846 parms
.cot_len
= global_iec60870_cot_len
;
2847 parms
.asdu_addr_len
= global_iec60870_asdu_addr_len
;
2848 parms
.ioa_len
= global_iec60870_ioa_len
;
2850 call_dissector_with_data(iec60870_asdu_handle
, tvb_new_subset_length_caplen(tvb
, offset
, -1, data_len
), pinfo
, tree
, &parms
);
2854 proto_tree_add_item(iec101_tree
, hf_iec60870_101_checksum
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
2855 expert_item
= proto_tree_add_item_ret_uint(iec101_tree
, hf_iec60870_101_stopchar
, tvb
, offset
+1, 1, ENC_LITTLE_ENDIAN
, &stopchar
);
2856 if (stopchar
!= IEC101_STOP_CHAR
) {
2857 expert_add_info(pinfo
, expert_item
, &ei_iec101_stopchar_invalid
);
2865 static int dissect_iec60870_104_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data
)
2867 /* 5th parameter = 6 = minimum bytes received to calculate the length.
2868 * (Not 2 in order to find more APCIs in case of 'noisy' bytes between the APCIs)
2870 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, APCI_LEN
,
2871 get_iec104apdu_len
, dissect_iec60870_104
, data
);
2872 return tvb_captured_length(tvb
);
2875 /* The protocol has two subprotocols: Register APCI */
2877 proto_register_iec60870_104(void)
2879 static hf_register_info hf_ap
[] = {
2882 { "ApduLen", "iec60870_104.apdulen", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
2883 "APDU Len", HFILL
}},
2886 { "Type", "iec60870_104.type", FT_UINT32
, BASE_HEX
, VALS(apci_types
), 0x00000001,
2887 "APCI type", HFILL
}},
2890 { "Type", "iec60870_104.type", FT_UINT32
, BASE_HEX
, VALS(apci_types
), 0x00000003,
2891 "APCI type", HFILL
}},
2894 { "UType", "iec60870_104.utype", FT_UINT32
, BASE_HEX
, VALS(u_types
), 0x000000FC,
2895 "Apci U type", HFILL
}},
2898 { "Tx", "iec60870_104.tx", FT_UINT32
, BASE_DEC
, NULL
, 0x0000FFFE,
2902 { "Rx", "iec60870_104.rx", FT_UINT32
, BASE_DEC
, NULL
, 0xFFFE0000,
2906 { "Data", "iec60870_104.data", FT_BYTES
, BASE_NONE
, NULL
, 0,
2910 static int *ett_ap
[] = {
2914 proto_iec60870_104
= proto_register_protocol("IEC 60870-5-104", "IEC 60870-5-104", "iec60870_104");
2916 /* Provide an alias to the previous name of this dissector */
2917 proto_register_alias(proto_iec60870_104
, "104apci");
2919 proto_register_field_array(proto_iec60870_104
, hf_ap
, array_length(hf_ap
));
2920 proto_register_subtree_array(ett_ap
, array_length(ett_ap
));
2922 prefs_register_protocol(proto_iec60870_104
, NULL
);
2924 iec60870_104_handle
= register_dissector("iec60870_104", dissect_iec60870_104_tcp
, proto_iec60870_104
);
2928 /* Register ASDU dissection, shared by the '101 and '104 dissectors */
2930 proto_register_iec60870_asdu(void)
2932 static hf_register_info hf_as
[] = {
2935 { "Addr", "iec60870_asdu.addr", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
2936 "Common Address of Asdu", HFILL
}},
2939 { "OA", "iec60870_asdu.oa", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
2940 "Originator Address", HFILL
}},
2943 { "TypeId", "iec60870_asdu.typeid", FT_UINT8
, BASE_DEC
, VALS(asdu_types
), 0x0,
2944 "Asdu Type Id", HFILL
}},
2947 { "CauseTx", "iec60870_asdu.causetx", FT_UINT8
, BASE_DEC
, VALS(causetx_types
), F_CAUSE
,
2948 "Cause of Transmission", HFILL
}},
2951 { "Negative", "iec60870_asdu.nega", FT_BOOLEAN
, 8, NULL
, F_NEGA
,
2955 { "Test", "iec60870_asdu.test", FT_BOOLEAN
, 8, NULL
, F_TEST
,
2959 { "IOA", "iec60870_asdu.ioa", FT_UINT24
, BASE_DEC
, NULL
, 0x0,
2960 "Information Object Address", HFILL
}},
2963 { "NumIx", "iec60870_asdu.numix", FT_UINT8
, BASE_DEC
, NULL
, 0x7F,
2964 "Number of Information Objects/Elements", HFILL
}},
2967 { "SQ", "iec60870_asdu.sq", FT_BOOLEAN
, 8, NULL
, F_SQ
,
2968 "Sequence", HFILL
}},
2971 { "CP24Time", "iec60870_asdu.cp24time", FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0,
2975 { "MS", "iec60870_asdu.cp24time.ms", FT_UINT16
, BASE_DEC
, NULL
, 0,
2976 "CP24Time milliseconds", HFILL
}},
2979 { "Min", "iec60870_asdu.cp24time.min", FT_UINT8
, BASE_DEC
, NULL
, 0x3F,
2980 "CP24Time minutes", HFILL
}},
2983 { "IV", "iec60870_asdu.cp24time.iv", FT_BOOLEAN
, 8, TFS(&tfs_invalid_valid
), 0x80,
2984 "CP24Time invalid", HFILL
}},
2987 { "CP56Time", "iec60870_asdu.cp56time", FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_LOCAL
, NULL
, 0,
2991 { "MS", "iec60870_asdu.cp56time.ms", FT_UINT16
, BASE_DEC
, NULL
, 0,
2992 "CP56Time milliseconds", HFILL
}},
2995 { "Min", "iec60870_asdu.cp56time.min", FT_UINT8
, BASE_DEC
, NULL
, 0x3F,
2996 "CP56Time minutes", HFILL
}},
2999 { "GEN", "iec60870_asdu.cp56time.gen", FT_BOOLEAN
, 8, TFS(&tfs_substituted_not_substituted
), 0x40,
3000 "CP56Time substituted", HFILL
}},
3003 { "IV", "iec60870_asdu.cp56time.iv", FT_BOOLEAN
, 8, TFS(&tfs_invalid_valid
), 0x80,
3004 "CP56Time invalid", HFILL
}},
3006 { &hf_cp56time_hour
,
3007 { "Hour", "iec60870_asdu.cp56time.hour", FT_UINT8
, BASE_DEC
, NULL
, 0x1F,
3008 "CP56Time hours", HFILL
}},
3011 { "SU", "iec60870_asdu.cp56time.su", FT_BOOLEAN
, 8, TFS(&tfs_local_dst
), 0x80,
3012 "CP56Time summer time", HFILL
}},
3015 { "Day", "iec60870_asdu.cp56time.day", FT_UINT8
, BASE_DEC
, NULL
, 0x1F,
3016 "CP56Time day", HFILL
}},
3019 { "DOW", "iec60870_asdu.cp56time.dow", FT_UINT8
, BASE_DEC
, NULL
, 0xE0,
3020 "CP56Time day of week", HFILL
}},
3022 { &hf_cp56time_month
,
3023 { "Month", "iec60870_asdu.cp56time.month", FT_UINT8
, BASE_DEC
, NULL
, 0x0F,
3024 "CP56Time month", HFILL
}},
3026 { &hf_cp56time_year
,
3027 { "Year", "iec60870_asdu.cp56time.year", FT_UINT8
, BASE_DEC
, NULL
, 0x7F,
3028 "CP56Time year", HFILL
}},
3031 { "SIQ", "iec60870_asdu.siq", FT_UINT8
, BASE_HEX
, NULL
, 0,
3035 { "SPI", "iec60870_asdu.siq.spi", FT_BOOLEAN
, 8, TFS(&tfs_on_off
), 0x01,
3036 "SIQ SPI", HFILL
}},
3039 { "BL", "iec60870_asdu.siq.bl", FT_BOOLEAN
, 8, TFS(&tfs_blocked_not_blocked
), 0x10,
3043 { "SB", "iec60870_asdu.siq.sb", FT_BOOLEAN
, 8, TFS(&tfs_substituted_not_substituted
), 0x20,
3047 { "NT", "iec60870_asdu.siq.nt", FT_BOOLEAN
, 8, TFS(&tfs_not_topical_topical
), 0x40,
3051 { "IV", "iec60870_asdu.siq.iv", FT_BOOLEAN
, 8, TFS(&tfs_invalid_valid
), 0x80,
3055 { "DIQ", "iec60870_asdu.diq", FT_UINT8
, BASE_HEX
, NULL
, 0,
3059 { "DPI", "iec60870_asdu.diq.dpi", FT_UINT8
, BASE_DEC
, VALS(diq_types
), 0x03,
3060 "DIQ DPI", HFILL
}},
3063 { "BL", "iec60870_asdu.diq.bl", FT_BOOLEAN
, 8, TFS(&tfs_blocked_not_blocked
), 0x10,
3067 { "SB", "iec60870_asdu.diq.sb", FT_BOOLEAN
, 8, TFS(&tfs_substituted_not_substituted
), 0x20,
3071 { "NT", "iec60870_asdu.diq.nt", FT_BOOLEAN
, 8, TFS(&tfs_not_topical_topical
), 0x40,
3075 { "IV", "iec60870_asdu.diq.iv", FT_BOOLEAN
, 8, TFS(&tfs_invalid_valid
), 0x80,
3079 { "QDS", "iec60870_asdu.qds", FT_UINT8
, BASE_HEX
, NULL
, 0,
3083 { "OV", "iec60870_asdu.qds.ov", FT_BOOLEAN
, 8, TFS(&tfs_overflow_no_overflow
), 0x01,
3087 { "BL", "iec60870_asdu.qds.bl", FT_BOOLEAN
, 8, TFS(&tfs_blocked_not_blocked
), 0x10,
3091 { "SB", "iec60870_asdu.qds.sb", FT_BOOLEAN
, 8, TFS(&tfs_substituted_not_substituted
), 0x20,
3095 { "NT", "iec60870_asdu.qds.nt", FT_BOOLEAN
, 8, TFS(&tfs_not_topical_topical
), 0x40,
3099 { "IV", "iec60870_asdu.qds.iv", FT_BOOLEAN
, 8, TFS(&tfs_invalid_valid
), 0x80,
3103 { "VTI", "iec60870_asdu.vti", FT_UINT8
, BASE_HEX
, NULL
, 0,
3107 { "Value", "iec60870_asdu.vti.v", FT_INT8
, BASE_DEC
, NULL
, 0x7F,
3108 "VTI Value", HFILL
}},
3111 { "T", "iec60870_asdu.vti.t", FT_BOOLEAN
, 8, TFS(&tfs_transient_not_transient
), 0x80,
3115 { "QOS", "iec60870_asdu.qos", FT_UINT8
, BASE_HEX
, NULL
, 0,
3119 { "QL", "iec60870_asdu.qos.ql", FT_UINT8
, BASE_DEC
, NULL
, 0x7F,
3123 { "S/E", "iec60870_asdu.qos.se", FT_BOOLEAN
, 8, TFS(&tfs_select_execute
), 0x80,
3124 "QOS S/E", HFILL
}},
3127 { "SCO", "iec60870_asdu.sco", FT_UINT8
, BASE_HEX
, NULL
, 0,
3131 { "ON/OFF", "iec60870_asdu.sco.on", FT_BOOLEAN
, 8, TFS(&tfs_on_off
), 0x01,
3132 "SCO SCS", HFILL
}},
3135 { "QU", "iec60870_asdu.sco.qu", FT_UINT8
, BASE_DEC
, VALS(qos_qu_types
), 0x7C,
3139 { "S/E", "iec60870_asdu.sco.se", FT_BOOLEAN
, 8, TFS(&tfs_select_execute
), 0x80,
3140 "SCO S/E", HFILL
}},
3143 { "DCO", "iec60870_asdu.dco", FT_UINT8
, BASE_HEX
, NULL
, 0,
3147 { "ON/OFF", "iec60870_asdu.dco.on", FT_UINT8
, BASE_DEC
, VALS(dco_on_types
), 0x03,
3148 "DCO DCS", HFILL
}},
3151 { "QU", "iec60870_asdu.dco.qu", FT_UINT8
, BASE_DEC
, VALS(qos_qu_types
), 0x7C,
3155 { "S/E", "iec60870_asdu.dco.se", FT_BOOLEAN
, 8, TFS(&tfs_select_execute
), 0x80,
3156 "DCO S/E", HFILL
}},
3159 { "RCO", "iec60870_asdu.rco", FT_UINT8
, BASE_HEX
, NULL
, 0,
3163 { "UP/DOWN", "iec60870_asdu.rco.up", FT_UINT8
, BASE_DEC
, VALS(rco_up_types
), 0x03,
3164 "RCO RCS", HFILL
}},
3167 { "QU", "iec60870_asdu.rco.qu", FT_UINT8
, BASE_DEC
, VALS(qos_qu_types
), 0x7C,
3171 { "S/E", "iec60870_asdu.rco.se", FT_BOOLEAN
, 8, TFS(&tfs_select_execute
), 0x80,
3172 "RCO S/E", HFILL
}},
3175 { "QPM", "iec60870_asdu.qpm", FT_UINT8
, BASE_HEX
, NULL
, 0,
3179 { "KPA", "iec60870_asdu.qpm.kpa", FT_UINT8
, BASE_DEC
, VALS(qpm_kpa_types
), 0x3F,
3180 "QPM KPA", HFILL
} },
3183 { "LPC", "iec60870_asdu.qpm.lpc", FT_UINT8
, BASE_DEC
, VALS(qpm_lpc_types
), 0x40,
3184 "QPM LPC", HFILL
} },
3187 { "POP", "iec60870_asdu.qpm.pop", FT_UINT8
, BASE_DEC
, VALS(qpm_pop_types
), 0x80,
3188 "QPM POP", HFILL
} },
3191 { "ASDU Segment Sequence Number (ASN)", "iec60870_asdu.asn", FT_UINT8
, BASE_DEC
, NULL
, 0x3F,
3195 { "Final segment (FIN)", "iec60870_asdu.asn.fin", FT_BOOLEAN
, 8, TFS(&tfs_yes_no
), 0x80,
3199 { "First segment (FIR)", "iec60870_asdu.asn.fir", FT_BOOLEAN
, 8, TFS(&tfs_yes_no
), 0x40,
3202 { &hf_iec60870_segment_data
,
3203 { "ASDU segment data", "iec60870_asdu.segment_data", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
3207 { "User number (USR)", "iec60870_asdu.usr", FT_UINT16
, BASE_DEC
| BASE_RANGE_STRING
, RVALS(usr_types
), 0,
3211 { "MAC algorithm (MAL)", "iec60870_asdu.mal", FT_UINT8
, BASE_DEC
| BASE_RANGE_STRING
, RVALS(mal_types
), 0,
3215 { "Reason for challenge (RSC)", "iec60870_asdu.rsc", FT_UINT8
, BASE_DEC
, VALS(rsc_types
), 0,
3219 { "Challenge sequence number (CSQ)", "iec60870_asdu.csq", FT_UINT32
, BASE_DEC
, NULL
, 0,
3223 { "Key change sequence number (KSQ)", "iec60870_asdu.ksq", FT_UINT32
, BASE_DEC
, NULL
, 0,
3227 { "Key wrap algorithm (KWA)", "iec60870_asdu.kwa", FT_UINT8
, BASE_DEC
| BASE_RANGE_STRING
, RVALS(kwa_types
), 0,
3231 { "Key status (KST)", "iec60870_asdu.kst", FT_UINT8
, BASE_DEC
, VALS(kst_types
), 0,
3235 { "MAC length (HLN)", "iec60870_asdu.hln", FT_UINT16
, BASE_DEC
, NULL
, 0,
3239 { "MAC algorithm (HAL)", "iec60870_asdu.hal", FT_UINT8
, BASE_DEC
| BASE_RANGE_STRING
, RVALS(hal_types
), 0,
3243 { "Challenge data length (CLN)", "iec60870_asdu.cln", FT_UINT16
, BASE_DEC
, NULL
, 0,
3247 { "Wrapped key data length (CLN)", "iec60870_asdu.wkl", FT_UINT16
, BASE_DEC
, NULL
, 0,
3250 { &hf_prcd_raw_data
,
3251 { "Pseudo-random challenge data", "iec60870_asdu.challenge_data", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
3254 { &hf_hmac_raw_data
,
3255 { "HMAC value", "iec60870_asdu.hmac", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
3259 { "Wrapped key data", "iec60870_asdu.wkd", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
3263 { "Association ID (AID)", "iec60870_asdu.aid", FT_UINT16
, BASE_DEC
, NULL
, 0,
3267 { "Error code (ERR)", "iec60870_asdu.err", FT_UINT8
, BASE_DEC
| BASE_RANGE_STRING
, RVALS(error_codes
), 0,
3271 { "Error time stamp (ETM)", "iec60870_asdu.etm", FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_LOCAL
, NULL
, 0,
3275 { "MS", "iec60870_asdu.etm.ms", FT_UINT16
, BASE_DEC
, NULL
, 0,
3276 "Error time stamp milliseconds", HFILL
}},
3279 { "Min", "iec60870_asdu.etm.min", FT_UINT8
, BASE_DEC
, NULL
, 0x3F,
3280 "Error time stamp minutes", HFILL
}},
3283 { "IV", "iec60870_asdu.etm.iv", FT_BOOLEAN
, 8, TFS(&tfs_invalid_valid
), 0x80,
3284 "Error time stamp invalid", HFILL
}},
3287 { "Hour", "iec60870_asdu.etm.hour", FT_UINT8
, BASE_DEC
, NULL
, 0x1F,
3288 "Error time stamp hours", HFILL
}},
3291 { "SU", "iec60870_asdu.etm.su", FT_BOOLEAN
, 8, TFS(&tfs_local_dst
), 0x80,
3292 "Error time stamp summer time", HFILL
}},
3295 { "Day", "iec60870_asdu.etm.day", FT_UINT8
, BASE_DEC
, NULL
, 0x1F,
3296 "Error time stamp day", HFILL
}},
3299 { "DOW", "iec60870_asdu.etm.dow", FT_UINT8
, BASE_DEC
, NULL
, 0xE0,
3300 "Error time stamp day of week", HFILL
}},
3303 { "Month", "iec60870_asdu.etm.month", FT_UINT8
, BASE_DEC
, NULL
, 0x0F,
3304 "Error time stamp month", HFILL
}},
3307 { "Year", "iec60870_asdu.etm.year", FT_UINT8
, BASE_DEC
, NULL
, 0x7F,
3308 "Error time stamp year", HFILL
}},
3311 { "Error length (ELN)", "iec60870_asdu.eln", FT_UINT16
, BASE_DEC
, NULL
, 0,
3315 { "Error text", "iec60870_asdu.error_text", FT_STRING
, BASE_NONE
, NULL
, 0x0,
3319 { "COI", "iec60870_asdu.coi", FT_UINT8
, BASE_HEX
, NULL
, 0,
3323 { "R", "iec60870_asdu.coi.r", FT_UINT8
, BASE_DEC
, VALS(coi_r_types
), 0x7F,
3327 { "I", "iec60870_asdu.coi.i", FT_BOOLEAN
, 8, TFS(&tfs_coi_i
), 0x80,
3331 { "QOI", "iec60870_asdu.qoi", FT_UINT8
, BASE_DEC
, VALS(qoi_r_types
), 0,
3335 { "QCC", "iec60870_asdu.qcc", FT_UINT8
, BASE_HEX
, NULL
, 0,
3339 { "RQT", "iec60870_asdu.qcc.rqt", FT_UINT8
, BASE_DEC
, VALS(rqt_r_types
), 0x3F,
3343 { "FRZ", "iec60870_asdu.qcc.frz", FT_UINT8
, BASE_DEC
, VALS(frz_r_types
), 0xC0,
3347 { "QRP", "iec60870_asdu.qrp", FT_UINT8
, BASE_DEC
, VALS(qrp_r_types
), 0,
3351 { "BCR", "iec60870_asdu.bcr", FT_INT32
, BASE_DEC
, NULL
, 0x0,
3352 "Binary Counter", HFILL
}},
3355 { "Value", "iec60870_asdu.bcr.count", FT_INT32
, BASE_DEC
, NULL
, 0x0,
3359 { "SQ", "iec60870_asdu.bcr.sq", FT_UINT8
, BASE_DEC
, NULL
, 0x1F,
3360 "Sequence Number", HFILL
}},
3363 { "CY", "iec60870_asdu.bcr.cy", FT_BOOLEAN
, 8, TFS(&tfs_overflow_no_overflow
), 0x20,
3364 "Counter Overflow", HFILL
}},
3367 { "CA", "iec60870_asdu.bcr.ca", FT_BOOLEAN
, 8, TFS(&tfs_adjusted_not_adjusted
), 0x40,
3368 "Counter Adjusted", HFILL
}},
3371 { "IV", "iec60870_asdu.bcr.iv", FT_BOOLEAN
, 8, TFS(&tfs_invalid_valid
), 0x80,
3372 "Counter Validity", HFILL
}},
3375 { "START", "iec60870_asdu.start", FT_UINT8
, BASE_HEX
, NULL
, 0x0,
3378 { &hf_asdu_bitstring
,
3379 { "Value", "iec60870_asdu.bitstring", FT_UINT32
, BASE_HEX
, NULL
, 0x0,
3380 "BSI value", HFILL
}},
3383 { "Value", "iec60870_asdu.float", FT_FLOAT
, BASE_NONE
, NULL
, 0x0,
3384 "Float value", HFILL
}},
3387 { "Value", "iec60870_asdu.normval", FT_FLOAT
, BASE_NONE
, NULL
, 0x0,
3388 "Normalised value", HFILL
}},
3391 { "Value", "iec60870_asdu.scalval", FT_INT16
, BASE_DEC
, NULL
, 0x0,
3392 "Scaled value", HFILL
}},
3395 { "TSC", "iec60870_asdu.tsc", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
3396 "Test sequence counter", HFILL
}},
3398 { &hf_asdu_raw_data
,
3399 { "Raw Data", "iec60870_asdu.rawdata", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
3400 "Information object raw data", HFILL
}},
3402 { &hf_iec60870_segments
,
3403 { "ASDU Segments", "iec60870_asdu.segments", FT_NONE
, BASE_NONE
, NULL
, 0x0,
3406 { &hf_iec60870_segment
,
3407 { "ASDU Segment", "iec60870_asdu.segment", FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
3410 { &hf_iec60870_segment_overlap
,
3411 { "Segment overlap", "iec60870_asdu.segment.overlap", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
3412 "Segment overlaps with other segments", HFILL
}},
3414 { &hf_iec60870_segment_overlap_conflict
,
3415 { "Conflicting data in segment overlap", "iec60870_asdu.segment.overlap.conflict", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
3416 "Overlapping segments contained conflicting data", HFILL
}},
3418 { &hf_iec60870_segment_multiple_tails
,
3419 { "Multiple tail segments found", "iec60870_asdu.segment.multipletails", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
3420 "Several tails were found when reassembling the packet", HFILL
}},
3422 { &hf_iec60870_segment_too_long_segment
,
3423 { "Segment too long", "iec60870_asdu.segment.toolongsegment", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
3424 "Segment contained data past end of packet", HFILL
}},
3426 { &hf_iec60870_segment_error
,
3427 { "Reassembly error", "iec60870_asdu.segment.error", FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
3428 "Reassembly error due to illegal segments", HFILL
}},
3430 { &hf_iec60870_segment_count
,
3431 { "Segment count", "iec60870_asdu.segment.count", FT_UINT32
, BASE_DEC
, NULL
, 0x0,
3434 { &hf_iec60870_reassembled_in
,
3435 { "Reassembled ASDU in frame", "iec60870_asdu.reassembled_in", FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
3436 "This ASDU packet is reassembled in this frame", HFILL
}},
3438 { &hf_iec60870_reassembled_length
,
3439 { "Reassembled ASDU length", "iec60870_asdu.reassembled.length", FT_UINT32
, BASE_DEC
, NULL
, 0x0,
3440 "The total length of the reassembled payload", HFILL
}},
3444 static int *ett_as
[] = {
3461 &ett_iec60870_segment
,
3462 &ett_iec60870_segments
3465 static ei_register_info ei
[] = {
3466 { &ei_iec104_short_asdu
, { "iec104.short_asdu", PI_MALFORMED
, PI_ERROR
, "<ERR Short Asdu>", EXPFILL
}},
3467 { &ei_iec104_apdu_min_len
, { "iec104.apdu_min_len", PI_MALFORMED
, PI_ERROR
, "APDU less than bytes", EXPFILL
}},
3468 { &ei_iec104_apdu_invalid_len
, { "iec104.apdu_invalid_len", PI_MALFORMED
, PI_ERROR
, "Invalid ApduLen", EXPFILL
}},
3471 expert_module_t
* expert_iec60870
;
3473 proto_iec60870_asdu
= proto_register_protocol("IEC 60870-5-101/104 ASDU", "IEC 60870-5-101/104 ASDU", "iec60870_asdu");
3474 iec60870_asdu_handle
= register_dissector("iec60870_asdu", dissect_iec60870_asdu
, proto_iec60870_asdu
);
3476 /* Provide an alias to the previous name of this dissector */
3477 proto_register_alias(proto_iec60870_asdu
, "104asdu");
3479 proto_register_field_array(proto_iec60870_asdu
, hf_as
, array_length(hf_as
));
3480 proto_register_subtree_array(ett_as
, array_length(ett_as
));
3481 expert_iec60870
= expert_register_protocol(proto_iec60870_asdu
);
3482 expert_register_field_array(expert_iec60870
, ei
, array_length(ei
));
3484 reassembly_table_register(&iec60870_reassemble_table
, &addresses_ports_reassembly_table_functions
);
3487 /* The registration hand-off routine */
3489 proto_reg_handoff_iec60870_104(void)
3491 dissector_add_uint_with_preference("tcp.port", IEC104_PORT
, iec60870_104_handle
);
3494 /******************************************************************************************************/
3495 /* Return length of IEC 101 Protocol over TCP message (used for re-assembly) */
3496 /******************************************************************************************************/
3498 get_iec101_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset _U_
, void *data _U_
)
3501 unsigned len
=0, type
;
3502 type
= tvb_get_uint8(tvb
, offset
);
3505 case IEC101_SINGLE_CHAR
:
3508 case IEC101_FIXED_LEN
:
3509 len
= global_iec60870_link_addr_len
+ 4;
3511 case IEC101_VAR_LEN
:
3512 if (tvb_captured_length_remaining(tvb
, offset
) < 3) {
3513 /* We need another segment. */
3516 len
= tvb_get_uint8(tvb
, offset
+ 1) + 6;
3517 /* If the copy of the length or start byte is wrong,
3518 * we have errors. Take the entire remaining length
3519 * and the dissector will show the error. We'll try
3520 * to start a new PDU in a later packet. We can't
3521 * really reject the packet at this point.
3523 if (len
!= (unsigned)tvb_get_uint8(tvb
, offset
+ 2) + 6) {
3524 len
= tvb_reported_length_remaining(tvb
, offset
);
3526 if (tvb_get_uint8(tvb
, offset
+3) != IEC101_VAR_LEN
) {
3527 len
= tvb_reported_length_remaining(tvb
, offset
);
3535 /******************************************************************************************************/
3536 /* Dissect (and possibly Re-assemble) IEC 101 protocol payload data */
3537 /******************************************************************************************************/
3539 dissect_iec60870_101_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
3542 unsigned type
= tvb_get_uint8(tvb
, 0);
3544 /* Check that this is actually a IEC 60870-5-101 packet. */
3545 /* Note we are guaranteed to get one byte here, not necessarily more. */
3547 case IEC101_SINGLE_CHAR
:
3548 case IEC101_FIXED_LEN
:
3549 case IEC101_VAR_LEN
:
3550 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, 1, get_iec101_len
, dissect_iec60870_101
, data
);
3556 return tvb_captured_length(tvb
);
3559 /* The registration hand-off routine */
3561 proto_register_iec60870_101(void)
3563 /* IEC 101 Protocol header fields */
3564 static hf_register_info iec60870_101_hf
[] = {
3565 { &hf_iec60870_101_frame
,
3566 { "Frame Format", "iec60870_101.header", FT_UINT8
, BASE_HEX
, VALS(iec60870_101_frame_vals
), 0x0, NULL
, HFILL
}},
3567 { &hf_iec60870_101_length
,
3568 { "Length", "iec60870_101.length", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3569 { &hf_iec60870_101_num_user_octets
,
3570 { "Number of User Octets", "iec60870_101.num_user_octets", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3571 { &hf_iec60870_101_ctrlfield
,
3572 { "Control Field", "iec60870_101.ctrlfield", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
3573 { &hf_iec60870_101_ctrl_prm
,
3574 { "PRM", "iec60870_101.ctrl_prm", FT_UINT8
, BASE_DEC
, VALS(iec60870_101_ctrl_prm_values
), 0x40, "Primary Message", HFILL
}},
3575 { &hf_iec60870_101_ctrl_fcb
,
3576 { "FCB", "iec60870_101.ctrl_fcb", FT_UINT8
, BASE_DEC
, NULL
, 0x20, "Frame Count Bit", HFILL
}},
3577 { &hf_iec60870_101_ctrl_fcv
,
3578 { "FCV", "iec60870_101.ctrl_fcv", FT_UINT8
, BASE_DEC
, NULL
, 0x10, "Frame Count Bit Valid", HFILL
}},
3579 { &hf_iec60870_101_ctrl_dfc
,
3580 { "DFC", "iec60870_101.ctrl_dfc", FT_UINT8
, BASE_DEC
, NULL
, 0x10, "Data Flow Control", HFILL
}},
3581 { &hf_iec60870_101_ctrl_func_pri_to_sec
,
3582 { "CF Func Code", "iec60870_101.ctrl_func_pri_to_sec", FT_UINT8
, BASE_DEC
, VALS(iec60870_101_ctrl_func_pri_to_sec_values
), 0x0F, "Control Field Function Code, Pri to Sec", HFILL
}},
3583 { &hf_iec60870_101_ctrl_func_sec_to_pri
,
3584 { "CF Func Code", "iec60870_101.ctrl_func_sec_to_pri", FT_UINT8
, BASE_DEC
, VALS(iec60870_101_ctrl_func_sec_to_pri_values
), 0x0F, "Control Field Function Code, Sec to Pri", HFILL
}},
3585 { &hf_iec60870_101_linkaddr
,
3586 { "Data Link Address", "iec60870_101.linkaddr", FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3587 { &hf_iec60870_101_checksum
,
3588 { "Checksum", "iec60870_101.checksum", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
3589 { &hf_iec60870_101_stopchar
,
3590 { "Stop Character", "iec60870_101.stopchar", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
3594 /* Setup protocol subtree array */
3595 static int *ett_serial
[] = {
3597 &ett_iec60870_101_ctrlfield
,
3600 static ei_register_info ei_101
[] = {
3601 { &ei_iec101_frame_mismatch
, { "iec60870_101.header.mismatch", PI_MALFORMED
, PI_ERROR
, "Variable Length frames must have two matching start bytes (0x68)", EXPFILL
}},
3602 { &ei_iec101_length_mismatch
, { "iec60870_101.length.mismatch", PI_MALFORMED
, PI_ERROR
, "Variable Length frames must have two matching length bytes", EXPFILL
}},
3603 { &ei_iec101_stopchar_invalid
, { "iec60870_101.stopchar.invalid", PI_PROTOCOL
, PI_WARN
, "Stop character must be 0x16", EXPFILL
}},
3606 module_t
*iec60870_101_module
;
3607 expert_module_t
* expert_iec60870_101
;
3609 /* Register the protocol name and description */
3610 proto_iec60870_101
= proto_register_protocol("IEC 60870-5-101", "IEC 60870-5-101", "iec60870_101");
3612 /* Required function calls to register the header fields and subtrees used */
3613 proto_register_field_array(proto_iec60870_101
, iec60870_101_hf
, array_length(iec60870_101_hf
));
3614 proto_register_subtree_array(ett_serial
, array_length(ett_serial
));
3616 expert_iec60870_101
= expert_register_protocol(proto_iec60870_101
);
3617 expert_register_field_array(expert_iec60870_101
, ei_101
, array_length(ei_101
));
3619 iec60870_101_handle
= register_dissector("iec60870_101", dissect_iec60870_101_tcp
, proto_iec60870_101
);
3621 /* Register required preferences for IEC 101 configurable field lengths */
3622 iec60870_101_module
= prefs_register_protocol(proto_iec60870_101
, NULL
);
3624 static const enum_val_t link_addr_len
[] = {
3625 {"0", "0 octet", 0},
3626 {"1", "1 octet", 1},
3627 {"2", "2 octet", 2},
3631 static const enum_val_t cot_len
[] = {
3632 {"1", "1 octet", 1},
3633 {"2", "2 octet", 2},
3637 static const enum_val_t asdu_addr_len
[] = {
3638 {"1", "1 octet", 1},
3639 {"2", "2 octet", 2},
3643 static const enum_val_t asdu_ioa_len
[] = {
3644 {"1", "1 octet", 1},
3645 {"2", "2 octet", 2},
3646 {"3", "3 octet", 3},
3650 prefs_register_enum_preference(iec60870_101_module
, "linkaddr_len",
3651 "Length of the Link Address Field",
3652 "Length of the Link Address Field, configurable in '101 and absent in '104",
3653 &global_iec60870_link_addr_len
, link_addr_len
, false);
3655 prefs_register_enum_preference(iec60870_101_module
, "cot_len",
3656 "Length of the Cause of Transmission Field",
3657 "Length of the Cause of Transmission Field, configurable in '101 and fixed at 2 octets with '104",
3658 &global_iec60870_cot_len
, cot_len
, false);
3660 prefs_register_enum_preference(iec60870_101_module
, "asdu_addr_len",
3661 "Length of the Common ASDU Address Field",
3662 "Length of the Common ASDU Address Field, configurable in '101 and fixed at 2 octets with '104",
3663 &global_iec60870_asdu_addr_len
, asdu_addr_len
, false);
3665 prefs_register_enum_preference(iec60870_101_module
, "asdu_ioa_len",
3666 "Length of the Information Object Address Field",
3667 "Length of the Information Object Address Field, configurable in '101 and fixed at 3 octets with '104",
3668 &global_iec60870_ioa_len
, asdu_ioa_len
, false);
3673 proto_reg_handoff_iec60870_101(void)
3675 /* Add decode-as connection to determine user-customized TCP port */
3676 dissector_add_for_decode_as_with_preference("tcp.port", iec60870_101_handle
);
3677 /* Add dissection for serial pcap files generated by the RTAC */
3678 dissector_add_for_decode_as("rtacser.data", iec60870_101_handle
);
3681 /******************************************************************************************************/
3682 /* Code to dissect IEC 60870-5-103 Protocol packets */
3683 /******************************************************************************************************/
3685 dissect_iec60870_5_103(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void* data _U_
)
3687 /* Set up structures needed to add the protocol subtree and manage it */
3688 proto_item
*iec103_item
, *ctrlfield_item
;
3689 proto_tree
*iec103_tree
, *ctrlfield_tree
;
3690 uint8_t frametype
, ctrlfield_prm
, linkaddr
, asdu_type
, sq_num_obj
;
3694 /* Make entries in Protocol column on summary display */
3695 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "IEC 60870-5-103");
3696 col_clear(pinfo
->cinfo
, COL_INFO
);
3698 iec103_item
= proto_tree_add_item(tree
, proto_iec60870_5_103
, tvb
, 0, -1, ENC_NA
);
3699 iec103_tree
= proto_item_add_subtree(iec103_item
, ett_iec60870_5_103
);
3701 /* Add Frame Format to Protocol Tree */
3702 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_frame
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3703 frametype
= tvb_get_uint8(tvb
, 0);
3706 /* If this is a single character frame, there is nothing left to do... */
3707 if (frametype
== IEC103_SINGLE_CHAR
) {
3711 if (frametype
== IEC103_VAR_LEN
) {
3712 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_length
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3713 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_num_user_octets
, tvb
, offset
+1, 1, ENC_LITTLE_ENDIAN
);
3714 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_frame
, tvb
, offset
+2, 1, ENC_LITTLE_ENDIAN
);
3718 /* Fields common to both variable and fixed length frames */
3719 ctrlfield_item
= proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_ctrlfield
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3720 ctrlfield_tree
= proto_item_add_subtree(ctrlfield_item
, ett_iec60870_5_103_ctrlfield
);
3722 ctrlfield_prm
= tvb_get_uint8(tvb
, offset
) & 0x40;
3723 if (ctrlfield_prm
) {
3724 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ", ", "Pri->Sec");
3725 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_5_103_ctrl_prm
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3726 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_5_103_ctrl_fcb
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3727 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_5_103_ctrl_fcv
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3728 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_5_103_ctrl_func_pri_to_sec
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3731 col_append_sep_str(pinfo
->cinfo
, COL_INFO
, ", ", "Sec->Pri");
3732 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_5_103_ctrl_prm
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3733 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_5_103_ctrl_dfc
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3734 proto_tree_add_item(ctrlfield_tree
, hf_iec60870_5_103_ctrl_func_sec_to_pri
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3738 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_linkaddr
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3739 linkaddr
= tvb_get_uint8(tvb
, offset
);
3740 col_append_sep_fstr(pinfo
->cinfo
, COL_INFO
, NULL
, "Link Address: %d ", linkaddr
);
3743 /* If this is a variable length frame, we need to perform additional dissection */
3744 if (frametype
== IEC103_VAR_LEN
) {
3746 if (ctrlfield_prm
) {
3747 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_asdu_typeid_ctrl
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3748 asdu_type
= tvb_get_uint8(tvb
, offset
);
3751 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_asdu_typeid_mon
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3752 asdu_type
= tvb_get_uint8(tvb
, offset
);
3754 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_sq
, tvb
, offset
+1, 1, ENC_LITTLE_ENDIAN
);
3755 sq_num_obj
= tvb_get_uint8(tvb
, offset
+1) & 0x1F;
3757 if (ctrlfield_prm
) {
3758 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_cot_ctrl
, tvb
, offset
+2, 1, ENC_LITTLE_ENDIAN
);
3761 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_cot_mon
, tvb
, offset
+2, 1, ENC_LITTLE_ENDIAN
);
3764 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_asdu_address
, tvb
, offset
+3, 1, ENC_LITTLE_ENDIAN
);
3765 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_func_type
, tvb
, offset
+4, 1, ENC_LITTLE_ENDIAN
);
3766 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_info_num
, tvb
, offset
+5, 1, ENC_LITTLE_ENDIAN
);
3769 for(i
= 0; i
< sq_num_obj
; i
++) {
3770 /* Control Direction */
3771 if (ctrlfield_prm
) {
3772 switch (asdu_type
) {
3773 case 0x06: /* ASDU 6 - Time synchronization */
3774 get_CP56Time(tvb
, &offset
, iec103_tree
);
3776 case 0x07: /* ASDU 7 - General interrogation */
3777 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_scn
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3780 case 0x14: /* ASDU 20 - general command */
3781 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_dco
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3782 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_rii
, tvb
, offset
+1, 1, ENC_LITTLE_ENDIAN
);
3785 case 0x2d: /* ASDU 45 - Private, Areva Single command */
3786 case 0x2e: /* ASDU 46 - Private, Areva Double command */
3787 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_areva_cmd
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3792 /* Monitor Direction */
3794 switch (asdu_type
) {
3795 case 0x01: /* ASDU 1 - Time Tagged Message */
3796 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_dpi
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3798 get_CP32TimeA(tvb
, &offset
, iec103_tree
);
3799 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_sin
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3802 case 0x05: /* ASDU 5 - Identification */
3803 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_col
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3805 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_mfg
, tvb
, offset
, 8, ENC_ASCII
);
3807 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_mfg_sw
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
3810 case 0x06: /* ASDU 6 - Time synchronization */
3811 get_CP56Time(tvb
, &offset
, iec103_tree
);
3813 case 0x08: /* ASDU 8 - Termination of general interrogation */
3814 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_scn
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3817 case 0x09: /* ASDU 9 - Measurements II */
3818 get_NVA(tvb
, &offset
, iec103_tree
);
3820 case 0xcd: /* ASDU 205 - private, siemens energy counters */
3821 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_asdu205_value
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
3822 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_asdu205_ms
, tvb
, offset
+4, 2, ENC_LITTLE_ENDIAN
);
3823 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_asdu205_min
, tvb
, offset
+6, 1, ENC_LITTLE_ENDIAN
);
3824 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_asdu205_h
, tvb
, offset
+7, 1, ENC_LITTLE_ENDIAN
);
3833 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_checksum
, tvb
, offset
, 1, ENC_LITTLE_ENDIAN
);
3834 proto_tree_add_item(iec103_tree
, hf_iec60870_5_103_stopchar
, tvb
, offset
+1, 1, ENC_LITTLE_ENDIAN
);
3841 /******************************************************************************************************/
3842 /* Return length of IEC 103 Protocol over TCP message (used for re-assembly) */
3843 /******************************************************************************************************/
3845 get_iec103_len(packet_info
*pinfo _U_
, tvbuff_t
*tvb
, int offset _U_
, void *data _U_
)
3848 unsigned len
=0, type
;
3849 type
= tvb_get_uint8(tvb
, offset
);
3852 case IEC103_SINGLE_CHAR
:
3855 case IEC103_FIXED_LEN
:
3858 case IEC103_VAR_LEN
:
3859 len
= tvb_get_uint8(tvb
, offset
+1) + 6;
3866 /******************************************************************************************************/
3867 /* Dissect (and possibly Re-assemble) IEC 103 protocol payload data */
3868 /******************************************************************************************************/
3870 dissect_iec60870_5_103_tcp(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
3873 unsigned type
= tvb_get_uint8(tvb
, 0);
3875 /* Check that this is actually a IEC 60870-5-103 packet. */
3877 case IEC103_SINGLE_CHAR
:
3878 case IEC103_FIXED_LEN
:
3879 case IEC103_VAR_LEN
:
3880 tcp_dissect_pdus(tvb
, pinfo
, tree
, true, 1, get_iec103_len
, dissect_iec60870_5_103
, data
);
3886 return tvb_captured_length(tvb
);
3889 /* IEC 60870-5-103 Protocol registration hand-off routine */
3891 proto_register_iec60870_5_103(void)
3893 /* IEC 103 Protocol header fields */
3894 static hf_register_info iec60870_5_103_hf
[] = {
3895 { &hf_iec60870_5_103_areva_cmd
,
3896 { "Areva Command Code", "iec60870_5_103.areva_cmd", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
3897 { &hf_iec60870_5_103_asdu_address
,
3898 { "ASDU Common Address", "iec60870_5_103.asdu_address", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3899 { &hf_iec60870_5_103_asdu_typeid_ctrl
,
3900 { "ASDU Type ID (Ctrl Direction)", "iec60870_5_103.asdu_typeid_ctrl", FT_UINT8
, BASE_HEX
, VALS(iec103_asdu_types_control_dir
), 0x0, NULL
, HFILL
}},
3901 { &hf_iec60870_5_103_asdu_typeid_mon
,
3902 { "ASDU Type ID (Monitor Direction)", "iec60870_5_103.asdu_typeid_mon", FT_UINT8
, BASE_HEX
, VALS(iec103_asdu_types_monitor_dir
), 0x0, NULL
, HFILL
}},
3903 { &hf_iec60870_5_103_asdu205_ms
,
3904 { "Timestamp: Milliseconds", "iec60870_5_103.asdu205_ms", FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3905 { &hf_iec60870_5_103_asdu205_min
,
3906 { "Timestamp: Minutes", "iec60870_5_103.asdu205_min", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3907 { &hf_iec60870_5_103_asdu205_h
,
3908 { "Timestamp: Hours", "iec60870_5_103.asdu205_h", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3909 { &hf_iec60870_5_103_asdu205_value
,
3910 { "Counter Value", "iec60870_5_103.asdu205_value", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3911 { &hf_iec60870_5_103_checksum
,
3912 { "Checksum", "iec60870_5_103.checksum", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
3913 { &hf_iec60870_5_103_col
,
3914 { "Compatibility Level", "iec60870_5_103.col", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3915 { &hf_iec60870_5_103_cot_ctrl
,
3916 { "Cause of Transmission (Ctrl Direction)", "iec60870_5_103.cot_ctrl", FT_UINT8
, BASE_HEX
, VALS(iec60870_5_103_cot_ctrl_dir
), 0x0, NULL
, HFILL
}},
3917 { &hf_iec60870_5_103_cot_mon
,
3918 { "Cause of Transmission (Monitored Direction)", "iec60870_5_103.cot_mon", FT_UINT8
, BASE_HEX
, VALS(iec60870_5_103_cot_monitor_dir
), 0x0, NULL
, HFILL
}},
3919 { &hf_iec60870_5_103_cp32time2a
,
3920 { "CP32Time2a", "iec60870_5_103.cp32time2a", FT_ABSOLUTE_TIME
, ABSOLUTE_TIME_LOCAL
, NULL
, 0, NULL
, HFILL
}},
3921 { &hf_iec60870_5_103_cp32time2a_ms
,
3922 { "Milliseconds", "iec60870_5_103.cp32time2a_ms", FT_UINT16
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3923 { &hf_iec60870_5_103_cp32time2a_min
,
3924 { "Minutes", "iec60870_5_103.cp32time2a_min", FT_UINT8
, BASE_DEC
, NULL
, 0x3f, NULL
, HFILL
}},
3925 { &hf_iec60870_5_103_cp32time2a_res1
,
3926 { "Res1", "iec60870_5_103.cp32time2a_res1", FT_UINT8
, BASE_DEC
, NULL
, 0x40, NULL
, HFILL
}},
3927 { &hf_iec60870_5_103_cp32time2a_iv
,
3928 { "Invalid", "iec60870_5_103.cp32time2a_iv", FT_UINT8
, BASE_DEC
, NULL
, 0x80, NULL
, HFILL
}},
3929 { &hf_iec60870_5_103_cp32time2a_hr
,
3930 { "Hours", "iec60870_5_103.cp32time2a_hr", FT_UINT8
, BASE_DEC
, NULL
, 0x1f, NULL
, HFILL
}},
3931 { &hf_iec60870_5_103_cp32time2a_res2
,
3932 { "Res2", "iec60870_5_103.cp32time2a_res2", FT_UINT8
, BASE_DEC
, NULL
, 0x60, NULL
, HFILL
}},
3933 { &hf_iec60870_5_103_cp32time2a_sum
,
3934 { "Summer Time", "iec60870_5_103.cp32time2a_sum", FT_UINT8
, BASE_DEC
, NULL
, 0x80, NULL
, HFILL
}},
3935 { &hf_iec60870_5_103_ctrlfield
,
3936 { "Control Field", "iec60870_5_103.ctrlfield", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
3937 { &hf_iec60870_5_103_ctrl_prm
,
3938 { "PRM", "iec60870_5_103.ctrl_prm", FT_UINT8
, BASE_DEC
, VALS(iec60870_5_103_ctrl_prm_values
), 0x40, "Primary Message", HFILL
}},
3939 { &hf_iec60870_5_103_ctrl_fcb
,
3940 { "FCB", "iec60870_5_103.ctrl_fcb", FT_UINT8
, BASE_DEC
, NULL
, 0x20, "Frame Count Bit", HFILL
}},
3941 { &hf_iec60870_5_103_ctrl_fcv
,
3942 { "FCV", "iec60870_5_103.ctrl_fcv", FT_UINT8
, BASE_DEC
, NULL
, 0x10, "Frame Count Bit Valid", HFILL
}},
3943 { &hf_iec60870_5_103_ctrl_dfc
,
3944 { "DFC", "iec60870_5_103.ctrl_dfc", FT_UINT8
, BASE_DEC
, NULL
, 0x10, "Data Flow Control", HFILL
}},
3945 { &hf_iec60870_5_103_ctrl_func_pri_to_sec
,
3946 { "CF Func Code", "iec60870_5_103.ctrl_func_pri_to_sec", FT_UINT8
, BASE_DEC
, VALS(iec60870_5_103_ctrl_func_pri_to_sec_values
), 0x0F, "Control Field Function Code, Pri to Sec", HFILL
}},
3947 { &hf_iec60870_5_103_ctrl_func_sec_to_pri
,
3948 { "CF Func Code", "iec60870_5_103.ctrl_func_sec_to_pri", FT_UINT8
, BASE_DEC
, VALS(iec60870_5_103_ctrl_func_sec_to_pri_values
), 0x0F, "Control Field Function Code, Sec to Pri", HFILL
}},
3949 { &hf_iec60870_5_103_dco
,
3950 { "Double Command Type", "iec60870_5_103.dco", FT_UINT8
, BASE_DEC
, VALS(iec103_quadstate_types
), 0x0, NULL
, HFILL
}},
3951 { &hf_iec60870_5_103_dpi
,
3952 { "Double Point Information", "iec60870_5_103.dpi", FT_UINT8
, BASE_DEC
, VALS(iec103_quadstate_types
), 0x0, NULL
, HFILL
}},
3953 { &hf_iec60870_5_103_frame
,
3954 { "Frame Format", "iec60870_5_103.header", FT_UINT8
, BASE_HEX
, VALS(iec60870_5_103_frame_vals
), 0x0, NULL
, HFILL
}},
3955 { &hf_iec60870_5_103_func_type
,
3956 { "Function Type", "iec60870_5_103.func_type", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3957 { &hf_iec60870_5_103_info_num
,
3958 { "Information Number", "iec60870_5_103.info_num", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3959 { &hf_iec60870_5_103_length
,
3960 { "Length", "iec60870_5_103.length", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3961 { &hf_iec60870_5_103_linkaddr
,
3962 { "Data Link Address", "iec60870_5_103.linkaddr", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3963 { &hf_iec60870_5_103_mfg
,
3964 { "Manufacturer Identity", "iec60870_5_103.mfg", FT_STRING
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
3965 { &hf_iec60870_5_103_mfg_sw
,
3966 { "Manufacturer's Software Identification", "iec60870_5_103.mfg_sw", FT_UINT32
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3967 { &hf_iec60870_5_103_num_user_octets
,
3968 { "Number of User Octets", "iec60870_5_103.num_user_octets", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3969 { &hf_iec60870_5_103_rii
,
3970 { "Return Information Identifier", "iec60870_5_103.rii", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3971 { &hf_iec60870_5_103_scn
,
3972 { "Scan Number", "iec60870_5_103.scn", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3973 { &hf_iec60870_5_103_sin
,
3974 { "Supplementary Information", "iec60870_5_103.sin", FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
3975 { &hf_iec60870_5_103_sq
,
3976 { "Structured Qualifier", "iec60870_5_103.sq", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
3977 { &hf_iec60870_5_103_stopchar
,
3978 { "Stop Character", "iec60870_5_103.stopchar", FT_UINT8
, BASE_HEX
, NULL
, 0x0, NULL
, HFILL
}},
3981 /* Setup protocol subtree array */
3982 static int *ett_serial
[] = {
3983 &ett_iec60870_5_103
,
3984 &ett_iec60870_5_103_ctrlfield
,
3985 &ett_iec60870_5_103_cp32time2a
,
3988 /* Register the protocol name and description */
3989 proto_iec60870_5_103
= proto_register_protocol("IEC 60870-5-103", "IEC 60870-5-103", "iec60870_5_103");
3991 /* Required function calls to register the header fields and subtrees used */
3992 proto_register_field_array(proto_iec60870_5_103
, iec60870_5_103_hf
, array_length(iec60870_5_103_hf
));
3993 proto_register_subtree_array(ett_serial
, array_length(ett_serial
));
3995 iec60870_5_103_handle
= register_dissector("iec60870_5_103", dissect_iec60870_5_103_tcp
, proto_iec60870_5_103
);
3999 proto_reg_handoff_iec60870_5_103(void)
4001 /* Add decode-as connection to determine user-customized TCP port */
4002 dissector_add_for_decode_as_with_preference("tcp.port", iec60870_5_103_handle
);
4003 /* Add dissection for serial pcap files generated by the RTAC */
4004 dissector_add_for_decode_as("rtacser.data", iec60870_5_103_handle
);
4009 * Editor modelines - https://www.wireshark.org/tools/modelines.html
4014 * indent-tabs-mode: t
4017 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
4018 * :indentSize=8:tabSize=8:noTabs=false: