2 * Routines for IEC-60870-5-104 (iec104) Protocol disassembly
7 * Copyright (c) 2008 by Joan Ramio <joan@ramio.cat>
8 * Joan is a masculine catalan name. Search the Internet for Joan Pujol (alias Garbo).
10 * Copyright (c) 2009 by Kjell Hultman <kjell.hultman@gmail.com>
11 * Added dissection of signal (ASDU) information.
12 * Kjell is also a masculine name, but a Scandinavian one.
14 * Wireshark - Network traffic analyzer
15 * By Gerald Combs <gerald@wireshark.org>
16 * Copyright 1999 Gerald Combs
18 * This program is free software; you can redistribute it and/or
19 * modify it under the terms of the GNU General Public License
20 * as published by the Free Software Foundation; either version 2
21 * of the License, or (at your option) any later version.
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
40 #include <math.h> /* floor */
42 #include <epan/packet.h>
43 #include <epan/dissectors/packet-tcp.h>
44 #include <epan/emem.h>
46 /* IEC-104 comment: Fields are little endian. */
50 static dissector_handle_t iec104asdu_handle;
52 /* the asdu header structure */
64 /* the apci header structure */
73 /* asdu value time stamp structure */
80 guint8 cp56t_dom; /* day of month */
81 guint8 cp56t_dow; /* day of week */
85 gboolean IV; /* Invalid (1) */
90 /* asdu value/status structure */
93 guint8 VTI; /* Value w transient state indication, */
94 /* CP8: value I7[1..7]<-64..+63>, */
95 /* Transient BS1[8]<0..1>0: eq. not in transient state, 1: in trans ... */
96 gint16 NVA; /* Normalized value F16[1..16]<-1..+1-2^-15> */
97 gint16 SVA; /* Scaled value I16[1..16]<-2^15..+2^15-1> */
98 gfloat FLT; /* IEEE 754 float value R32.23{Fraction,Exponent,Sign} */
99 /* ToDo -- BCR Binary counter reading */
100 } MV; /* Measured Value */
102 /* together with VTI */
103 gboolean TRANSIENT; /* equipment is in transient state */
106 gboolean IPOS0; /* double-point: indeterminate or intermediate state */
109 gboolean IPOS3; /* double-point: indeterminate state (fault?) */
111 /* quality descriptor-bits */
112 gboolean BL; /* Blocked (1) */
113 gboolean SB; /* Substituted (1) */
114 gboolean NT; /* Topical (0) / Not topical (1) [Topical <=> if most recent update was succesful] */
115 gboolean IV; /* Invalid (1) */
116 /* from separat quality descriptor */
117 gboolean OV; /* Overflow (1) */
118 /* from separate quality descriptor */
119 gboolean EI; /* Elapsed time valid (0) / Elapsed time invalid (1) */
123 /* asdu command value/status structure */
128 /* QOC qualifier-bits */
129 guint16 QU; /* qualifier-value */
130 gboolean ZeroP; /* No pulse */
131 gboolean ShortP; /* Short Pulse */
132 gboolean LongP; /* Long Pulse */
133 gboolean Persist; /* Persistent output */
134 gboolean SE; /* Select (1) / Execute (0) */
139 /* asdu setpoint value/status structure */
142 gint16 NVA; /* Normalized value F16[1..16]<-1..+1-2^-15> */
143 gint16 SVA; /* Scaled value I16[1..16]<-2^15..+2^15-1> */
144 gfloat FLT; /* IEEE 754 float value R32.23{Fraction,Exponent,Sign} */
145 } SP; /* Measured Value */
147 /* QOS qualifier-bits */
148 guint8 QL; /* UI7[1..7]<0..127>; 0-default, 1..63-reserved for strd def., */
149 /* 64..127-reserved for special use (private range) */
150 gboolean SE; /* Select (1) / Execute (0) */
156 static guint iec104port = 2404;
158 /* Define the iec104 proto */
159 static int proto_iec104apci = -1;
160 static int proto_iec104asdu = -1;
162 /* Protocol constants */
163 #define APCI_START 0x68
165 #define APCI_START_LEN 2
166 #define APCI_DATA_LEN (APCI_LEN- APCI_START_LEN)
167 #define APDU_MIN_LEN 4
168 #define APDU_MAX_LEN 253
170 /* ASDU_HEAD_LEN: Includes Asdu head and first IOA */
171 #define ASDU_HEAD_LEN 9
181 #define APCI_TYPE_UNKNOWN 4
182 static const value_string apci_types [] = {
190 /* Constants relative to the filed, independent of the field position in the byte */
191 /* U (Unnombered) constants */
192 #define U_STARTDT_ACT 0x01
193 #define U_STARTDT_CON 0x02
194 #define U_STOPDT_ACT 0x04
195 #define U_STOPDT_CON 0x08
196 #define U_TESTFR_ACT 0x10
197 #define U_TESTFR_CON 0x20
198 static const value_string u_types[] = {
199 { U_STARTDT_ACT, "STARTDT act" },
200 { U_STARTDT_CON, "STARTDT con" },
201 { U_STOPDT_ACT, "STOPDT act" },
202 { U_STOPDT_CON, "STOPDT con" },
203 { U_TESTFR_ACT, "TESTFR act" },
204 { U_TESTFR_CON, "TESTFR con" },
209 /* ASDU types (TypeId) */
210 #define M_SP_NA_1 1 /* single-point information */
211 #define M_DP_NA_1 3 /* double-point information */
212 #define M_ST_NA_1 5 /* step position information */
213 #define M_BO_NA_1 7 /* bitstring of 32 bits */
214 #define M_ME_NA_1 9 /* measured value, normalized value */
215 #define M_ME_NB_1 11 /* measured value, scaled value */
216 #define M_ME_NC_1 13 /* measured value, short floating point number */
217 #define M_IT_NA_1 15 /* integrated totals */
218 #define M_PS_NA_1 20 /* packed single-point information with status change detection */
219 #define M_ME_ND_1 21 /* measured value, normalized value without quality descriptor */
220 #define M_SP_TB_1 30 /* single-point information with time tag CP56Time2a */
221 #define M_DP_TB_1 31 /* double-point information with time tag CP56Time2a */
222 #define M_ST_TB_1 32 /* step position information with time tag CP56Time2a */
223 #define M_BO_TB_1 33 /* bitstring of 32 bit with time tag CP56Time2a */
224 #define M_ME_TD_1 34 /* measured value, normalized value with time tag CP56Time2a */
225 #define M_ME_TE_1 35 /* measured value, scaled value with time tag CP56Time2a */
226 #define M_ME_TF_1 36 /* measured value, short floating point number with time tag CP56Time2a */
227 #define M_IT_TB_1 37 /* integrated totals with time tag CP56Time2a */
228 #define M_EP_TD_1 38 /* event of protection equipment with time tag CP56Time2a */
229 #define M_EP_TE_1 39 /* packed start events of protection equipment with time tag CP56Time2a */
230 #define M_EP_TF_1 40 /* packed output circuit information of protection equipment with time tag CP56Time2a */
231 #define C_SC_NA_1 45 /* single command */
232 #define C_DC_NA_1 46 /* double command */
233 #define C_RC_NA_1 47 /* regulating step command */
234 #define C_SE_NA_1 48 /* set point command, normalized value */
235 #define C_SE_NB_1 49 /* set point command, scaled value */
236 #define C_SE_NC_1 50 /* set point command, short floating point number */
237 #define C_BO_NA_1 51 /* bitstring of 32 bits */
238 #define C_SC_TA_1 58 /* single command with time tag CP56Time2a */
239 #define C_DC_TA_1 59 /* double command with time tag CP56Time2a */
240 #define C_RC_TA_1 60 /* regulating step command with time tag CP56Time2a */
241 #define C_SE_TA_1 61 /* set point command, normalized value with time tag CP56Time2a */
242 #define C_SE_TB_1 62 /* set point command, scaled value with time tag CP56Time2a */
243 #define C_SE_TC_1 63 /* set point command, short floating-point number with time tag CP56Time2a */
244 #define C_BO_TA_1 64 /* bitstring of 32 bits with time tag CP56Time2a */
245 #define M_EI_NA_1 70 /* end of initialization */
246 #define C_IC_NA_1 100 /* interrogation command */
247 #define C_CI_NA_1 101 /* counter interrogation command */
248 #define C_RD_NA_1 102 /* read command */
249 #define C_CS_NA_1 103 /* clock synchronization command */
250 #define C_RP_NA_1 105 /* reset process command */
251 #define C_TS_TA_1 107 /* test command with time tag CP56Time2a */
252 #define P_ME_NA_1 110 /* parameter of measured value, normalized value */
253 #define P_ME_NB_1 111 /* parameter of measured value, scaled value */
254 #define P_ME_NC_1 112 /* parameter of measured value, short floating-point number */
255 #define P_AC_NA_1 113 /* parameter activation */
256 #define F_FR_NA_1 120 /* file ready */
257 #define F_SR_NA_1 121 /* section ready */
258 #define F_SC_NA_1 122 /* call directory, select file, call file, call section */
259 #define F_LS_NA_1 123 /* last section, last segment */
260 #define F_AF_NA_1 124 /* ack file, ack section */
261 #define F_SG_NA_1 125 /* segment */
262 #define F_DR_TA_1 126 /* directory */
263 #define F_SC_NB_1 127 /* Query Log - Request archive file */
264 static const value_string asdu_types [] = {
265 { M_SP_NA_1, "M_SP_NA_1" },
266 { M_DP_NA_1, "M_DP_NA_1" },
267 { M_ST_NA_1, "M_ST_NA_1" },
268 { M_BO_NA_1, "M_BO_NA_1" },
269 { M_ME_NA_1, "M_ME_NA_1" },
270 { M_ME_NB_1, "M_ME_NB_1" },
271 { M_ME_NC_1, "M_ME_NC_1" },
272 { M_IT_NA_1, "M_IT_NA_1" },
273 { M_PS_NA_1, "M_PS_NA_1" },
274 { M_ME_ND_1, "M_ME_ND_1" },
275 { M_SP_TB_1, "M_SP_TB_1" },
276 { M_DP_TB_1, "M_DP_TB_1" },
277 { M_ST_TB_1, "M_ST_TB_1" },
278 { M_BO_TB_1, "M_BO_TB_1" },
279 { M_ME_TD_1, "M_ME_TD_1" },
280 { M_ME_TE_1, "M_ME_TE_1" },
281 { M_ME_TF_1, "M_ME_TF_1" },
282 { M_IT_TB_1, "M_IT_TB_1" },
283 { M_EP_TD_1, "M_EP_TD_1" },
284 { M_EP_TE_1, "M_EP_TE_1" },
285 { M_EP_TF_1, "M_EP_TF_1" },
286 { C_SC_NA_1, "C_SC_NA_1" },
287 { C_DC_NA_1, "C_DC_NA_1" },
288 { C_RC_NA_1, "C_RC_NA_1" },
289 { C_SE_NA_1, "C_SE_NA_1" },
290 { C_SE_NB_1, "C_SE_NB_1" },
291 { C_SE_NC_1, "C_SE_NC_1" },
292 { C_BO_NA_1, "C_BO_NA_1" },
293 { C_SC_TA_1, "C_SC_TA_1" },
294 { C_DC_TA_1, "C_DC_TA_1" },
295 { C_RC_TA_1, "C_RC_TA_1" },
296 { C_SE_TA_1, "C_SE_TA_1" },
297 { C_SE_TB_1, "C_SE_TB_1" },
298 { C_SE_TC_1, "C_SE_TC_1" },
299 { C_BO_TA_1, "C_BO_TA_1" },
300 { M_EI_NA_1, "M_EI_NA_1" },
301 { C_IC_NA_1, "C_IC_NA_1" },
302 { C_CI_NA_1, "C_CI_NA_1" },
303 { C_RD_NA_1, "C_RD_NA_1" },
304 { C_CS_NA_1, "C_CS_NA_1" },
305 { C_RP_NA_1, "C_RP_NA_1" },
306 { C_TS_TA_1, "C_TS_TA_1" },
307 { P_ME_NA_1, "P_ME_NA_1" },
308 { P_ME_NB_1, "P_ME_NB_1" },
309 { P_ME_NC_1, "P_ME_NC_1" },
310 { P_AC_NA_1, "P_AC_NA_1" },
311 { F_FR_NA_1, "F_FR_NA_1" },
312 { F_SR_NA_1, "F_SR_NA_1" },
313 { F_SC_NA_1, "F_SC_NA_1" },
314 { F_LS_NA_1, "F_LS_NA_1" },
315 { F_AF_NA_1, "F_AF_NA_1" },
316 { F_SG_NA_1, "F_SG_NA_1" },
317 { F_DR_TA_1, "F_DR_TA_1" },
318 { F_SC_NB_1, "F_SC_NB_1" },
321 static const value_string asdu_lngtypes [] = {
322 { M_SP_NA_1, "single-point information" },
323 { M_DP_NA_1, "double-point information" },
324 { M_ST_NA_1, "step position information" },
325 { M_BO_NA_1, "bitstring of 32 bits" },
326 { M_ME_NA_1, "measured value, normalized value" },
327 { M_ME_NB_1, "measured value, scaled value" },
328 { M_ME_NC_1, "measured value, short floating point number" },
329 { M_IT_NA_1, "integrated totals" },
330 { M_PS_NA_1, "packed single-point information with status change detection" },
331 { M_ME_ND_1, "measured value, normalized value without quality descriptor" },
332 { M_SP_TB_1, "single-point information with time tag CP56Time2a" },
333 { M_DP_TB_1, "double-point information with time tag CP56Time2a" },
334 { M_ST_TB_1, "step position information with time tag CP56Time2a" },
335 { M_BO_TB_1, "bitstring of 32 bit with time tag CP56Time2a" },
336 { M_ME_TD_1, "measured value, normalized value with time tag CP56Time2a" },
337 { M_ME_TE_1, "measured value, scaled value with time tag CP56Time2a" },
338 { M_ME_TF_1, "measured value, short floating point number with time tag CP56Time2a" },
339 { M_IT_TB_1, "integrated totals with time tag CP56Time2a" },
340 { M_EP_TD_1, "event of protection equipment with time tag CP56Time2a" },
341 { M_EP_TE_1, "packed start events of protection equipment with time tag CP56Time2a" },
342 { M_EP_TF_1, "packed output circuit information of protection equipment with time tag CP56Time2a" },
343 { C_SC_NA_1, "single command" },
344 { C_DC_NA_1, "double command" },
345 { C_RC_NA_1, "regulating step command" },
346 { C_SE_NA_1, "set point command, normalized value" },
347 { C_SE_NB_1, "set point command, scaled value" },
348 { C_SE_NC_1, "set point command, short floating point number" },
349 { C_BO_NA_1, "bitstring of 32 bits" },
350 { C_SC_TA_1, "single command with time tag CP56Time2a" },
351 { C_DC_TA_1, "double command with time tag CP56Time2a" },
352 { C_RC_TA_1, "regulating step command with time tag CP56Time2a" },
353 { C_SE_TA_1, "set point command, normalized value with time tag CP56Time2a" },
354 { C_SE_TB_1, "set point command, scaled value with time tag CP56Time2a" },
355 { C_SE_TC_1, "set point command, short floating-point number with time tag CP56Time2a" },
356 { C_BO_TA_1, "bitstring of 32 bits with time tag CP56Time2a" },
357 { M_EI_NA_1, "end of initialization" },
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" },
380 /* Cause of Transmision (CauseTx) */
418 #define UkComAdrASDU 46
420 static const value_string causetx_types [] = {
421 { Per_Cyc ,"Per/Cyc" },
427 { ActCon ,"ActCon" },
429 { DeactCon ,"DeactCon" },
430 { ActTerm ,"ActTerm" },
431 { Retrem ,"Retrem" },
432 { Retloc ,"Retloc" },
434 { Inrogen ,"Inrogen" },
444 { Inro10 ,"Inro10" },
445 { Inro11 ,"Inro11" },
446 { Inro12 ,"Inro12" },
447 { Inro13 ,"Inro13" },
448 { Inro14 ,"Inro14" },
449 { Inro15 ,"Inro15" },
450 { Inro16 ,"Inro16" },
451 { Reqcogen ,"Reqcogen" },
452 { Reqco1 ,"Reqco1" },
453 { Reqco2 ,"Reqco2" },
454 { Reqco3 ,"Reqco3" },
455 { Reqco4 ,"Reqco4" },
456 { UkTypeId ,"UkTypeId" },
457 { UkCauseTx ,"UkCauseTx" },
458 { UkComAdrASDU ,"UkComAdrASDU" },
464 /* Protocol fields to be filtered */
465 static int hf_apdulen = -1;
466 static int hf_apcitype = -1;
467 static int hf_apciutype = -1;
469 static int hf_addr = -1;
470 static int hf_oa = -1;
471 static int hf_typeid = -1;
472 static int hf_causetx = -1;
473 static int hf_nega = -1;
474 static int hf_test = -1;
475 static int hf_ioa = -1;
476 static int hf_numix = -1;
477 static int hf_sq = -1;
479 static gint hf_iec104_asdufloat = -1;
480 static gint hf_iec104_asdunormval = -1;
482 static gint ett_apci = -1;
483 static gint ett_asdu = -1;
485 /* Misc. functions for dissection of signal values */
487 /* ====================================================================
488 void get_CP56Time( td_CP56Time *cp56t, tvbuff_t *tvb, guint8 offset)
490 Dissects the CP56Time2a time (Seven octet binary time)
491 that starts 'offset' bytes in 'tvb'.
492 The time and date is put in struct 'cp56t'
493 ==================================================================== */
494 void get_CP56Time( td_CP56Time *cp56t, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
497 ms = tvb_get_letohs( tvb , *offset );
499 cp56t->cp56t_s = (int)floor(ms/1000);
500 cp56t->cp56t_ms = (int)(ms-(cp56t->cp56t_s*1000));
502 cp56t->cp56t_min = tvb_get_guint8(tvb, *offset);
503 /* "Invalid" -- Todo: test */
504 cp56t->IV = cp56t->cp56t_min & 0x80;
506 cp56t->cp56t_min = cp56t->cp56t_min & 0x3F;
508 cp56t->cp56t_h = 0x1F & tvb_get_guint8(tvb, *offset);
510 cp56t->cp56t_dom = tvb_get_guint8(tvb, *offset);
511 cp56t->cp56t_dow = 0xE0 & cp56t->cp56t_dom;
512 cp56t->cp56t_dow >>= 5;
513 cp56t->cp56t_dom = cp56t->cp56t_dom & 0x1F;
515 cp56t->cp56t_month = 0x0F & tvb_get_guint8(tvb, *offset);
517 cp56t->cp56t_year = 0x7F & tvb_get_guint8(tvb, *offset);
521 if( iec104_header_tree != NULL )
523 /* ---- format yy-mm-dd (dow) hh:mm:ss.ms */
524 proto_tree_add_text(iec104_header_tree, tvb, (*offset)-7, 7,
525 "%.2d-%.2d-%.2d (%d) %.2d:%.2d:%.2d.%.3d (%s)",
526 cp56t->cp56t_year,cp56t->cp56t_month,cp56t->cp56t_dom,
527 cp56t->cp56t_dow,cp56t->cp56t_h,cp56t->cp56t_min,
528 cp56t->cp56t_s,cp56t->cp56t_ms,cp56t->IV?"Invalid":"Valid");
535 /* ====================================================================
536 Information object address (Identifier)
537 ASDU -> Inform Object #1 -> Information object address
538 ==================================================================== */
539 void get_InfoObjectAddress( guint32 *asdu_info_obj_addr, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
541 /* -------- Information object address */
542 *asdu_info_obj_addr = tvb_get_letoh24(tvb, *offset);
543 if( iec104_header_tree != NULL )
545 proto_tree_add_uint(iec104_header_tree, hf_ioa,
546 tvb, *offset, 3, *asdu_info_obj_addr);
555 /* ====================================================================
556 SIQ: Single-point information (IEV 371-02-07) w quality descriptor
557 ==================================================================== */
558 void get_SIQ( td_ValueInfo *value, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
561 siq = tvb_get_guint8(tvb, *offset);
563 value->ON = siq & 0x01;
564 value->OFF = !(value->ON);
565 value->BL = siq & 0x10; /* Blocked (1) */
566 value->SB = siq & 0x20; /* Substituted (1) */
567 value->NT = siq & 0x40; /* Topical (0) / Not topical (1) */
568 /* [Topical <=> if most recent update was succesful] */
569 value->IV = siq & 0x80; /* Invalid (1) */
570 if( iec104_header_tree != NULL )
572 proto_tree_add_text( iec104_header_tree, tvb, *offset, 1, "Value: %s - Status: %s, %s, %s, %s",
573 value->ON?"ON":"OFF", value->BL?"Blocked":"Not blocked",
574 value->SB?"Substituted":"Not Substituted", value->NT?"Not Topical":"Topical",
575 value->IV?"Invalid":"Valid" );
583 /* ====================================================================
584 DIQ: Double-point information (IEV 371-02-08) w quality descriptor
585 ==================================================================== */
586 void get_DIQ( td_ValueInfo *value, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
590 diq = tvb_get_guint8(tvb, *offset);
591 value->IPOS0 = FALSE;
594 value->IPOS3 = FALSE;
595 switch ( diq & 0x03 )
612 value->BL = diq & 0x10; /* Blocked (1) */
613 value->SB = diq & 0x20; /* Substituted (1) */
614 value->NT = diq & 0x40; /* Topical (0) / Not topical (1) */
615 /* [Topical <=> if most recent update was succesful] */
616 value->IV = diq & 0x80; /* Invalid (1) */
618 if( iec104_header_tree != NULL )
620 proto_tree_add_text( iec104_header_tree, tvb, *offset, 1, "Value: %s%s%s%s - Status: %s, %s, %s, %s",
621 value->ON?"ON":"", value->OFF?"OFF":"", value->IPOS0?"IPOS0":"", value->IPOS3?"IPOS3":"",
622 value->BL?"Blocked":"Not blocked", value->SB?"Substituted":"Not Substituted",
623 value->NT?"Not Topical":"Topical", value->IV?"Invalid":"Valid" );
630 /* ====================================================================
631 QDS: Quality descriptor (separate octet)
632 ==================================================================== */
633 void get_QDS( td_ValueInfo *value, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
636 /* -------- QDS quality description */
637 qds = tvb_get_guint8(tvb, *offset);
639 value->OV = qds & 0x01; /* Overflow (1) */
640 value->BL = qds & 0x10; /* Blocked (1) */
641 value->SB = qds & 0x20; /* Substituted (1) */
642 value->NT = qds & 0x40; /* Topical (0) / Not topical (1) */
643 /* [Topical <=> if most recent update was succesful] */
644 value->IV = qds & 0x80; /* Invalid (1) */
645 if( iec104_header_tree != NULL )
647 proto_tree_add_text( iec104_header_tree, tvb, *offset, 1, "Status: %s, %s, %s, %s, %s",
648 value->OV?"Overflow!":"No Overflow", value->BL?"Blocked!":"Not Blocked",
649 value->SB?"Substituted!":"Not Substituted", value->NT?"Not Topical!":"Topical",
650 value->IV?"Invalid!":"Valid" );
657 /* ====================================================================
658 QDP: Quality descriptor for events of protection equipment
660 ==================================================================== */
661 void get_QDP( td_ValueInfo *value _U_, tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_header_tree _U_ )
667 /* ====================================================================
668 VTI: Value with transient state indication
669 ==================================================================== */
670 void get_VTI( td_ValueInfo *value _U_, tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_header_tree _U_ )
676 /* ====================================================================
677 NVA: Normalized value
678 ==================================================================== */
679 void get_NVA( td_ValueInfo *value, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
681 /* Normalized value F16[1..16]<-1..+1-2^-15> */
682 value->MV.NVA = tvb_get_letohs(tvb, *offset);
684 if ( iec104_header_tree != NULL )
686 proto_tree_add_int(iec104_header_tree, hf_iec104_asdunormval,
687 tvb, *offset, 2, value->MV.NVA);
688 /* todo ... presentation as float +/- 1 (val/32767) ... */
694 void get_NVAspt( td_SpInfo *spt, tvbuff_t *tvb, guint8 *offset,
695 proto_tree *iec104_header_tree )
697 /* Normalized value F16[1..16]<-1..+1-2^-15> */
698 spt->SP.NVA = tvb_get_letohs(tvb, *offset);
700 if ( iec104_header_tree != NULL )
702 proto_tree_add_int(iec104_header_tree, hf_iec104_asdunormval,
703 tvb, *offset, 2, spt->SP.NVA);
704 /* todo ... presentation as float +/- 1 */
710 /* ====================================================================
712 ==================================================================== */
713 void get_SVA( td_ValueInfo *value, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
715 /* Scaled value I16[1..16]<-2^15..+2^15-1> */
716 value->MV.SVA = tvb_get_letohs(tvb, *offset);
717 if ( iec104_header_tree != NULL )
719 proto_tree_add_int(iec104_header_tree, hf_iec104_asdunormval,
720 tvb, *offset, 2, value->MV.SVA);
726 void get_SVAspt( td_SpInfo *spt, tvbuff_t *tvb, guint8 *offset,
727 proto_tree *iec104_header_tree )
729 /* Scaled value I16[1..16]<-2^15..+2^15-1> */
730 spt->SP.SVA = tvb_get_letohs(tvb, *offset);
731 if ( iec104_header_tree != NULL )
733 proto_tree_add_int(iec104_header_tree, hf_iec104_asdunormval,
734 tvb, *offset, 2, spt->SP.SVA);
740 /* ====================================================================
741 "FLT": Short floating point number
742 ==================================================================== */
743 void get_FLT( td_ValueInfo *value, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
745 /* -------- IEEE 754 float value */
746 value->MV.FLT = tvb_get_letohieee_float(tvb, *offset);
748 if ( iec104_header_tree != NULL )
750 proto_tree_add_float(iec104_header_tree, hf_iec104_asdufloat,
751 tvb, *offset, 4, value->MV.FLT);
758 void get_FLTspt( td_SpInfo *spt, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
760 /* -------- IEEE 754 float value */
761 spt->SP.FLT = tvb_get_letohieee_float(tvb, *offset);
763 if ( iec104_header_tree != NULL )
765 proto_tree_add_float(iec104_header_tree, hf_iec104_asdufloat,
766 tvb, *offset, 4, spt->SP.FLT);
773 /* ====================================================================
774 todo -- BCR: Binary counter reading
775 ==================================================================== */
776 /* void get_BCR( td_ValueInfo *value, tvbuff_t *tvb, guint8 *offset,
777 proto_tree *iec104_header_tree ); */
779 /* ====================================================================
780 todo -- SEP: Single event of protection equipment
781 ==================================================================== */
782 void get_SEP( td_ValueInfo *value _U_, tvbuff_t *tvb _U_, guint8 *offset _U_, proto_tree *iec104_header_tree _U_ )
788 /* ====================================================================
789 QOC: Qualifier Of Command
790 ==================================================================== */
791 void get_QOC( td_CmdInfo *value, guint8 data )
793 value->ZeroP = FALSE; /* No pulse */
794 value->ShortP = FALSE; /* Short Pulse */
795 value->LongP = FALSE; /* Long Pulse */
796 value->Persist = FALSE; /* Persistent output */
798 value->QU = data & 0x7c;
805 break; /* No additional definition */
807 value->ShortP = TRUE;
813 value->Persist = TRUE;
816 /* case 4..31 --> reserved .. */
820 value->SE = data & 0x80; /* Select (1) / Execute (0) */
825 /* ====================================================================
826 QOS: Qualifier Of Set-point command
827 ==================================================================== */
828 void get_QOS( td_SpInfo *spt, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
831 /* -------- QOS quality description */
832 qos = tvb_get_guint8(tvb, *offset);
834 spt->QL = qos & 0x7F; /* UI7[1..7]<0..127> */
835 spt->SE = qos & 0x80; /* Select (1) / Execute (0) */
837 if( iec104_header_tree != NULL )
839 proto_tree_add_text( iec104_header_tree, tvb, *offset, 1, "Qualifier - QL: %d, S/E: %s",
840 spt->QL, spt->SE?"Select":"Execute" );
848 /* ====================================================================
849 SCO: Single Command (IEV 371-03-02)
850 ==================================================================== */
851 void get_SCO( td_CmdInfo *value, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
855 data = tvb_get_guint8(tvb, *offset);
856 value->ON = data & 0x01;
857 value->OFF = !(value->ON);
860 get_QOC( value, data );
863 if( iec104_header_tree != NULL )
867 proto_tree_add_text( iec104_header_tree, tvb, *offset, 1, "Command: %s%s, Qualifier: %s%s%s%s, %s",
868 value->ON?"ON":"", value->OFF?"OFF":"",
869 value->ZeroP?"No pulse defined":"", value->ShortP?"Short Pulse":"",
870 value->LongP?"Long Pulse":"", value->Persist?"Persistent Output":"",
871 value->SE?"Select":"Execute");
873 proto_tree_add_text( iec104_header_tree, tvb, *offset, 1, "Command: %s%s, Qualifier: QU=%d, %s",
874 value->ON?"ON":"", value->OFF?"OFF":"",
876 value->SE?"Select":"Execute");
884 /* ====================================================================
885 DCO: Double Command (IEV 371-03-03)
886 ==================================================================== */
887 void get_DCO( td_CmdInfo *value, tvbuff_t *tvb, guint8 *offset, proto_tree *iec104_header_tree )
891 data = tvb_get_guint8(tvb, *offset);
894 switch ( data & 0x03 )
908 get_QOC( value, data );
911 if( iec104_header_tree != NULL )
915 proto_tree_add_text( iec104_header_tree, tvb, *offset, 1, "Command: %s%s%s, Qualifier: %s%s%s%s, %s",
916 value->ON?"ON":"", value->OFF?"OFF":"", (value->ON | value->OFF)?"":"Error: On/Off not defined",
917 value->ZeroP?"No pulse defined":"", value->ShortP?"Short Pulse":"",
918 value->LongP?"Long Pulse":"", value->Persist?"Persistent Output":"",
919 value->SE?"Select":"Execute");
921 proto_tree_add_text( iec104_header_tree, tvb, *offset, 1, "Command: %s%s%s, Qualifier: QU=%d, %s",
922 value->ON?"ON":"", value->OFF?"OFF":"", (value->ON | value->OFF)?"":"Error: On/Off not defined",
924 value->SE?"Select":"Execute");
931 /* .... end Misc. functions for dissection of signal values */
934 /* Find the APDU 104 (APDU=APCI+ASDU) length.
935 Includes possible tvb_length-1 bytes that don't form an APDU */
936 static guint get_iec104apdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
941 for (Off= 0; Off <= tvb_length(tvb)- 2; Off++) {
942 Val = tvb_get_guint8(tvb, offset+ Off);
943 if (Val == APCI_START) {
944 return (guint)(Off+ tvb_get_guint8(tvb, offset+ Off+ 1)+ 2);
948 return (guint)(tvb_length(tvb));
952 /* Is is called twice: For 'Packet List' and for 'Packet Details' */
953 static void dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
955 guint Len = tvb_reported_length(tvb);
957 const char *cause_str;
959 struct asduheader * asduh;
961 proto_item * it104 = NULL;
965 guint8 offset = 0; /* byte offset, signal dissection */
966 guint8 offset_start_ioa = 0; /* position first ioa */
968 guint32 asdu_info_obj_addr = 0;
969 proto_item * itSignal = NULL;
970 proto_tree * trSignal;
971 td_ValueInfo value; /* signal value struct */
972 td_CmdInfo cmd; /* command value struct */
973 td_SpInfo spt; /* setpoint value struct */
974 td_CP56Time cp56t; /* time value struct */
976 if (!(check_col(pinfo->cinfo, COL_INFO) || tree)) return; /* Be sure that the function is only called twice */
978 col_set_str(pinfo->cinfo, COL_PROTOCOL, "104asdu");
979 col_clear(pinfo->cinfo, COL_INFO);
981 asduh = ep_alloc(sizeof(struct asduheader));
982 res = ep_strbuf_new_label(NULL);
984 /*** *** START: Common to 'Packet List' and 'Packet Details' *** ***/
985 if (Len >= ASDU_HEAD_LEN) {
987 asduh->AddrLow = tvb_get_guint8(tvb, 4);
988 asduh->AddrHigh = tvb_get_guint8(tvb, 5);
989 asduh->OA = tvb_get_guint8(tvb, 3);
990 asduh->TypeId = tvb_get_guint8(tvb, 0);
991 asduh->TNCause = tvb_get_guint8(tvb, 2);
992 asduh->IOA = tvb_get_letoh24(tvb, 6);
993 Bytex = tvb_get_guint8(tvb, 1);
994 asduh->NumIx = Bytex & 0x7F;
995 asduh->SQ = Bytex & F_SQ;
996 /* Build common string for 'Packet List' and 'Packet Details' */
997 ep_strbuf_printf(res, "%u,%u%s%u ", asduh->AddrLow, asduh->AddrHigh, pinfo->srcport == iec104port ? "->" : "<-", asduh->OA);
998 ep_strbuf_append(res, val_to_str(asduh->TypeId, asdu_types, "<TypeId=%u>"));
999 ep_strbuf_append_c(res, ' ');
1000 cause_str = val_to_str(asduh->TNCause & F_CAUSE, causetx_types, " <CauseTx=%u>");
1001 ep_strbuf_append(res, cause_str);
1002 if (asduh->TNCause & F_NEGA) ep_strbuf_append(res, "_NEGA");
1003 if (asduh->TNCause & F_TEST) ep_strbuf_append(res, "_TEST");
1004 if (asduh->TNCause & (F_TEST | F_NEGA)) {
1005 for (Ind=strlen(cause_str); Ind< 7; Ind++) ep_strbuf_append_c(res, ' ');
1007 ep_strbuf_append_printf(res, " IOA=%d", asduh->IOA);
1008 if (asduh->NumIx > 1) {
1009 if (asduh->SQ == F_SQ) ep_strbuf_append_printf(res, "-%d", asduh->IOA + asduh->NumIx - 1);
1010 else ep_strbuf_append(res, ",...");
1011 ep_strbuf_append_printf(res, " (%u)", asduh->NumIx);
1015 ep_strbuf_printf(res, "<ERR Short Asdu, Len=%u>", Len);
1017 ep_strbuf_append_c(res, ' '); /* We add a space to separate possible APCIs/ASDUs in the same packet */
1018 /*** *** END: Common to 'Packet List' and 'Packet Details' *** ***/
1020 /*** *** DISSECT 'Packet List' *** ***/
1021 if (check_col(pinfo->cinfo, COL_INFO)) {
1022 col_add_str(pinfo->cinfo, COL_INFO, res->str);
1023 col_set_fence(pinfo->cinfo, COL_INFO);
1028 /*** *** DISSECT 'Packet Details' *** ***/
1030 it104 = proto_tree_add_item(tree, proto_iec104asdu, tvb, 0, -1, FALSE);
1032 /* 'Packet Details': ROOT ITEM */
1033 proto_item_append_text(it104, ": %s'%s'", res->str, Len >= ASDU_HEAD_LEN ? val_to_str(asduh->TypeId, asdu_lngtypes, "<Unknown TypeId>") : "");
1035 /* 'Packet Details': TREE */
1036 if (Len < ASDU_HEAD_LEN) return;
1037 trHead = proto_item_add_subtree(it104, ett_asdu);
1039 /* Remember: add_uint, add_boolean, _add_text: value from last parameter.
1040 add_item: value from tvb. */
1041 proto_tree_add_uint(trHead, hf_typeid, tvb, 0, 1, asduh->TypeId);
1042 proto_tree_add_uint(trHead, hf_numix, tvb, 1, 1, asduh->NumIx);
1043 proto_tree_add_uint(trHead, hf_causetx, tvb, 2, 1, asduh->TNCause & F_CAUSE);
1044 proto_tree_add_boolean(trHead, hf_nega, tvb, 2, 1, asduh->TNCause);
1045 proto_tree_add_boolean(trHead, hf_test, tvb, 2, 1, asduh->TNCause);
1046 proto_tree_add_uint(trHead, hf_oa, tvb, 3, 1, asduh->OA);
1047 proto_tree_add_uint(trHead, hf_addr, tvb, 4, 2, asduh->AddrLow+ 256* asduh->AddrHigh);
1048 proto_tree_add_uint(trHead, hf_ioa, tvb, 6, 3, asduh->IOA);
1049 if (asduh->NumIx > 1) proto_tree_add_boolean(trHead, hf_sq, tvb, 1, 1, asduh->SQ);
1051 /* 'Signal Details': TREE */
1052 offset = 6; /* offset position after DUI, already stored in asduh struct */
1053 /* -------- get signal value and status based on ASDU type id */
1055 switch (asduh->TypeId) {
1079 /* create subtree for the signal values ... */
1080 itSignal = proto_tree_add_item( trHead, proto_iec104asdu, tvb, offset, -1, FALSE );
1081 proto_item_append_text(itSignal, ": Value");
1083 trSignal = proto_item_add_subtree( itSignal, ett_asdu );
1085 /* -- object values */
1086 for(i = 0; i < asduh->NumIx; i++)
1088 /* -------- First Information object address */
1091 offset_start_ioa = offset;
1092 /* -------- Information object address */
1093 asdu_info_obj_addr = asduh->IOA;
1094 proto_tree_add_uint(trSignal, hf_ioa,
1095 tvb, offset_start_ioa, 3, asdu_info_obj_addr);
1097 if( Len < (guint)(offset+3) ) {
1098 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1101 offset += 3; /* step over IOA bytes */
1103 /* -------- following Information object address depending on SQ */
1104 if (asduh->SQ) /* <=> SQ=1, info obj addr = startaddr++ */
1106 asdu_info_obj_addr++;
1107 proto_tree_add_uint(trSignal, hf_ioa,
1108 tvb, offset_start_ioa, 3, asdu_info_obj_addr);
1111 } else { /* SQ=0, info obj addr given */
1112 /* -------- Information object address */
1114 if( Len < (guint)(offset+3) ) {
1115 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1118 get_InfoObjectAddress( &asdu_info_obj_addr, tvb, &offset,
1124 switch (asduh->TypeId) {
1125 case M_SP_NA_1: /* 1 Single-point information */
1127 if( Len < (guint)(offset+1) ) {
1128 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1131 get_SIQ( &value, tvb, &offset, trSignal );
1133 case M_DP_NA_1: /* 3 Double-point information */
1135 if( Len < (guint)(offset+1) ) {
1136 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1139 get_DIQ( &value, tvb, &offset, trSignal );
1141 case M_ME_NA_1: /* 9 Measured value, normalized value */
1143 if( Len < (guint)(offset+3) ) {
1144 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1147 get_NVA( &value, tvb, &offset, trSignal );
1148 get_QDS( &value, tvb, &offset, trSignal );
1150 case M_ME_NB_1: /* 11 Measured value, scaled value */
1152 if( Len < (guint)(offset+3) ) {
1153 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1156 get_SVA( &value, tvb, &offset, trSignal );
1157 get_QDS( &value, tvb, &offset, trSignal );
1159 case M_ME_NC_1: /* 13 Measured value, short floating point value */
1161 if( Len < (guint)(offset+5) ) {
1162 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1165 get_FLT( &value, tvb, &offset, trSignal );
1166 get_QDS( &value, tvb, &offset, trSignal );
1168 case M_ME_ND_1: /* 21 Measured value, normalized value without quality descriptor */
1170 if( Len < (guint)(offset+2) ) {
1171 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1174 get_NVA( &value, tvb, &offset, trSignal );
1176 case M_SP_TB_1: /* 30 Single-point information with time tag CP56Time2a */
1178 if( Len < (guint)(offset+8) ) {
1179 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1182 get_SIQ( &value, tvb, &offset, trSignal );
1183 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1185 case M_DP_TB_1: /* 31 Double-point information with time tag CP56Time2a */
1187 if( Len < (guint)(offset+8) ) {
1188 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1191 get_DIQ( &value, tvb, &offset, trSignal );
1192 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1194 case M_ME_TD_1: /* 34 Measured value, normalized value with time tag CP56Time2a */
1196 if( Len < (guint)(offset+10) ) {
1197 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1200 get_NVA( &value, tvb, &offset, trSignal );
1201 get_QDS( &value, tvb, &offset, trSignal );
1202 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1204 case M_ME_TE_1: /* 35 Measured value, scaled value with time tag CP56Time2a */
1206 if( Len < (guint)(offset+10) ) {
1207 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1210 get_SVA( &value, tvb, &offset, trSignal );
1211 get_QDS( &value, tvb, &offset, trSignal );
1212 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1214 case M_ME_TF_1: /* 36 Measured value, short floating point value with time tag CP56Time2a */
1216 if( Len < (guint)(offset+12) ) {
1217 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1220 get_FLT( &value, tvb, &offset, trSignal );
1221 get_QDS( &value, tvb, &offset, trSignal );
1222 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1224 case C_SC_NA_1: /* 45 Single command */
1226 if( Len < (guint)(offset+1) ) {
1227 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1230 get_SCO( &cmd, tvb, &offset, trSignal );
1232 case C_DC_NA_1: /* 46 Double command */
1234 if( Len < (guint)(offset+1) ) {
1235 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1238 get_DCO( &cmd, tvb, &offset, trSignal );
1240 case C_SE_NA_1: /* 48 Set point command, normalized value */
1242 if( Len < (guint)(offset+3) ) {
1243 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1246 get_NVAspt( &spt, tvb, &offset, trSignal );
1247 get_QOS( &spt, tvb, &offset, trSignal );
1249 case C_SE_NB_1: /* 49 Set point command, scaled value */
1251 if( Len < (guint)(offset+3) ) {
1252 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1255 get_SVAspt( &spt, tvb, &offset, trSignal );
1256 get_QOS( &spt, tvb, &offset, trSignal );
1258 case C_SE_NC_1: /* 50 Set point command, short floating point value */
1260 if( Len < (guint)(offset+5) ) {
1261 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1264 get_FLTspt( &spt, tvb, &offset, trSignal );
1265 get_QOS( &spt, tvb, &offset, trSignal );
1267 case C_SC_TA_1: /* 58 Single command with time tag CP56Time2a */
1269 if( Len < (guint)(offset+8) ) {
1270 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1273 get_SCO( &cmd, tvb, &offset, trSignal );
1274 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1276 case C_DC_TA_1: /* 59 Double command with time tag CP56Time2a */
1278 if( Len < (guint)(offset+8) ) {
1279 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1282 get_DCO( &cmd, tvb, &offset, trSignal );
1283 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1285 case C_SE_TA_1: /* 61 Set point command, normalized value with time tag CP56Time2a */
1287 if( Len < (guint)(offset+10) ) {
1288 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1291 get_NVAspt( &spt, tvb, &offset, trSignal );
1292 get_QOS( &spt, tvb, &offset, trSignal );
1293 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1295 case C_SE_TB_1: /* 62 Set point command, scaled value with time tag CP56Time2a */
1297 if( Len < (guint)(offset+10) ) {
1298 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1301 get_SVAspt( &spt, tvb, &offset, trSignal );
1302 get_QOS( &spt, tvb, &offset, trSignal );
1303 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1305 case C_SE_TC_1: /* 63 Set point command, short floating point value with time tag CP56Time2a */
1307 if( Len < (guint)(offset+12) ) {
1308 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1311 get_FLTspt( &spt, tvb, &offset, trSignal );
1312 get_QOS( &spt, tvb, &offset, trSignal );
1313 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1315 case C_CS_NA_1: /* 103 clock synchronization command */
1317 if( Len < (guint)(offset+7) ) {
1318 proto_tree_add_text( trSignal, tvb, offset, 1, "<ERR Short Asdu>" );
1321 get_CP56Time( &cp56t, tvb, &offset, trSignal );
1326 } /* end 'switch (asduh->TypeId)' */
1327 } /* end 'for(i = 0; i < dui.asdu_vsq_no_of_obj; i++)' */
1331 } /* end 'switch (asdu_typeid)' */
1337 /* Is is called twice: For 'Packet List' and for 'Packet Details' */
1338 static void dissect_iec104apci(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1340 guint8 TcpLen = tvb_length(tvb);
1345 struct apciheader * apcih;
1346 emem_strbuf_t * res;
1347 proto_item * it104 = NULL;
1348 proto_tree * trHead;
1350 if (!(check_col(pinfo->cinfo, COL_INFO) || tree)) return; /* Be sure that the function is only called twice */
1352 col_set_str(pinfo->cinfo, COL_PROTOCOL, "104apci");
1353 col_clear(pinfo->cinfo, COL_INFO);
1355 apcih = ep_alloc(sizeof(struct apciheader));
1357 /*** *** START: Common to 'Packet List' and 'Packet Details' *** ***/
1359 for (Off= 0; Off <= TcpLen- 2; Off++) {
1360 Start = tvb_get_guint8(tvb, Off);
1361 if (Start == APCI_START) {
1363 apcih->ApduLen = tvb_get_guint8(tvb, Off+ 1);
1364 if (apcih->ApduLen >= APDU_MIN_LEN) {
1365 Byte1 = tvb_get_guint8(tvb, Off+ 2);
1366 apcih->Type = Byte1 & 0x03;
1367 /* Type I is only lowest bit set to 0 */
1368 if (apcih->Type == 2) apcih->Type = 0;
1369 switch(apcih->Type) {
1371 apcih->Tx = tvb_get_letohs(tvb, Off+ 2) >> 1;
1373 apcih->Rx = tvb_get_letohs(tvb, Off+ 4) >> 1;
1376 apcih->UType = (Byte1 & 0xFC); /* Don't shift */
1381 /* WireShark can crash if we process packets with length less than expected (6). We consider that everything is bad */
1384 /* Don't search more the APCI_START */
1388 if (Start != APCI_START) {
1389 /* Everything is bad (no APCI found) */
1392 /* Construir string comu a List i Details */
1393 res = ep_strbuf_new_label(NULL);
1395 ep_strbuf_append_printf(res, "<ERR %u bytes> ", Brossa);
1396 if (Brossa != TcpLen) {
1397 if (apcih->ApduLen <= APDU_MAX_LEN) {
1398 /* APCI in 'Paquet List' */
1399 ep_strbuf_append_printf(res, "%s%s(", pinfo->srcport == iec104port ? "->" : "<-", val_to_str(apcih->Type, apci_types, "<ERR>"));
1400 switch(apcih->Type) { /* APCI in 'Packet List' */
1402 ep_strbuf_append_printf(res, "%d,", apcih->Tx);
1404 ep_strbuf_append_printf(res, "%d)", apcih->Rx);
1405 /* Align first packets */
1407 ep_strbuf_append_c(res, ' ');
1409 ep_strbuf_append_c(res, ' ');
1412 ep_strbuf_append_printf(res, "%s)", val_to_str(apcih->UType >> 2, u_types, "<ERR>"));
1415 if (apcih->Type != I_TYPE && apcih->ApduLen > APDU_MIN_LEN) ep_strbuf_append_printf(res, "<ERR %u bytes> ", apcih->ApduLen- APDU_MIN_LEN);
1418 ep_strbuf_append_printf(res, "<ERR ApduLen=%u bytes> ", apcih->ApduLen);
1421 ep_strbuf_append_c(res, ' '); /* We add a space to separate possible APCIs/ASDUs in the same packet */
1422 /*** *** END: Common to 'Packet List' and 'Packet Details' *** ***/
1424 /*** *** Dissect 'Packet List' *** ***/
1425 if (check_col(pinfo->cinfo, COL_INFO)) {
1426 col_add_str(pinfo->cinfo, COL_INFO, res->str);
1427 if(apcih->Type == I_TYPE && Brossa != TcpLen) {
1428 call_dissector(iec104asdu_handle, tvb_new_subset(tvb, Off+ APCI_LEN, -1, apcih->ApduLen- APCI_DATA_LEN), pinfo, tree);
1430 col_set_fence(pinfo->cinfo, COL_INFO);
1436 /*** *** DISSECT 'Packet Details' *** ***/
1438 it104 = proto_tree_add_item(tree, proto_iec104apci, tvb, 0, Off+ APCI_LEN, FALSE);
1440 /* 'Packet Details': ROOT ITEM */
1441 proto_item_append_text(it104, ": %s", res->str);
1443 if(Brossa == TcpLen) return;
1445 /* Don't call ASDU dissector if it was called before */
1446 if(apcih->Type == I_TYPE && (!check_col(pinfo->cinfo, COL_INFO))){
1447 call_dissector(iec104asdu_handle, tvb_new_subset(tvb, Off+ APCI_LEN, -1, apcih->ApduLen- APCI_DATA_LEN), pinfo, tree);
1450 /* 'Packet Details': TREE */
1451 trHead = proto_item_add_subtree(it104, ett_apci);
1452 /* Remember: add_uint, add_boolean, _add_text: value from last parameter.
1453 add_item: value from tvb. */
1454 proto_tree_add_uint(trHead, hf_apdulen, tvb, Off+ 1, 1, apcih->ApduLen);
1455 proto_tree_add_uint(trHead, hf_apcitype, tvb, Off+ 2, 1, apcih->Type);
1456 switch(apcih->Type){
1458 proto_tree_add_uint(trHead, hf_apciutype, tvb, Off+ 2, 1, apcih->UType); /* Don't shift the value */
1467 static void dissect_iec104reas(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1469 /* 5th parameter = 6 = minimum bytes received to calculate the length.
1470 * (Not 2 in order to find more APCIs in case of 'noisy' bytes between the APCIs)
1472 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, APCI_LEN,
1473 get_iec104apdu_len, dissect_iec104apci);
1477 /* The protocol has two subprotocols: Register APCI */
1479 proto_register_iec104apci(void)
1482 static hf_register_info hf_ap[] = {
1485 { "ApduLen", "104apci.apdulen", FT_UINT8, BASE_DEC, NULL, 0x0,
1486 "APDU Len", HFILL }},
1489 { "ApciType", "104apci.type", FT_UINT8, BASE_HEX, VALS(apci_types), 0x03,
1490 "APCI type", HFILL }},
1493 { "ApciUType", "104apci.utype", FT_UINT8, BASE_HEX, VALS(u_types), 0xFC,
1494 "Apci U type", HFILL }},
1498 static gint *ett_ap[] = {
1502 proto_iec104apci = proto_register_protocol(
1503 "IEC 60870-5-104-Apci",
1507 proto_register_field_array(proto_iec104apci, hf_ap, array_length(hf_ap));
1508 proto_register_subtree_array(ett_ap, array_length(ett_ap));
1513 /* The protocol has two subprotocols: Register ASDU */
1515 proto_register_iec104asdu(void)
1518 static hf_register_info hf_as[] = {
1521 { "Addr", "104asdu.addr", FT_UINT16, BASE_DEC, NULL, 0x0,
1522 "Common Address of Asdu", HFILL }},
1525 { "OA", "104asdu.oa", FT_UINT8, BASE_DEC, NULL, 0x0,
1526 "Originator Address", HFILL }},
1529 { "TypeId", "104asdu.typeid", FT_UINT8, BASE_DEC, VALS(asdu_types), 0x0,
1530 "Asdu Type Id", HFILL }},
1533 { "CauseTx", "104asdu.causetx", FT_UINT8, BASE_DEC, VALS(causetx_types), 0x3F,
1534 "Cause of Transmision", HFILL }},
1537 { "Negative", "104asdu.nega", FT_BOOLEAN, 8, NULL, F_NEGA,
1541 { "Test", "104asdu.test", FT_BOOLEAN, 8, NULL, F_TEST,
1546 { "IOA", "104asdu.ioa", FT_UINT24, BASE_DEC, NULL, 0x0,
1547 "Information Object Address", HFILL }},
1550 { "NumIx", "104asdu.numix", FT_UINT8, BASE_DEC, NULL, 0x7F,
1551 "Number of Information Objects/Elements", HFILL }},
1554 { "SQ", "104asdu.sq", FT_BOOLEAN, 8, NULL, F_SQ,
1555 "Sequence", HFILL }},
1557 { &hf_iec104_asdufloat,
1558 { "Object value", "iec104.asdu_float", FT_FLOAT, BASE_NONE, NULL, 0x0,
1559 "Object value", HFILL }},
1561 { &hf_iec104_asdunormval,
1562 { "Object value", "iec104.asdu_asdunormval", FT_INT16, BASE_DEC, NULL, 0x0,
1563 "Object value", HFILL }},
1567 static gint *ett_as[] = {
1571 proto_iec104asdu = proto_register_protocol(
1572 "IEC 60870-5-104-Asdu",
1576 proto_register_field_array(proto_iec104asdu, hf_as, array_length(hf_as));
1577 proto_register_subtree_array(ett_as, array_length(ett_as));
1583 /* The registration hand-off routine */
1585 proto_reg_handoff_iec104(void)
1587 dissector_handle_t iec104apci_handle;
1589 iec104apci_handle = create_dissector_handle(dissect_iec104reas, proto_iec104apci);
1590 iec104asdu_handle = create_dissector_handle(dissect_iec104asdu, proto_iec104asdu);
1592 dissector_add("tcp.port", iec104port, iec104apci_handle);