Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-iec104.c
blobdf2cf2ba01817abdba75b92b5790183c4cbaed3c
1 /* packet-iec104.c
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
18 #include "config.h"
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>
26 #include <epan/tfs.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 */
52 struct asduheader {
53 uint32_t Addr;
54 uint8_t OA;
55 uint8_t TypeId;
56 uint8_t TNCause;
57 uint32_t IOA;
58 uint8_t NumIx;
59 uint8_t SQ;
60 uint8_t DataLength;
63 struct asdu_parms {
64 unsigned cot_len;
65 unsigned asdu_addr_len;
66 unsigned ioa_len;
69 /* ASDU command value/status structure */
70 typedef struct {
71 bool OFF;
72 bool ON;
74 bool UP;
75 bool DOWN;
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) */
84 } td_CmdInfo;
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
96 #define APCI_LEN 6
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
104 #define F_TEST 0x80
105 #define F_NEGA 0x40
106 #define F_CAUSE 0x3F
107 #define F_SQ 0x80
109 /* APCI types */
111 /* Type I is only lowest bit set to 0 */
112 #define I_TYPE 0
113 #define S_TYPE 1
114 #define U_TYPE 3
115 #define APCI_TYPE_UNKNOWN 4
117 static const value_string apci_types [] = {
118 { I_TYPE, "I" },
119 { S_TYPE, "S" },
120 { U_TYPE, "U" },
121 { 0, NULL }
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" },
139 { 0, NULL }
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" },
296 { 0, NULL }
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" },
376 { 0, NULL }
379 typedef struct {
380 uint8_t value;
381 uint8_t length;
382 } td_asdu_length;
384 static const td_asdu_length asdu_length [] = {
385 { M_SP_NA_1, 1 },
386 { M_SP_TA_1, 4 },
387 { M_DP_NA_1, 1 },
388 { M_DP_TA_1, 4 },
389 { M_ST_NA_1, 2 },
390 { M_ST_TA_1, 5 },
391 { M_BO_NA_1, 5 },
392 { M_BO_TA_1, 8 },
393 { M_ME_NA_1, 3 },
394 { M_ME_TA_1, 6 },
395 { M_ME_NB_1, 3 },
396 { M_ME_TB_1, 6 },
397 { M_ME_NC_1, 5 },
398 { M_ME_TC_1, 8 },
399 { M_IT_NA_1, 5 },
400 { M_IT_TA_1, 8 },
401 { M_PS_NA_1, 5 },
402 { M_ME_ND_1, 2 },
403 { M_SP_TB_1, 8 },
404 { M_DP_TB_1, 8 },
405 { M_ST_TB_1, 9 },
406 { M_BO_TB_1, 12 },
407 { M_ME_TD_1, 10 },
408 { M_ME_TE_1, 10 },
409 { M_ME_TF_1, 12 },
410 { M_IT_TB_1, 12 },
411 { M_EP_TD_1, 10 },
412 { M_EP_TE_1, 11 },
413 { M_EP_TF_1, 11 },
414 { S_IT_TC_1, 14 },
415 { C_SC_NA_1, 1 },
416 { C_DC_NA_1, 1 },
417 { C_RC_NA_1, 1 },
418 { C_SE_NA_1, 3 },
419 { C_SE_NB_1, 3 },
420 { C_SE_NC_1, 5 },
421 { C_BO_NA_1, 4 },
422 { C_SC_TA_1, 8 },
423 { C_DC_TA_1, 8 },
424 { C_RC_TA_1, 8 },
425 { C_SE_TA_1, 10 },
426 { C_SE_TB_1, 10 },
427 { C_SE_TC_1, 12 },
428 { C_BO_TA_1, 11 },
429 { M_EI_NA_1, 1 },
430 { S_CH_NA_1, 0 },
431 { S_RP_NA_1, 0 },
432 { S_AR_NA_1, 0 },
433 { S_KR_NA_1, 0 },
434 { S_KS_NA_1, 0 },
435 { S_KC_NA_1, 0 },
436 { S_ER_NA_1, 0 },
437 { S_US_NA_1, 0 },
438 { S_UQ_NA_1, 0 },
439 { S_UR_NA_1, 0 },
440 { S_UK_NA_1, 0 },
441 { S_UA_NA_1, 0 },
442 { S_UC_NA_1, 0 },
443 { C_IC_NA_1, 1 },
444 { C_CI_NA_1, 1 },
445 { C_RD_NA_1, 0 },
446 { C_CS_NA_1, 7 },
447 { C_RP_NA_1, 1 },
448 { C_TS_TA_1, 9 },
449 { P_ME_NA_1, 3 },
450 { P_ME_NB_1, 3 },
451 { P_ME_NC_1, 5 },
452 { P_AC_NA_1, 1 },
453 { F_FR_NA_1, 6 },
454 { F_SR_NA_1, 7 },
455 { F_SC_NA_1, 4 },
456 { F_LS_NA_1, 5 },
457 { F_AF_NA_1, 4 },
458 { F_SG_NA_1, 0 },
459 { F_DR_TA_1, 13 },
460 { F_SC_NB_1, 16 },
461 { 0, 0 }
464 /* Cause of Transmission (CauseTx) */
465 #define Per_Cyc 1
466 #define Back 2
467 #define Spont 3
468 #define Init 4
469 #define Req 5
470 #define Act 6
471 #define ActCon 7
472 #define Deact 8
473 #define DeactCon 9
474 #define ActTerm 10
475 #define Retrem 11
476 #define Retloc 12
477 #define File 13
478 #define Auth 14
479 #define Seskey 15
480 #define Usrkey 16
481 #define Inrogen 20
482 #define Inro1 21
483 #define Inro2 22
484 #define Inro3 23
485 #define Inro4 24
486 #define Inro5 25
487 #define Inro6 26
488 #define Inro7 27
489 #define Inro8 28
490 #define Inro9 29
491 #define Inro10 30
492 #define Inro11 31
493 #define Inro12 32
494 #define Inro13 33
495 #define Inro14 34
496 #define Inro15 35
497 #define Inro16 36
498 #define Reqcogen 37
499 #define Reqco1 38
500 #define Reqco2 39
501 #define Reqco3 40
502 #define Reqco4 41
503 #define UkTypeId 44
504 #define UkCauseTx 45
505 #define UkComAdrASDU 46
506 #define UkIOA 47
508 static const value_string causetx_types [] = {
509 { Per_Cyc ,"Per/Cyc" },
510 { Back ,"Back" },
511 { Spont ,"Spont" },
512 { Init ,"Init" },
513 { Req ,"Req" },
514 { Act ,"Act" },
515 { ActCon ,"ActCon" },
516 { Deact ,"Deact" },
517 { DeactCon ,"DeactCon" },
518 { ActTerm ,"ActTerm" },
519 { Retrem ,"Retrem" },
520 { Retloc ,"Retloc" },
521 { File ,"File" },
522 { Auth ,"Auth" },
523 { Seskey ,"Seskey" },
524 { Usrkey ,"Usrkey" },
525 { Inrogen ,"Inrogen" },
526 { Inro1 ,"Inro1" },
527 { Inro2 ,"Inro2" },
528 { Inro3 ,"Inro3" },
529 { Inro4 ,"Inro4" },
530 { Inro5 ,"Inro5" },
531 { Inro6 ,"Inro6" },
532 { Inro7 ,"Inro7" },
533 { Inro8 ,"Inro8" },
534 { Inro9 ,"Inro9" },
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" },
550 { UkIOA ,"UkIOA" },
551 { 0, NULL }
554 static const value_string diq_types[] = {
555 { 0, "Indeterminate or Intermediate" },
556 { 1, "OFF" },
557 { 2, "ON" },
558 { 3, "Indeterminate" },
559 { 0, NULL }
562 static const value_string qos_qu_types[] = {
563 { 0, "No pulse defined" },
564 { 1, "Short Pulse" },
565 { 2, "Long Pulse" },
566 { 3, "Persistent Output" },
567 { 0, NULL }
570 static const value_string dco_on_types[] = {
571 { 0, "(None)" },
572 { 1, "OFF" },
573 { 2, "ON" },
574 { 3, "Error: On/Off not defined" },
575 { 0, NULL }
578 static const value_string rco_up_types[] = {
579 { 0, "(None)" },
580 { 1, "DOWN" },
581 { 2, "UP" },
582 { 3, "Error: Up/Down not defined" },
583 { 0, NULL }
586 static const value_string qpm_kpa_types[] = {
587 { 0, "Not used" },
588 { 1, "Threshold value" },
589 { 2, "Smoothing factor (filter time constant)" },
590 { 0, NULL }
593 static const value_string qpm_lpc_types[] = {
594 { 0, "No change" },
595 { 1, "Change" },
596 { 0, NULL }
599 static const value_string qpm_pop_types[] = {
600 { 0, "Operation" },
601 { 1, "Not in operation" },
602 { 0, NULL }
605 static const value_string coi_r_types[] = {
606 { 0, "Local power switch on" },
607 { 1, "Local manual reset" },
608 { 2, "Remote reset" },
609 { 0, NULL }
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" },
631 { 0, NULL }
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" },
641 { 0, NULL }
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" },
649 { 0, NULL }
652 static const value_string qrp_r_types[] = {
653 { 0, "Not used" },
654 { 1, "General reset of process" },
655 { 2, "Reset of pending information with time tag of the event buffer" },
656 { 0, NULL }
659 static const range_string usr_types[] = {
660 { 0, 0, "(Unknown)" },
661 { 1, 1, "Default" },
662 { 2, 65535, "Chosen by the controlling station" },
663 { 0, 0, NULL }
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)" },
673 { 0, 0, NULL }
676 static const value_string rsc_types[] = {
677 { 0, "(not used)" },
678 { 1, "CRITICAL" },
679 { 0, NULL }
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)" },
687 { 0, 0, NULL }
690 static const value_string kst_types[] = {
691 { 0, "(not used)" },
692 { 1, "OK" },
693 { 2, "NOT INIT" },
694 { 3, "COMM FAIL" },
695 { 4, "AUTH FAIL" },
696 { 0, NULL }
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)" },
706 { 0, 0, NULL }
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)"},
723 { 0, 0, NULL }
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;
750 static int hf_addr;
751 static int hf_oa;
752 static int hf_typeid;
753 static int hf_causetx;
754 static int hf_nega;
755 static int hf_test;
756 static int hf_ioa;
757 static int hf_numix;
758 static int hf_sq;
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;
774 static int hf_siq;
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;
780 static int hf_diq;
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;
786 static int hf_qds;
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;
792 static int hf_vti;
793 static int hf_vti_v;
794 static int hf_vti_t;
795 static int hf_qos;
796 static int hf_qos_ql;
797 static int hf_qos_se;
798 static int hf_sco;
799 static int hf_sco_on;
800 static int hf_sco_qu;
801 static int hf_sco_se;
802 static int hf_dco;
803 static int hf_dco_on;
804 static int hf_dco_qu;
805 static int hf_dco_se;
806 static int hf_rco;
807 static int hf_rco_up;
808 static int hf_rco_qu;
809 static int hf_rco_se;
810 static int hf_qpm;
811 static int hf_qpm_kpa;
812 static int hf_qpm_lpc;
813 static int hf_qpm_pop;
814 static int hf_asn;
815 static int hf_usr;
816 static int hf_iec60870_segment_data;
817 static int hf_mal;
818 static int hf_rsc;
819 static int hf_asn_fin;
820 static int hf_asn_fir;
821 static int hf_csq;
822 static int hf_ksq;
823 static int hf_kwa;
824 static int hf_kst;
825 static int hf_hln;
826 static int hf_hal;
827 static int hf_cln;
828 static int hf_wkl;
829 static int hf_prcd_raw_data;
830 static int hf_hmac_raw_data;
831 static int hf_wkd_raw_data;
832 static int hf_aid;
833 static int hf_err;
834 static int hf_etm;
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;
844 static int hf_eln;
845 static int hf_error_text;
846 static int hf_coi;
847 static int hf_coi_r;
848 static int hf_coi_i;
849 static int hf_qoi;
850 static int hf_qcc;
851 static int hf_qcc_rqt;
852 static int hf_qcc_frz;
853 static int hf_qrp;
854 static int hf_bcr;
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;
860 static int hf_start;
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;
869 static int ett_apci;
870 static int ett_asdu;
871 static int ett_asdu_objects;
872 static int ett_siq;
873 static int ett_diq;
874 static int ett_vti;
875 static int ett_qds;
876 static int ett_qos;
877 static int ett_sco;
878 static int ett_dco;
879 static int ett_rco;
880 static int ett_qpm;
881 static int ett_coi;
882 static int ett_qcc;
883 static int ett_cp24time;
884 static int ett_cp56time;
885 static int ett_etm;
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;
894 /* IEC 101 stuff */
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 */
935 NULL,
936 "segments"
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;
947 /* Frame Format */
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" },
957 { 0, NULL }
960 static const value_string iec60870_101_ctrl_prm_values[] = {
961 { 0, "Message from Secondary (Responding) Station" },
962 { 1, "Message from Primary (Initiating) Station" },
963 { 0, NULL }
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" },
970 { 3, "User Data" },
971 { 4, "User Data" },
972 { 5, "Reserved" },
973 { 6, "Reserved" },
974 { 7, "Reserved" },
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" },
979 { 12, "Reserved" },
980 { 13, "Reserved" },
981 { 14, "Reserved" },
982 { 15, "Reserved" },
983 { 0, NULL }
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" },
989 { 2, "Reserved" },
990 { 3, "Reserved" },
991 { 4, "Reserved" },
992 { 5, "Reserved" },
993 { 6, "Reserved" },
994 { 7, "Reserved" },
995 { 8, "User Data" },
996 { 9, "NACK: Requested Data not Available" },
997 { 10, "Reserved" },
998 { 11, "Status of Link" },
999 { 12, "Reserved" },
1000 { 13, "Reserved" },
1001 { 14, "Link Service not Functioning" },
1002 { 15, "Link Service not Implemented" },
1003 { 0, NULL }
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;
1056 /* Frame Format */
1057 #define IEC103_VAR_LEN 0x68
1058 #define IEC103_FIXED_LEN 0x10
1059 #define IEC103_SINGLE_CHAR 0xE5
1061 /* Frame Format */
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" },
1066 { 0, NULL }
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" },
1072 { 0, NULL }
1075 static const value_string iec60870_5_103_ctrl_func_pri_to_sec_values[] = {
1076 { 0, "Reset of Communications Unit" },
1077 { 1, "Reserved" },
1078 { 2, "Reserved" },
1079 { 3, "Send / Confirm Expected" },
1080 { 4, "Send / No Confirm Expected" },
1081 { 5, "Reserved" },
1082 { 6, "Reserved" },
1083 { 7, "Reset Frame Count Bit" },
1084 { 8, "Reserved" },
1085 { 9, "Request Status of Link" },
1086 { 10, "Request User Data Class 1" },
1087 { 11, "Request User Data Class 2" },
1088 { 12, "Reserved" },
1089 { 13, "Reserved" },
1090 { 14, "Reserved" },
1091 { 15, "Reserved" },
1092 { 0, NULL }
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" },
1098 { 2, "Reserved" },
1099 { 3, "Reserved" },
1100 { 4, "Reserved" },
1101 { 5, "Reserved" },
1102 { 6, "Reserved" },
1103 { 7, "Reserved" },
1104 { 8, "ACK: User Data" },
1105 { 9, "NACK: Requested Data not Available" },
1106 { 10, "Reserved" },
1107 { 11, "Status of Link" },
1108 { 12, "Reserved" },
1109 { 13, "Reserved" },
1110 { 14, "Link Service not Functioning" },
1111 { 15, "Link Service not Implemented" },
1112 { 0, NULL }
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" },
1127 { 12, "reserved" },
1128 { 13, "reserved" },
1129 { 14, "reserved" },
1130 { 15, "reserved" },
1131 { 16, "reserved" },
1132 { 17, "reserved" },
1133 { 18, "reserved" },
1134 { 19, "reserved" },
1135 { 20, "reserved" },
1136 { 21, "reserved" },
1137 { 22, "reserved" },
1138 { 23, "List of recorded disturbances" },
1139 { 24, "reserved" },
1140 { 25, "reserved" },
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 */
1148 { 0, NULL }
1151 /* IEC 60870-5-103 ASDU types (TypeId); control direction */
1152 static const value_string iec103_asdu_types_control_dir [] = {
1153 { 1, "reserved" },
1154 { 2, "reserved" },
1155 { 3, "reserved" },
1156 { 4, "reserved" },
1157 { 5, "reserved" },
1158 { 6, "Time synchronization" }, /* dissection implemented */
1159 { 7, "General interrogation" }, /* dissection implemented */
1160 { 8, "reserved" },
1161 { 9, "reserved" },
1162 { 10, "Generic data" },
1163 { 11, "reserved" },
1164 { 12, "reserved" },
1165 { 13, "reserved" },
1166 { 14, "reserved" },
1167 { 15, "reserved" },
1168 { 16, "reserved" },
1169 { 17, "reserved" },
1170 { 18, "reserved" },
1171 { 19, "reserved" },
1172 { 20, "General command" }, /* dissection implemented */
1173 { 21, "Generic command" },
1174 { 22, "reserved" },
1175 { 23, "reserved" },
1176 { 24, "Order for disturbance data transmission" },
1177 { 25, "Acknowledgement for disturbance data transmission" },
1178 { 26, "reserved" },
1179 { 27, "reserved" },
1180 { 28, "reserved" },
1181 { 29, "reserved" },
1182 { 30, "reserved" },
1183 { 31, "reserved" },
1184 { 45, "Private, Areva Single Command" }, /* dissection implemented */
1185 { 46, "Private, Areva Double Command" }, /* dissection implemented */
1186 { 0, NULL }
1189 static const value_string iec60870_5_103_cot_monitor_dir [] = {
1190 { 1, "Spontaneous" },
1191 { 2, "Cyclic" },
1192 { 3, "Reset frame count bit (FCB)" },
1193 { 4, "Reset communication unit (CU)" },
1194 { 5, "Start / restart" },
1195 { 6, "Power on" },
1196 { 7, "Test mode" },
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" },
1210 { 0, NULL }
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" },
1220 { 0, NULL }
1224 static const value_string iec103_quadstate_types[] = {
1225 { 0, "Not used" },
1226 { 1, "OFF" },
1227 { 2, "ON" },
1228 { 3, "Not used" },
1229 { 0, NULL }
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)
1240 uint16_t ms;
1241 uint8_t min;
1242 nstime_t nstime;
1243 proto_item* ti;
1244 proto_tree* cp24time_tree;
1246 ms = tvb_get_letohs(tvb, *offset);
1247 nstime.nsecs = (ms % 1000) * 1000000;
1248 nstime.secs = ms / 1000;
1249 (*offset) += 2;
1251 min = tvb_get_uint8(tvb, *offset);
1252 nstime.secs += (min & 0x3F) * 60;
1253 (*offset)++;
1255 (*offset) -= 3;
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);
1261 (*offset) += 2;
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);
1265 (*offset) ++;
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)
1273 uint16_t ms;
1274 uint8_t value;
1275 nstime_t datetime;
1276 struct tm tm = {0};
1277 proto_item* ti;
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 */
1291 tm.tm_mday = 1;
1292 tm.tm_mon = 0;
1293 tm.tm_year = 100;
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);
1308 (*offset) += 4;
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)
1317 uint16_t ms;
1318 uint8_t value;
1319 uint8_t su;
1320 struct tm tm;
1321 nstime_t datetime;
1322 proto_item* ti;
1323 proto_tree* cp56time_tree;
1325 ms = tvb_get_letohs(tvb, *offset);
1326 tm.tm_sec = ms / 1000;
1327 datetime.nsecs = (ms % 1000) * 1000000;
1328 (*offset) += 2;
1330 value = tvb_get_uint8(tvb, *offset);
1331 tm.tm_min = value & 0x3F;
1332 (*offset)++;
1334 value = tvb_get_uint8(tvb, *offset);
1335 tm.tm_hour = value & 0x1F;
1336 su = value & 0x80;
1337 (*offset)++;
1339 value = tvb_get_uint8(tvb, *offset);
1340 tm.tm_mday = value & 0x1F;
1341 (*offset)++;
1343 value = tvb_get_uint8(tvb, *offset);
1344 tm.tm_mon = (value & 0x0F) - 1;
1345 (*offset)++;
1347 value = tvb_get_uint8(tvb, *offset);
1348 tm.tm_year = value & 0x7F;
1349 if (tm.tm_year < 70)
1350 tm.tm_year += 100;
1352 (*offset)++;
1354 if (su)
1355 tm.tm_isdst = 1;
1356 else
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);
1362 (*offset) -= 7;
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);
1368 (*offset) += 2;
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);
1373 (*offset) ++;
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);
1377 (*offset) ++;
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);
1381 (*offset) ++;
1383 proto_tree_add_item(cp56time_tree, hf_cp56time_month, tvb, *offset, 1, ENC_LITTLE_ENDIAN);
1384 (*offset) ++;
1386 proto_tree_add_item(cp56time_tree, hf_cp56time_year, tvb, *offset, 1, ENC_LITTLE_ENDIAN);
1387 (*offset) ++;
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;
1403 return ti;
1406 /* ====================================================================
1407 TypeId length
1408 ==================================================================== */
1409 static uint8_t get_TypeIdLength(uint8_t TypeId)
1411 uint8_t ret = 0;
1412 const td_asdu_length *item;
1414 item = asdu_length;
1415 while (item->value)
1417 if (item->value == TypeId)
1419 ret = item->length;
1420 break;
1422 item++;
1425 return ret;
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)
1433 proto_item* ti;
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);
1445 (*offset)++;
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)
1453 proto_item* ti;
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);
1465 (*offset)++;
1468 /* ====================================================================
1469 QDS: Quality descriptor (separate octet)
1470 ==================================================================== */
1471 static void get_QDS(tvbuff_t *tvb, uint8_t *offset, proto_tree *iec104_header_tree)
1473 proto_item* ti;
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);
1485 (*offset)++;
1488 /* ====================================================================
1489 QDP: Quality descriptor for events of protection equipment
1490 (separate octet)
1491 ==================================================================== */
1492 #if 0
1493 static void get_QDP(tvbuff_t *tvb _U_, uint8_t *offset _U_, proto_tree *iec104_header_tree _U_)
1495 /* todo */
1498 #endif
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)
1505 proto_item* ti;
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);
1514 (*offset)++;
1517 /* ====================================================================
1518 NVA: Normalized value
1519 ==================================================================== */
1520 static void get_NVA(tvbuff_t *tvb, uint8_t *offset, proto_tree *iec104_header_tree)
1522 int16_t value;
1523 float fvalue;
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);
1531 (*offset) += 2;
1534 static void get_NVAspt(tvbuff_t *tvb, uint8_t *offset, proto_tree *iec104_header_tree)
1536 int16_t value;
1537 float fvalue;
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);
1545 (*offset) += 2;
1548 /* ====================================================================
1549 SVA: Scaled value
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);
1556 (*offset) += 2;
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);
1564 (*offset) += 2;
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);
1574 (*offset) += 2;
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);
1585 (*offset) += 4;
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);
1593 (*offset) += 4;
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);
1603 (*offset) += 4;
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);
1610 (*offset) += 4;
1613 /* ====================================================================
1614 BCR: Binary counter reading
1615 ==================================================================== */
1616 static void get_BCR(tvbuff_t *tvb, uint8_t *offset, proto_tree *iec104_header_tree)
1618 proto_item* ti;
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);
1625 (*offset) += 4;
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);
1631 (*offset)++;
1634 /* ====================================================================
1635 todo -- SEP: Single event of protection equipment
1636 ==================================================================== */
1637 #if 0
1638 static void get_SEP(tvbuff_t *tvb _U_, uint8_t *offset _U_, proto_tree *iec104_header_tree _U_)
1640 /* todo */
1643 #endif
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)
1650 proto_item* ti;
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);
1659 (*offset)++;
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)
1667 proto_item* ti;
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);
1677 (*offset)++;
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)
1685 proto_item* ti;
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);
1695 (*offset)++;
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)
1703 proto_item* ti;
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);
1713 (*offset)++;
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)
1721 proto_item* ti;
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);
1731 (*offset)++;
1734 /* ====================================================================
1735 USR: User Number
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);
1741 (*offset) += 2;
1744 /* ====================================================================
1745 MAL: MAC algorithm
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);
1751 (*offset)++;
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);
1761 (*offset)++;
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);
1771 (*offset) += 4;
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);
1781 (*offset) += 4;
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);
1791 (*offset)++;
1794 /* ====================================================================
1795 KST: Key status
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);
1801 (*offset)++;
1804 /* ====================================================================
1805 HLN: MAC length
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);
1813 (*offset) += 2;
1814 return value;
1817 /* ====================================================================
1818 HAL: MAC algorithm
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);
1825 (*offset)++;
1826 switch (hal)
1828 case 3: /* HMAC-SHA-256 truncated to 8 octets (serial) */
1829 return 8;
1830 case 4: /* HMAC-SHA-256 truncated to 16 octets (networked) */
1831 return 16;
1832 case 6: /* AES-GMAC (output is 12 octets) */
1833 return 12;
1835 return 0;
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);
1847 (*offset) += 2;
1848 return value;
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);
1860 (*offset) += 2;
1861 return value;
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 /* ====================================================================
1874 MAC value
1875 ==================================================================== */
1876 static void get_HMAC(tvbuff_t *tvb, uint8_t *offset, proto_tree *iec104_header_tree, int length)
1878 if (length)
1880 proto_tree_add_item(iec104_header_tree, hf_hmac_raw_data, tvb, *offset, length, ENC_NA);
1881 (*offset) += length;
1885 /* ====================================================================
1886 Wrapped key data
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 /* ====================================================================
1895 AID: Association ID
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);
1901 (*offset) += 2;
1904 /* ====================================================================
1905 ERR: Error code
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);
1911 (*offset)++;
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)
1919 uint16_t ms;
1920 uint8_t value;
1921 uint8_t su;
1922 struct tm tm;
1923 nstime_t datetime;
1924 proto_item* ti;
1925 proto_tree* etm_tree;
1927 ms = tvb_get_letohs(tvb, *offset);
1928 tm.tm_sec = ms / 1000;
1929 datetime.nsecs = (ms % 1000) * 1000000;
1930 (*offset) += 2;
1932 value = tvb_get_uint8(tvb, *offset);
1933 tm.tm_min = value & 0x3F;
1934 (*offset)++;
1936 value = tvb_get_uint8(tvb, *offset);
1937 tm.tm_hour = value & 0x1F;
1938 su = value & 0x80;
1939 (*offset)++;
1941 value = tvb_get_uint8(tvb, *offset);
1942 tm.tm_mday = value & 0x1F;
1943 (*offset)++;
1945 value = tvb_get_uint8(tvb, *offset);
1946 tm.tm_mon = (value & 0x0F) - 1;
1947 (*offset)++;
1949 value = tvb_get_uint8(tvb, *offset);
1950 tm.tm_year = value & 0x7F;
1951 if (tm.tm_year < 70)
1952 tm.tm_year += 100;
1954 (*offset)++;
1956 if (su)
1957 tm.tm_isdst = 1;
1958 else
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);
1964 (*offset) -= 7;
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);
1970 (*offset) += 2;
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);
1974 (*offset)++;
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);
1978 (*offset)++;
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);
1982 (*offset)++;
1984 proto_tree_add_item(etm_tree, hf_etm_month, tvb, *offset, 1, ENC_LITTLE_ENDIAN);
1985 (*offset)++;
1987 proto_tree_add_item(etm_tree, hf_etm_year, tvb, *offset, 1, ENC_LITTLE_ENDIAN);
1988 (*offset)++;
1991 /* ====================================================================
1992 ELN: Error length
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);
1999 (*offset) += 2;
2000 return value;
2003 /* ====================================================================
2004 Error text
2005 ==================================================================== */
2006 static void get_ErrorText(tvbuff_t *tvb, uint8_t *offset, proto_tree *iec104_header_tree, int length)
2008 if (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)
2020 proto_item* ti;
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);
2029 (*offset)++;
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);
2039 (*offset)++;
2042 /* ====================================================================
2043 QCC: Qualifier of counter interrogation
2044 ==================================================================== */
2045 static void get_QCC(tvbuff_t *tvb, uint8_t *offset, proto_tree *iec104_header_tree)
2047 proto_item* ti;
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);
2056 (*offset)++;
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);
2066 (*offset)++;
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_)
2076 uint8_t Val;
2077 uint32_t Off;
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);
2094 uint8_t offset = 0;
2095 uint8_t i, encapsulated_type, encapsulated_length;
2096 uint16_t j;
2097 tvbuff_t *encapsulated_tvb = NULL;
2099 switch (type_id) {
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);
2107 break;
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);
2113 break;
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));
2123 break;
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);
2133 break;
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);
2139 break;
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);
2148 break;
2149 default:
2150 proto_tree_add_item(it_segment_tree, hf_ioa, tvb, offset, 3, ENC_LITTLE_ENDIAN);
2151 offset += 3;
2153 if (Len - offset > 0)
2154 proto_tree_add_item(it_segment_tree, hf_asdu_raw_data, tvb, offset, Len - offset, ENC_NA);
2155 offset = Len;
2157 break;
2159 /* check correct apdu length */
2160 if (Len != offset) {
2161 expert_add_info(pinfo, it_segment_tree, &ei_iec104_apdu_invalid_len);
2162 return offset;
2165 return 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);
2180 (*offset)++;
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"));
2187 } else {
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);
2225 uint8_t Bytex;
2226 const char *cause_str;
2227 size_t Ind;
2228 struct asduheader asduh = { .OA = 0, .Addr = 0, .IOA = 0};
2229 struct asdu_parms* parms = (struct asdu_parms*)data;
2230 proto_item *it104;
2231 proto_tree *it104tree;
2232 wmem_strbuf_t * res;
2234 uint8_t offset = 0; /* byte offset, signal dissection */
2235 uint8_t i;
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);
2251 offset += 1;
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);
2259 offset += 1;
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);
2266 offset += 1;
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);
2273 offset += 1;
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);
2310 else
2311 wmem_strbuf_append(res, ",...");
2312 } else {
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) {
2327 case M_SP_NA_1:
2328 case M_SP_TA_1:
2329 case M_DP_NA_1:
2330 case M_DP_TA_1:
2331 case M_ST_NA_1:
2332 case M_ST_TA_1:
2333 case M_BO_NA_1:
2334 case M_BO_TA_1:
2335 case M_SP_TB_1:
2336 case M_DP_TB_1:
2337 case M_ST_TB_1:
2338 case M_BO_TB_1:
2339 case M_ME_NA_1:
2340 case M_ME_TA_1:
2341 case M_ME_NB_1:
2342 case M_ME_TB_1:
2343 case M_ME_NC_1:
2344 case M_ME_TC_1:
2345 case M_ME_ND_1:
2346 case M_ME_TD_1:
2347 case M_ME_TE_1:
2348 case M_ME_TF_1:
2349 case M_IT_NA_1:
2350 case M_IT_TA_1:
2351 case M_IT_TB_1:
2352 case S_IT_TC_1:
2353 case C_SC_NA_1:
2354 case C_DC_NA_1:
2355 case C_RC_NA_1:
2356 case C_SE_NA_1:
2357 case C_SE_NB_1:
2358 case C_SE_NC_1:
2359 case C_BO_NA_1:
2360 case C_SC_TA_1:
2361 case C_DC_TA_1:
2362 case C_RC_TA_1:
2363 case C_SE_TA_1:
2364 case C_SE_TB_1:
2365 case C_SE_TC_1:
2366 case C_BO_TA_1:
2367 case M_EI_NA_1:
2368 case C_IC_NA_1:
2369 case C_CI_NA_1:
2370 case C_CS_NA_1:
2371 case C_RP_NA_1:
2372 case C_TS_TA_1:
2373 case P_ME_NA_1:
2374 case P_ME_NB_1:
2375 case P_ME_NC_1:
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");
2384 else
2385 trSignal = proto_tree_add_subtree(it104tree, tvb, offset, asduh.DataLength,
2386 ett_asdu_objects, &itSignal, "IOA:s");
2388 /* -------- First Information object address */
2389 if (i == 0)
2391 /* -------- Information object address */
2392 /* check length */
2393 if(Len < (unsigned)(offset + 3)) {
2394 expert_add_info(pinfo, itSignal, &ei_iec104_short_asdu);
2395 return offset;
2397 get_InfoObjectAddress(&asdu_info_obj_addr, tvb, &offset, trSignal, parms->ioa_len);
2398 } else {
2399 /* -------- following Information object address depending on SQ */
2400 if (asduh.SQ) /* <=> SQ=1, info obj addr = startaddr++ */
2402 proto_item *ti;
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 */
2408 /* check length */
2409 if(Len < (unsigned)(offset + 3)) {
2410 expert_add_info(pinfo, itSignal, &ei_iec104_short_asdu);
2411 return offset;
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);
2419 /* check length */
2420 if(Len < (unsigned)(offset + asduh.DataLength)) {
2421 expert_add_info(pinfo, itSignal, &ei_iec104_short_asdu);
2422 return offset;
2425 switch (asduh.TypeId) {
2426 case M_SP_NA_1: /* 1 Single-point information */
2427 get_SIQ(tvb, &offset, trSignal);
2428 break;
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);
2432 break;
2433 case M_DP_NA_1: /* 3 Double-point information */
2434 get_DIQ(tvb, &offset, trSignal);
2435 break;
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);
2439 break;
2440 case M_ST_NA_1: /* 5 Step position information */
2441 get_VTI(tvb, &offset, trSignal);
2442 get_QDS(tvb, &offset, trSignal);
2443 break;
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);
2448 break;
2449 case M_BO_NA_1: /* 7 Bitstring of 32 bits */
2450 get_BSI(tvb, &offset, trSignal);
2451 get_QDS(tvb, &offset, trSignal);
2452 break;
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);
2457 break;
2458 case M_ME_NA_1: /* 9 Measured value, normalized value */
2459 get_NVA(tvb, &offset, trSignal);
2460 get_QDS(tvb, &offset, trSignal);
2461 break;
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);
2466 break;
2467 case M_ME_NB_1: /* 11 Measured value, scaled value */
2468 get_SVA(tvb, &offset, trSignal);
2469 get_QDS(tvb, &offset, trSignal);
2470 break;
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);
2475 break;
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);
2479 break;
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);
2484 break;
2485 case M_IT_NA_1: /* 15 Integrated totals */
2486 get_BCR(tvb, &offset, trSignal);
2487 break;
2488 case M_IT_TA_1: /* 16 Integrated totals with time tag */
2489 get_BCR(tvb, &offset, trSignal);
2490 get_CP24Time(tvb, &offset, trSignal);
2491 break;
2492 case M_ME_ND_1: /* 21 Measured value, normalized value without quality descriptor */
2493 get_NVA(tvb, &offset, trSignal);
2494 break;
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);
2498 break;
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);
2502 break;
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);
2507 break;
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);
2512 break;
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);
2517 break;
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);
2522 break;
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);
2527 break;
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);
2531 break;
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);
2536 break;
2537 case C_SC_NA_1: /* 45 Single command */
2538 get_SCO(tvb, &offset, trSignal);
2539 break;
2540 case C_DC_NA_1: /* 46 Double command */
2541 get_DCO(tvb, &offset, trSignal);
2542 break;
2543 case C_RC_NA_1: /* 47 Regulating step command */
2544 get_RCO(tvb, &offset, trSignal);
2545 break;
2546 case C_SE_NA_1: /* 48 Set point command, normalized value */
2547 get_NVAspt(tvb, &offset, trSignal);
2548 get_QOS(tvb, &offset, trSignal);
2549 break;
2550 case C_SE_NB_1: /* 49 Set point command, scaled value */
2551 get_SVAspt(tvb, &offset, trSignal);
2552 get_QOS(tvb, &offset, trSignal);
2553 break;
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);
2557 break;
2558 case C_BO_NA_1: /* 51 Bitstring of 32 bits */
2559 get_BSIspt(tvb, &offset, trSignal);
2560 break;
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);
2564 break;
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);
2568 break;
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);
2572 break;
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);
2577 break;
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);
2582 break;
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);
2587 break;
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);
2591 break;
2592 case M_EI_NA_1: /* 70 End of initialization */
2593 get_COI(tvb, &offset, trSignal);
2594 break;
2595 case C_IC_NA_1: /* 100 Interrogation command */
2596 get_QOI(tvb, &offset, trSignal);
2597 break;
2598 case C_CI_NA_1: /* 101 Counter interrogation command */
2599 get_QCC(tvb, &offset, trSignal);
2600 break;
2601 case C_CS_NA_1: /* 103 Clock synchronization command */
2602 get_CP56Time(tvb, &offset, trSignal);
2603 break;
2604 case C_RP_NA_1: /* 105 reset process command */
2605 get_QRP(tvb, &offset, trSignal);
2606 break;
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);
2610 break;
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);
2614 break;
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);
2618 break;
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);
2622 break;
2623 default:
2624 break;
2625 } /* end 'switch (asduh.TypeId)' */
2626 } /* end 'for(i = 0; i < dui.asdu_vsq_no_of_obj; i++)' */
2627 break;
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);
2635 break;
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);
2638 offset += 2;
2639 break;
2640 default:
2641 proto_tree_add_item(it104tree, hf_ioa, tvb, offset, 3, ENC_LITTLE_ENDIAN);
2642 offset += 3;
2644 if (Len - offset > 0)
2645 proto_tree_add_item(it104tree, hf_asdu_raw_data, tvb, offset, Len - offset, ENC_NA);
2646 offset = Len;
2648 break;
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);
2654 return 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;
2668 unsigned Off;
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);
2681 Start = 0;
2682 for (Off = 0; Off <= TcpLen - 2; Off++) {
2683 Start = tvb_get_uint8(tvb, Off);
2685 if (Start == APCI_START) {
2686 if (Off > 0)
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)
2706 type = 0;
2707 else
2708 type = temp8 & 0x03;
2710 if (type == I_TYPE)
2711 proto_tree_add_item(it104tree, hf_apcitype_i, tvb, Off + 2, 4, ENC_LITTLE_ENDIAN);
2712 else
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>"));
2720 else {
2721 wmem_strbuf_append_printf(res, "<ERR ApduLen=%u bytes> ", len);
2724 switch(type) {
2725 case I_TYPE:
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);
2729 break;
2730 case S_TYPE:
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);
2733 break;
2734 case U_TYPE:
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>"));
2737 break;
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 */
2748 parms.cot_len = 2;
2749 parms.asdu_addr_len = 2;
2750 parms.ioa_len = 3;
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 */
2755 break;
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 /******************************************************************************************************/
2770 static int
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;
2778 int offset = 0;
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);
2790 offset += 1;
2792 /* If this is a single character frame, there is nothing left to do... */
2793 if (frametype == IEC101_SINGLE_CHAR) {
2794 return offset;
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);
2813 offset += 3;
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);
2828 else {
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);
2834 offset += 1;
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);
2851 offset += data_len;
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);
2859 offset += 2;
2861 return offset;
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 */
2876 void
2877 proto_register_iec60870_104(void)
2879 static hf_register_info hf_ap[] = {
2881 { &hf_apdulen,
2882 { "ApduLen", "iec60870_104.apdulen", FT_UINT8, BASE_DEC, NULL, 0x0,
2883 "APDU Len", HFILL }},
2885 { &hf_apcitype_i,
2886 { "Type", "iec60870_104.type", FT_UINT32, BASE_HEX, VALS(apci_types), 0x00000001,
2887 "APCI type", HFILL }},
2889 { &hf_apcitype_s_u,
2890 { "Type", "iec60870_104.type", FT_UINT32, BASE_HEX, VALS(apci_types), 0x00000003,
2891 "APCI type", HFILL }},
2893 { &hf_apciutype,
2894 { "UType", "iec60870_104.utype", FT_UINT32, BASE_HEX, VALS(u_types), 0x000000FC,
2895 "Apci U type", HFILL }},
2897 { &hf_apcitx,
2898 { "Tx", "iec60870_104.tx", FT_UINT32, BASE_DEC, NULL, 0x0000FFFE,
2899 NULL, HFILL }},
2901 { &hf_apcirx,
2902 { "Rx", "iec60870_104.rx", FT_UINT32, BASE_DEC, NULL, 0xFFFE0000,
2903 NULL, HFILL }},
2905 { &hf_apcidata,
2906 { "Data", "iec60870_104.data", FT_BYTES, BASE_NONE, NULL, 0,
2907 NULL, HFILL }},
2910 static int *ett_ap[] = {
2911 &ett_apci,
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 */
2929 void
2930 proto_register_iec60870_asdu(void)
2932 static hf_register_info hf_as[] = {
2934 { &hf_addr,
2935 { "Addr", "iec60870_asdu.addr", FT_UINT16, BASE_DEC, NULL, 0x0,
2936 "Common Address of Asdu", HFILL }},
2938 { &hf_oa,
2939 { "OA", "iec60870_asdu.oa", FT_UINT8, BASE_DEC, NULL, 0x0,
2940 "Originator Address", HFILL }},
2942 { &hf_typeid,
2943 { "TypeId", "iec60870_asdu.typeid", FT_UINT8, BASE_DEC, VALS(asdu_types), 0x0,
2944 "Asdu Type Id", HFILL }},
2946 { &hf_causetx,
2947 { "CauseTx", "iec60870_asdu.causetx", FT_UINT8, BASE_DEC, VALS(causetx_types), F_CAUSE,
2948 "Cause of Transmission", HFILL }},
2950 { &hf_nega,
2951 { "Negative", "iec60870_asdu.nega", FT_BOOLEAN, 8, NULL, F_NEGA,
2952 NULL, HFILL }},
2954 { &hf_test,
2955 { "Test", "iec60870_asdu.test", FT_BOOLEAN, 8, NULL, F_TEST,
2956 NULL, HFILL }},
2958 { &hf_ioa,
2959 { "IOA", "iec60870_asdu.ioa", FT_UINT24, BASE_DEC, NULL, 0x0,
2960 "Information Object Address", HFILL }},
2962 { &hf_numix,
2963 { "NumIx", "iec60870_asdu.numix", FT_UINT8, BASE_DEC, NULL, 0x7F,
2964 "Number of Information Objects/Elements", HFILL }},
2966 { &hf_sq,
2967 { "SQ", "iec60870_asdu.sq", FT_BOOLEAN, 8, NULL, F_SQ,
2968 "Sequence", HFILL }},
2970 { &hf_cp24time,
2971 { "CP24Time", "iec60870_asdu.cp24time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
2972 NULL, HFILL }},
2974 { &hf_cp24time_ms,
2975 { "MS", "iec60870_asdu.cp24time.ms", FT_UINT16, BASE_DEC, NULL, 0,
2976 "CP24Time milliseconds", HFILL }},
2978 { &hf_cp24time_min,
2979 { "Min", "iec60870_asdu.cp24time.min", FT_UINT8, BASE_DEC, NULL, 0x3F,
2980 "CP24Time minutes", HFILL }},
2982 { &hf_cp24time_iv,
2983 { "IV", "iec60870_asdu.cp24time.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
2984 "CP24Time invalid", HFILL }},
2986 { &hf_cp56time,
2987 { "CP56Time", "iec60870_asdu.cp56time", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
2988 NULL, HFILL }},
2990 { &hf_cp56time_ms,
2991 { "MS", "iec60870_asdu.cp56time.ms", FT_UINT16, BASE_DEC, NULL, 0,
2992 "CP56Time milliseconds", HFILL }},
2994 { &hf_cp56time_min,
2995 { "Min", "iec60870_asdu.cp56time.min", FT_UINT8, BASE_DEC, NULL, 0x3F,
2996 "CP56Time minutes", HFILL }},
2998 { &hf_cp56time_gen,
2999 { "GEN", "iec60870_asdu.cp56time.gen", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x40,
3000 "CP56Time substituted", HFILL }},
3002 { &hf_cp56time_iv,
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 }},
3010 { &hf_cp56time_su,
3011 { "SU", "iec60870_asdu.cp56time.su", FT_BOOLEAN, 8, TFS(&tfs_local_dst), 0x80,
3012 "CP56Time summer time", HFILL }},
3014 { &hf_cp56time_day,
3015 { "Day", "iec60870_asdu.cp56time.day", FT_UINT8, BASE_DEC, NULL, 0x1F,
3016 "CP56Time day", HFILL }},
3018 { &hf_cp56time_dow,
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 }},
3030 { &hf_siq,
3031 { "SIQ", "iec60870_asdu.siq", FT_UINT8, BASE_HEX, NULL, 0,
3032 NULL, HFILL }},
3034 { &hf_siq_spi,
3035 { "SPI", "iec60870_asdu.siq.spi", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01,
3036 "SIQ SPI", HFILL }},
3038 { &hf_siq_bl,
3039 { "BL", "iec60870_asdu.siq.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
3040 "SIQ BL", HFILL }},
3042 { &hf_siq_sb,
3043 { "SB", "iec60870_asdu.siq.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
3044 "SIQ SB", HFILL }},
3046 { &hf_siq_nt,
3047 { "NT", "iec60870_asdu.siq.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
3048 "SIQ NT", HFILL }},
3050 { &hf_siq_iv,
3051 { "IV", "iec60870_asdu.siq.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
3052 "SIQ IV", HFILL }},
3054 { &hf_diq,
3055 { "DIQ", "iec60870_asdu.diq", FT_UINT8, BASE_HEX, NULL, 0,
3056 NULL, HFILL }},
3058 { &hf_diq_dpi,
3059 { "DPI", "iec60870_asdu.diq.dpi", FT_UINT8, BASE_DEC, VALS(diq_types), 0x03,
3060 "DIQ DPI", HFILL }},
3062 { &hf_diq_bl,
3063 { "BL", "iec60870_asdu.diq.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
3064 "DIQ BL", HFILL }},
3066 { &hf_diq_sb,
3067 { "SB", "iec60870_asdu.diq.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
3068 "DIQ SB", HFILL }},
3070 { &hf_diq_nt,
3071 { "NT", "iec60870_asdu.diq.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
3072 "DIQ NT", HFILL }},
3074 { &hf_diq_iv,
3075 { "IV", "iec60870_asdu.diq.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
3076 "DIQ IV", HFILL }},
3078 { &hf_qds,
3079 { "QDS", "iec60870_asdu.qds", FT_UINT8, BASE_HEX, NULL, 0,
3080 NULL, HFILL }},
3082 { &hf_qds_ov,
3083 { "OV", "iec60870_asdu.qds.ov", FT_BOOLEAN, 8, TFS(&tfs_overflow_no_overflow), 0x01,
3084 "QDS OV", HFILL }},
3086 { &hf_qds_bl,
3087 { "BL", "iec60870_asdu.qds.bl", FT_BOOLEAN, 8, TFS(&tfs_blocked_not_blocked), 0x10,
3088 "QDS BL", HFILL }},
3090 { &hf_qds_sb,
3091 { "SB", "iec60870_asdu.qds.sb", FT_BOOLEAN, 8, TFS(&tfs_substituted_not_substituted), 0x20,
3092 "QDS SB", HFILL }},
3094 { &hf_qds_nt,
3095 { "NT", "iec60870_asdu.qds.nt", FT_BOOLEAN, 8, TFS(&tfs_not_topical_topical), 0x40,
3096 "QDS NT", HFILL }},
3098 { &hf_qds_iv,
3099 { "IV", "iec60870_asdu.qds.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
3100 "QDS IV", HFILL }},
3102 { &hf_vti,
3103 { "VTI", "iec60870_asdu.vti", FT_UINT8, BASE_HEX, NULL, 0,
3104 NULL, HFILL }},
3106 { &hf_vti_v,
3107 { "Value", "iec60870_asdu.vti.v", FT_INT8, BASE_DEC, NULL, 0x7F,
3108 "VTI Value", HFILL }},
3110 { &hf_vti_t,
3111 { "T", "iec60870_asdu.vti.t", FT_BOOLEAN, 8, TFS(&tfs_transient_not_transient), 0x80,
3112 "VTI T", HFILL }},
3114 { &hf_qos,
3115 { "QOS", "iec60870_asdu.qos", FT_UINT8, BASE_HEX, NULL, 0,
3116 NULL, HFILL }},
3118 { &hf_qos_ql,
3119 { "QL", "iec60870_asdu.qos.ql", FT_UINT8, BASE_DEC, NULL, 0x7F,
3120 "QOS QL", HFILL }},
3122 { &hf_qos_se,
3123 { "S/E", "iec60870_asdu.qos.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
3124 "QOS S/E", HFILL }},
3126 { &hf_sco,
3127 { "SCO", "iec60870_asdu.sco", FT_UINT8, BASE_HEX, NULL, 0,
3128 NULL, HFILL }},
3130 { &hf_sco_on,
3131 { "ON/OFF", "iec60870_asdu.sco.on", FT_BOOLEAN, 8, TFS(&tfs_on_off), 0x01,
3132 "SCO SCS", HFILL }},
3134 { &hf_sco_qu,
3135 { "QU", "iec60870_asdu.sco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
3136 "SCO QU", HFILL }},
3138 { &hf_sco_se,
3139 { "S/E", "iec60870_asdu.sco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
3140 "SCO S/E", HFILL }},
3142 { &hf_dco,
3143 { "DCO", "iec60870_asdu.dco", FT_UINT8, BASE_HEX, NULL, 0,
3144 NULL, HFILL }},
3146 { &hf_dco_on,
3147 { "ON/OFF", "iec60870_asdu.dco.on", FT_UINT8, BASE_DEC, VALS(dco_on_types), 0x03,
3148 "DCO DCS", HFILL }},
3150 { &hf_dco_qu,
3151 { "QU", "iec60870_asdu.dco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
3152 "DCO QU", HFILL }},
3154 { &hf_dco_se,
3155 { "S/E", "iec60870_asdu.dco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
3156 "DCO S/E", HFILL }},
3158 { &hf_rco,
3159 { "RCO", "iec60870_asdu.rco", FT_UINT8, BASE_HEX, NULL, 0,
3160 NULL, HFILL }},
3162 { &hf_rco_up,
3163 { "UP/DOWN", "iec60870_asdu.rco.up", FT_UINT8, BASE_DEC, VALS(rco_up_types), 0x03,
3164 "RCO RCS", HFILL }},
3166 { &hf_rco_qu,
3167 { "QU", "iec60870_asdu.rco.qu", FT_UINT8, BASE_DEC, VALS(qos_qu_types), 0x7C,
3168 "RCO QU", HFILL }},
3170 { &hf_rco_se,
3171 { "S/E", "iec60870_asdu.rco.se", FT_BOOLEAN, 8, TFS(&tfs_select_execute), 0x80,
3172 "RCO S/E", HFILL }},
3174 { &hf_qpm,
3175 { "QPM", "iec60870_asdu.qpm", FT_UINT8, BASE_HEX, NULL, 0,
3176 NULL, HFILL } },
3178 { &hf_qpm_kpa,
3179 { "KPA", "iec60870_asdu.qpm.kpa", FT_UINT8, BASE_DEC, VALS(qpm_kpa_types), 0x3F,
3180 "QPM KPA", HFILL } },
3182 { &hf_qpm_lpc,
3183 { "LPC", "iec60870_asdu.qpm.lpc", FT_UINT8, BASE_DEC, VALS(qpm_lpc_types), 0x40,
3184 "QPM LPC", HFILL } },
3186 { &hf_qpm_pop,
3187 { "POP", "iec60870_asdu.qpm.pop", FT_UINT8, BASE_DEC, VALS(qpm_pop_types), 0x80,
3188 "QPM POP", HFILL } },
3190 { &hf_asn,
3191 { "ASDU Segment Sequence Number (ASN)", "iec60870_asdu.asn", FT_UINT8, BASE_DEC, NULL, 0x3F,
3192 NULL, HFILL }},
3194 { &hf_asn_fin,
3195 { "Final segment (FIN)", "iec60870_asdu.asn.fin", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x80,
3196 NULL, HFILL }},
3198 { &hf_asn_fir,
3199 { "First segment (FIR)", "iec60870_asdu.asn.fir", FT_BOOLEAN, 8, TFS(&tfs_yes_no), 0x40,
3200 NULL, HFILL }},
3202 { &hf_iec60870_segment_data,
3203 { "ASDU segment data", "iec60870_asdu.segment_data", FT_BYTES, BASE_NONE, NULL, 0x0,
3204 NULL, HFILL }},
3206 { &hf_usr,
3207 { "User number (USR)", "iec60870_asdu.usr", FT_UINT16, BASE_DEC | BASE_RANGE_STRING, RVALS(usr_types), 0,
3208 NULL, HFILL }},
3210 { &hf_mal,
3211 { "MAC algorithm (MAL)", "iec60870_asdu.mal", FT_UINT8, BASE_DEC | BASE_RANGE_STRING, RVALS(mal_types), 0,
3212 NULL, HFILL }},
3214 { &hf_rsc,
3215 { "Reason for challenge (RSC)", "iec60870_asdu.rsc", FT_UINT8, BASE_DEC, VALS(rsc_types), 0,
3216 NULL, HFILL }},
3218 { &hf_csq,
3219 { "Challenge sequence number (CSQ)", "iec60870_asdu.csq", FT_UINT32, BASE_DEC, NULL, 0,
3220 NULL, HFILL }},
3222 { &hf_ksq,
3223 { "Key change sequence number (KSQ)", "iec60870_asdu.ksq", FT_UINT32, BASE_DEC, NULL, 0,
3224 NULL, HFILL }},
3226 { &hf_kwa,
3227 { "Key wrap algorithm (KWA)", "iec60870_asdu.kwa", FT_UINT8, BASE_DEC | BASE_RANGE_STRING, RVALS(kwa_types), 0,
3228 NULL, HFILL }},
3230 { &hf_kst,
3231 { "Key status (KST)", "iec60870_asdu.kst", FT_UINT8, BASE_DEC, VALS(kst_types), 0,
3232 NULL, HFILL }},
3234 { &hf_hln,
3235 { "MAC length (HLN)", "iec60870_asdu.hln", FT_UINT16, BASE_DEC, NULL, 0,
3236 NULL, HFILL }},
3238 { &hf_hal,
3239 { "MAC algorithm (HAL)", "iec60870_asdu.hal", FT_UINT8, BASE_DEC | BASE_RANGE_STRING, RVALS(hal_types), 0,
3240 NULL, HFILL }},
3242 { &hf_cln,
3243 { "Challenge data length (CLN)", "iec60870_asdu.cln", FT_UINT16, BASE_DEC, NULL, 0,
3244 NULL, HFILL }},
3246 { &hf_wkl,
3247 { "Wrapped key data length (CLN)", "iec60870_asdu.wkl", FT_UINT16, BASE_DEC, NULL, 0,
3248 NULL, HFILL }},
3250 { &hf_prcd_raw_data,
3251 { "Pseudo-random challenge data", "iec60870_asdu.challenge_data", FT_BYTES, BASE_NONE, NULL, 0x0,
3252 NULL, HFILL }},
3254 { &hf_hmac_raw_data,
3255 { "HMAC value", "iec60870_asdu.hmac", FT_BYTES, BASE_NONE, NULL, 0x0,
3256 NULL, HFILL }},
3258 { &hf_wkd_raw_data,
3259 { "Wrapped key data", "iec60870_asdu.wkd", FT_BYTES, BASE_NONE, NULL, 0x0,
3260 NULL, HFILL }},
3262 { &hf_aid,
3263 { "Association ID (AID)", "iec60870_asdu.aid", FT_UINT16, BASE_DEC, NULL, 0,
3264 NULL, HFILL }},
3266 { &hf_err,
3267 { "Error code (ERR)", "iec60870_asdu.err", FT_UINT8, BASE_DEC | BASE_RANGE_STRING, RVALS(error_codes), 0,
3268 NULL, HFILL }},
3270 { &hf_etm,
3271 { "Error time stamp (ETM)", "iec60870_asdu.etm", FT_ABSOLUTE_TIME, ABSOLUTE_TIME_LOCAL, NULL, 0,
3272 NULL, HFILL }},
3274 { &hf_etm_ms,
3275 { "MS", "iec60870_asdu.etm.ms", FT_UINT16, BASE_DEC, NULL, 0,
3276 "Error time stamp milliseconds", HFILL }},
3278 { &hf_etm_min,
3279 { "Min", "iec60870_asdu.etm.min", FT_UINT8, BASE_DEC, NULL, 0x3F,
3280 "Error time stamp minutes", HFILL }},
3282 { &hf_etm_iv,
3283 { "IV", "iec60870_asdu.etm.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
3284 "Error time stamp invalid", HFILL }},
3286 { &hf_etm_hour,
3287 { "Hour", "iec60870_asdu.etm.hour", FT_UINT8, BASE_DEC, NULL, 0x1F,
3288 "Error time stamp hours", HFILL }},
3290 { &hf_etm_su,
3291 { "SU", "iec60870_asdu.etm.su", FT_BOOLEAN, 8, TFS(&tfs_local_dst), 0x80,
3292 "Error time stamp summer time", HFILL }},
3294 { &hf_etm_day,
3295 { "Day", "iec60870_asdu.etm.day", FT_UINT8, BASE_DEC, NULL, 0x1F,
3296 "Error time stamp day", HFILL }},
3298 { &hf_etm_dow,
3299 { "DOW", "iec60870_asdu.etm.dow", FT_UINT8, BASE_DEC, NULL, 0xE0,
3300 "Error time stamp day of week", HFILL }},
3302 { &hf_etm_month,
3303 { "Month", "iec60870_asdu.etm.month", FT_UINT8, BASE_DEC, NULL, 0x0F,
3304 "Error time stamp month", HFILL }},
3306 { &hf_etm_year,
3307 { "Year", "iec60870_asdu.etm.year", FT_UINT8, BASE_DEC, NULL, 0x7F,
3308 "Error time stamp year", HFILL }},
3310 { &hf_eln,
3311 { "Error length (ELN)", "iec60870_asdu.eln", FT_UINT16, BASE_DEC, NULL, 0,
3312 NULL, HFILL }},
3314 { &hf_error_text,
3315 { "Error text", "iec60870_asdu.error_text", FT_STRING, BASE_NONE, NULL, 0x0,
3316 NULL, HFILL }},
3318 { &hf_coi,
3319 { "COI", "iec60870_asdu.coi", FT_UINT8, BASE_HEX, NULL, 0,
3320 NULL, HFILL }},
3322 { &hf_coi_r,
3323 { "R", "iec60870_asdu.coi.r", FT_UINT8, BASE_DEC, VALS(coi_r_types), 0x7F,
3324 "COI R", HFILL }},
3326 { &hf_coi_i,
3327 { "I", "iec60870_asdu.coi.i", FT_BOOLEAN, 8, TFS(&tfs_coi_i), 0x80,
3328 "COI I", HFILL }},
3330 { &hf_qoi,
3331 { "QOI", "iec60870_asdu.qoi", FT_UINT8, BASE_DEC, VALS(qoi_r_types), 0,
3332 NULL, HFILL }},
3334 { &hf_qcc,
3335 { "QCC", "iec60870_asdu.qcc", FT_UINT8, BASE_HEX, NULL, 0,
3336 NULL, HFILL } },
3338 { &hf_qcc_rqt,
3339 { "RQT", "iec60870_asdu.qcc.rqt", FT_UINT8, BASE_DEC, VALS(rqt_r_types), 0x3F,
3340 NULL, HFILL } },
3342 { &hf_qcc_frz,
3343 { "FRZ", "iec60870_asdu.qcc.frz", FT_UINT8, BASE_DEC, VALS(frz_r_types), 0xC0,
3344 NULL, HFILL } },
3346 { &hf_qrp,
3347 { "QRP", "iec60870_asdu.qrp", FT_UINT8, BASE_DEC, VALS(qrp_r_types), 0,
3348 NULL, HFILL }},
3350 { &hf_bcr,
3351 { "BCR", "iec60870_asdu.bcr", FT_INT32, BASE_DEC, NULL, 0x0,
3352 "Binary Counter", HFILL }},
3354 { &hf_bcr_count,
3355 { "Value", "iec60870_asdu.bcr.count", FT_INT32, BASE_DEC, NULL, 0x0,
3356 NULL, HFILL }},
3358 { &hf_bcr_sq,
3359 { "SQ", "iec60870_asdu.bcr.sq", FT_UINT8, BASE_DEC, NULL, 0x1F,
3360 "Sequence Number", HFILL }},
3362 { &hf_bcr_cy,
3363 { "CY", "iec60870_asdu.bcr.cy", FT_BOOLEAN, 8, TFS(&tfs_overflow_no_overflow), 0x20,
3364 "Counter Overflow", HFILL }},
3366 { &hf_bcr_ca,
3367 { "CA", "iec60870_asdu.bcr.ca", FT_BOOLEAN, 8, TFS(&tfs_adjusted_not_adjusted), 0x40,
3368 "Counter Adjusted", HFILL }},
3370 { &hf_bcr_iv,
3371 { "IV", "iec60870_asdu.bcr.iv", FT_BOOLEAN, 8, TFS(&tfs_invalid_valid), 0x80,
3372 "Counter Validity", HFILL }},
3374 { &hf_start,
3375 { "START", "iec60870_asdu.start", FT_UINT8, BASE_HEX, NULL, 0x0,
3376 NULL, HFILL }},
3378 { &hf_asdu_bitstring,
3379 { "Value", "iec60870_asdu.bitstring", FT_UINT32, BASE_HEX, NULL, 0x0,
3380 "BSI value", HFILL }},
3382 { &hf_asdu_float,
3383 { "Value", "iec60870_asdu.float", FT_FLOAT, BASE_NONE, NULL, 0x0,
3384 "Float value", HFILL }},
3386 { &hf_asdu_normval,
3387 { "Value", "iec60870_asdu.normval", FT_FLOAT, BASE_NONE, NULL, 0x0,
3388 "Normalised value", HFILL }},
3390 { &hf_asdu_scalval,
3391 { "Value", "iec60870_asdu.scalval", FT_INT16, BASE_DEC, NULL, 0x0,
3392 "Scaled value", HFILL }},
3394 { &hf_asdu_tsc,
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,
3404 NULL, HFILL }},
3406 { &hf_iec60870_segment,
3407 { "ASDU Segment", "iec60870_asdu.segment", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
3408 NULL, HFILL }},
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,
3432 NULL, HFILL }},
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[] = {
3445 &ett_asdu,
3446 &ett_asdu_objects,
3447 &ett_siq,
3448 &ett_diq,
3449 &ett_qds,
3450 &ett_qos,
3451 &ett_vti,
3452 &ett_sco,
3453 &ett_dco,
3454 &ett_rco,
3455 &ett_qpm,
3456 &ett_coi,
3457 &ett_qcc,
3458 &ett_cp24time,
3459 &ett_cp56time,
3460 &ett_etm,
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 */
3488 void
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 /******************************************************************************************************/
3497 static unsigned
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);
3504 switch (type) {
3505 case IEC101_SINGLE_CHAR:
3506 len = 1;
3507 break;
3508 case IEC101_FIXED_LEN:
3509 len = global_iec60870_link_addr_len + 4;
3510 break;
3511 case IEC101_VAR_LEN:
3512 if (tvb_captured_length_remaining(tvb, offset) < 3) {
3513 /* We need another segment. */
3514 return 0;
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);
3529 break;
3532 return len;
3535 /******************************************************************************************************/
3536 /* Dissect (and possibly Re-assemble) IEC 101 protocol payload data */
3537 /******************************************************************************************************/
3538 static int
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. */
3546 switch (type) {
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);
3551 break;
3552 default:
3553 return 0;
3556 return tvb_captured_length(tvb);
3559 /* The registration hand-off routine */
3560 void
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[] = {
3596 &ett_iec60870_101,
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},
3628 {NULL, NULL, -1}
3631 static const enum_val_t cot_len[] = {
3632 {"1", "1 octet", 1},
3633 {"2", "2 octet", 2},
3634 {NULL, NULL, -1}
3637 static const enum_val_t asdu_addr_len[] = {
3638 {"1", "1 octet", 1},
3639 {"2", "2 octet", 2},
3640 {NULL, NULL, -1}
3643 static const enum_val_t asdu_ioa_len[] = {
3644 {"1", "1 octet", 1},
3645 {"2", "2 octet", 2},
3646 {"3", "3 octet", 3},
3647 {NULL, NULL, -1}
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);
3672 void
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 /******************************************************************************************************/
3684 static int
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;
3691 uint8_t offset = 0;
3692 int i;
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);
3704 offset += 1;
3706 /* If this is a single character frame, there is nothing left to do... */
3707 if (frametype == IEC103_SINGLE_CHAR) {
3708 return offset;
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);
3715 offset += 3;
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);
3730 else {
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);
3736 offset += 1;
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);
3741 offset += 1;
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);
3750 else {
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);
3760 else {
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);
3767 offset += 6;
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);
3775 break;
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);
3778 offset += 1;
3779 break;
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);
3783 offset += 2;
3784 break;
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);
3788 offset += 1;
3789 break;
3792 /* Monitor Direction */
3793 else {
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);
3797 offset += 1;
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);
3800 offset += 1;
3801 break;
3802 case 0x05: /* ASDU 5 - Identification */
3803 proto_tree_add_item(iec103_tree, hf_iec60870_5_103_col, tvb, offset, 1, ENC_LITTLE_ENDIAN);
3804 offset += 1;
3805 proto_tree_add_item(iec103_tree, hf_iec60870_5_103_mfg, tvb, offset, 8, ENC_ASCII);
3806 offset += 8;
3807 proto_tree_add_item(iec103_tree, hf_iec60870_5_103_mfg_sw, tvb, offset, 4, ENC_LITTLE_ENDIAN);
3808 offset += 4;
3809 break;
3810 case 0x06: /* ASDU 6 - Time synchronization */
3811 get_CP56Time(tvb, &offset, iec103_tree);
3812 break;
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);
3815 offset += 1;
3816 break;
3817 case 0x09: /* ASDU 9 - Measurements II */
3818 get_NVA(tvb, &offset, iec103_tree);
3819 break;
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);
3825 offset += 8;
3826 break;
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);
3835 offset += 2;
3837 return offset;
3841 /******************************************************************************************************/
3842 /* Return length of IEC 103 Protocol over TCP message (used for re-assembly) */
3843 /******************************************************************************************************/
3844 static unsigned
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);
3851 switch (type) {
3852 case IEC103_SINGLE_CHAR:
3853 len = 1;
3854 break;
3855 case IEC103_FIXED_LEN:
3856 len = 5;
3857 break;
3858 case IEC103_VAR_LEN:
3859 len = tvb_get_uint8(tvb, offset+1) + 6;
3860 break;
3863 return len;
3866 /******************************************************************************************************/
3867 /* Dissect (and possibly Re-assemble) IEC 103 protocol payload data */
3868 /******************************************************************************************************/
3869 static int
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. */
3876 switch (type) {
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);
3881 break;
3882 default:
3883 return 0;
3886 return tvb_captured_length(tvb);
3889 /* IEC 60870-5-103 Protocol registration hand-off routine */
3890 void
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);
3998 void
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
4011 * Local variables:
4012 * c-basic-offset: 8
4013 * tab-width: 8
4014 * indent-tabs-mode: t
4015 * End:
4017 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
4018 * :indentSize=8:tabSize=8:noTabs=false: