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 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1999 Gerald Combs
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
37 #include <epan/packet.h>
38 #include <epan/dissectors/packet-tcp.h>
39 #include <epan/prefs.h>
40 #include <epan/emem.h>
42 /* IEC-104 comment: Fields are little endian. */
46 static dissector_handle_t iec104asdu_handle;
48 /******* Utility to add to 'value_string.c' *******/
49 /* Tries to match val against each element in the value_string array vs.
50 Returns the associated string length on a match.
51 Returns 100, on failure. */
52 guint8 val_to_strlen(guint32 val, const value_string *vs);
54 guint8 val_to_strlen(guint32 val, const value_string *vs) {
56 ret = match_strval(val, vs);
64 /* the asdu header structure */
76 /* the apci header structure */
87 static guint iec104port = 2404;
89 /* Define the iec104 proto */
90 static int proto_iec104apci = -1;
91 static int proto_iec104asdu = -1;
93 /* Protocol constants */
94 #define APCI_START 0x68
96 #define APDU_MIN_LEN 4
97 #define APDU_MAX_LEN 253
99 /* ASDU_HEAD_LEN: Includes Asdu head and first IOA */
100 #define ASDU_HEAD_LEN 9
110 #define APCI_TYPE_UNKNOWN 4
111 static const value_string apci_types [] = {
119 /* Constants relative to the filed, independent of the field position in the byte */
120 /* U (Unnombered) constants */
121 #define U_STARTDT_ACT 0x01
122 #define U_STARTDT_CON 0x02
123 #define U_STOPDT_ACT 0x04
124 #define U_STOPDT_CON 0x08
125 #define U_TESTFR_ACT 0x10
126 #define U_TESTFR_CON 0x20
127 static const value_string u_types[] = {
128 { U_STARTDT_ACT, "STARTDT act" },
129 { U_STARTDT_CON, "STARTDT con" },
130 { U_STOPDT_ACT, "STOPDT act" },
131 { U_STOPDT_CON, "STOPDT con" },
132 { U_TESTFR_ACT, "TESTFR act" },
133 { U_TESTFR_CON, "TESTFR con" },
138 /* ASDU types (TypeId) */
139 #define M_SP_NA_1 1 /* single-point information */
140 #define M_DP_NA_1 3 /* double-point information */
141 #define M_ST_NA_1 5 /* step position information */
142 #define M_BO_NA_1 7 /* bitstring of 32 bits */
143 #define M_ME_NA_1 9 /* measured value, normalized value */
144 #define M_ME_NB_1 11 /* measured value, scaled value */
145 #define M_ME_NC_1 13 /* measured value, short floating point number */
146 #define M_IT_NA_1 15 /* integrated totals */
147 #define M_PS_NA_1 20 /* packed single-point information with status change detection */
148 #define M_ME_ND_1 21 /* measured value, normalized value without quality descriptor */
149 #define M_SP_TB_1 30 /* single-point information with time tag CP56Time2a */
150 #define M_DP_TB_1 31 /* double-point information with time tag CP56Time2a */
151 #define M_ST_TB_1 32 /* step position information with time tag CP56Time2a */
152 #define M_BO_TB_1 33 /* bitstring of 32 bit with time tag CP56Time2a */
153 #define M_ME_TD_1 34 /* measured value, normalized value with time tag CP56Time2a */
154 #define M_ME_TE_1 35 /* measured value, scaled value with time tag CP56Time2a */
155 #define M_ME_TF_1 36 /* measured value, short floating point number with time tag CP56Time2a */
156 #define M_IT_TB_1 37 /* integrated totals with time tag CP56Time2a */
157 #define M_EP_TD_1 38 /* event of protection equipment with time tag CP56Time2a */
158 #define M_EP_TE_1 39 /* packed start events of protection equipment with time tag CP56Time2a */
159 #define M_EP_TF_1 40 /* packed output circuit information of protection equipment with time tag CP56Time2a */
160 #define C_SC_NA_1 45 /* single command */
161 #define C_DC_NA_1 46 /* double command */
162 #define C_RC_NA_1 47 /* regulating step command */
163 #define C_SE_NA_1 48 /* set point command, normalized value */
164 #define C_SE_NB_1 49 /* set point command, scaled value */
165 #define C_SE_NC_1 50 /* set point command, short floating point number */
166 #define C_BO_NA_1 51 /* bitstring of 32 bits */
167 #define C_SC_TA_1 58 /* single command with time tag CP56Time2a */
168 #define C_DC_TA_1 59 /* double command with time tag CP56Time2a */
169 #define C_RC_TA_1 60 /* regulating step command with time tag CP56Time2a */
170 #define C_SE_TA_1 61 /* set point command, normalized value with time tag CP56Time2a */
171 #define C_SE_TB_1 62 /* set point command, scaled value with time tag CP56Time2a */
172 #define C_SE_TC_1 63 /* set point command, short floating-point number with time tag CP56Time2a */
173 #define C_BO_TA_1 64 /* bitstring of 32 bits with time tag CP56Time2a */
174 #define M_EI_NA_1 70 /* end of initialization */
175 #define C_IC_NA_1 100 /* interrogation command */
176 #define C_CI_NA_1 101 /* counter interrogation command */
177 #define C_RD_NA_1 102 /* read command */
178 #define C_CS_NA_1 103 /* clock synchronization command */
179 #define C_RP_NA_1 105 /* reset process command */
180 #define C_TS_TA_1 107 /* test command with time tag CP56Time2a */
181 #define P_ME_NA_1 110 /* parameter of measured value, normalized value */
182 #define P_ME_NB_1 111 /* parameter of measured value, scaled value */
183 #define P_ME_NC_1 112 /* parameter of measured value, short floating-point number */
184 #define P_AC_NA_1 113 /* parameter activation */
185 #define F_FR_NA_1 120 /* file ready */
186 #define F_SR_NA_1 121 /* section ready */
187 #define F_SC_NA_1 122 /* call directory, select file, call file, call section */
188 #define F_LS_NA_1 123 /* last section, last segment */
189 #define F_AF_NA_1 124 /* ack file, ack section */
190 #define F_SG_NA_1 125 /* segment */
191 #define F_DR_TA_1 126 /* directory */
192 #define F_SC_NB_1 127 /* Query Log - Request archive file */
193 static const value_string asdu_types [] = {
194 { M_SP_NA_1, "M_SP_NA_1" },
195 { M_DP_NA_1, "M_DP_NA_1" },
196 { M_ST_NA_1, "M_ST_NA_1" },
197 { M_BO_NA_1, "M_BO_NA_1" },
198 { M_ME_NA_1, "M_ME_NA_1" },
199 { M_ME_NB_1, "M_ME_NB_1" },
200 { M_ME_NC_1, "M_ME_NC_1" },
201 { M_IT_NA_1, "M_IT_NA_1" },
202 { M_PS_NA_1, "M_PS_NA_1" },
203 { M_ME_ND_1, "M_ME_ND_1" },
204 { M_SP_TB_1, "M_SP_TB_1" },
205 { M_DP_TB_1, "M_DP_TB_1" },
206 { M_ST_TB_1, "M_ST_TB_1" },
207 { M_BO_TB_1, "M_BO_TB_1" },
208 { M_ME_TD_1, "M_ME_TD_1" },
209 { M_ME_TE_1, "M_ME_TE_1" },
210 { M_ME_TF_1, "M_ME_TF_1" },
211 { M_IT_TB_1, "M_IT_TB_1" },
212 { M_EP_TD_1, "M_EP_TD_1" },
213 { M_EP_TE_1, "M_EP_TE_1" },
214 { M_EP_TF_1, "M_EP_TF_1" },
215 { C_SC_NA_1, "C_SC_NA_1" },
216 { C_DC_NA_1, "C_DC_NA_1" },
217 { C_RC_NA_1, "C_RC_NA_1" },
218 { C_SE_NA_1, "C_SE_NA_1" },
219 { C_SE_NB_1, "C_SE_NB_1" },
220 { C_SE_NC_1, "C_SE_NC_1" },
221 { C_BO_NA_1, "C_BO_NA_1" },
222 { C_SC_TA_1, "C_SC_TA_1" },
223 { C_DC_TA_1, "C_DC_TA_1" },
224 { C_RC_TA_1, "C_RC_TA_1" },
225 { C_SE_TA_1, "C_SE_TA_1" },
226 { C_SE_TB_1, "C_SE_TB_1" },
227 { C_SE_TC_1, "C_SE_TC_1" },
228 { C_BO_TA_1, "C_BO_TA_1" },
229 { M_EI_NA_1, "M_EI_NA_1" },
230 { C_IC_NA_1, "C_IC_NA_1" },
231 { C_CI_NA_1, "C_CI_NA_1" },
232 { C_RD_NA_1, "C_RD_NA_1" },
233 { C_CS_NA_1, "C_CS_NA_1" },
234 { C_RP_NA_1, "C_RP_NA_1" },
235 { C_TS_TA_1, "C_TS_TA_1" },
236 { P_ME_NA_1, "P_ME_NA_1" },
237 { P_ME_NB_1, "P_ME_NB_1" },
238 { P_ME_NC_1, "P_ME_NC_1" },
239 { P_AC_NA_1, "P_AC_NA_1" },
240 { F_FR_NA_1, "F_FR_NA_1" },
241 { F_SR_NA_1, "F_SR_NA_1" },
242 { F_SC_NA_1, "F_SC_NA_1" },
243 { F_LS_NA_1, "F_LS_NA_1" },
244 { F_AF_NA_1, "F_AF_NA_1" },
245 { F_SG_NA_1, "F_SG_NA_1" },
246 { F_DR_TA_1, "F_DR_TA_1" },
247 { F_SC_NB_1, "F_SC_NB_1" },
250 static const value_string asdu_lngtypes [] = {
251 { M_SP_NA_1, "single-point information" },
252 { M_DP_NA_1, "double-point information" },
253 { M_ST_NA_1, "step position information" },
254 { M_BO_NA_1, "bitstring of 32 bits" },
255 { M_ME_NA_1, "measured value, normalized value" },
256 { M_ME_NB_1, "measured value, scaled value" },
257 { M_ME_NC_1, "measured value, short floating point number" },
258 { M_IT_NA_1, "integrated totals" },
259 { M_PS_NA_1, "packed single-point information with status change detection" },
260 { M_ME_ND_1, "measured value, normalized value without quality descriptor" },
261 { M_SP_TB_1, "single-point information with time tag CP56Time2a" },
262 { M_DP_TB_1, "double-point information with time tag CP56Time2a" },
263 { M_ST_TB_1, "step position information with time tag CP56Time2a" },
264 { M_BO_TB_1, "bitstring of 32 bit with time tag CP56Time2a" },
265 { M_ME_TD_1, "measured value, normalized value with time tag CP56Time2a" },
266 { M_ME_TE_1, "measured value, scaled value with time tag CP56Time2a" },
267 { M_ME_TF_1, "measured value, short floating point number with time tag CP56Time2a" },
268 { M_IT_TB_1, "integrated totals with time tag CP56Time2a" },
269 { M_EP_TD_1, "event of protection equipment with time tag CP56Time2a" },
270 { M_EP_TE_1, "packed start events of protection equipment with time tag CP56Time2a" },
271 { M_EP_TF_1, "packed output circuit information of protection equipment with time tag CP56Time2a" },
272 { C_SC_NA_1, "single command" },
273 { C_DC_NA_1, "double command" },
274 { C_RC_NA_1, "regulating step command" },
275 { C_SE_NA_1, "set point command, normalized value" },
276 { C_SE_NB_1, "set point command, scaled value" },
277 { C_SE_NC_1, "set point command, short floating point number" },
278 { C_BO_NA_1, "bitstring of 32 bits" },
279 { C_SC_TA_1, "single command with time tag CP56Time2a" },
280 { C_DC_TA_1, "double command with time tag CP56Time2a" },
281 { C_RC_TA_1, "regulating step command with time tag CP56Time2a" },
282 { C_SE_TA_1, "set point command, normalized value with time tag CP56Time2a" },
283 { C_SE_TB_1, "set point command, scaled value with time tag CP56Time2a" },
284 { C_SE_TC_1, "set point command, short floating-point number with time tag CP56Time2a" },
285 { C_BO_TA_1, "bitstring of 32 bits with time tag CP56Time2a" },
286 { M_EI_NA_1, "end of initialization" },
287 { C_IC_NA_1, "interrogation command" },
288 { C_CI_NA_1, "counter interrogation command" },
289 { C_RD_NA_1, "read command" },
290 { C_CS_NA_1, "clock synchronization command" },
291 { C_RP_NA_1, "reset process command" },
292 { C_TS_TA_1, "test command with time tag CP56Time2a" },
293 { P_ME_NA_1, "parameter of measured value, normalized value" },
294 { P_ME_NB_1, "parameter of measured value, scaled value" },
295 { P_ME_NC_1, "parameter of measured value, short floating-point number" },
296 { P_AC_NA_1, "parameter activation" },
297 { F_FR_NA_1, "file ready" },
298 { F_SR_NA_1, "section ready" },
299 { F_SC_NA_1, "call directory, select file, call file, call section" },
300 { F_LS_NA_1, "last section, last segment" },
301 { F_AF_NA_1, "ack file, ack section" },
302 { F_SG_NA_1, "segment" },
303 { F_DR_TA_1, "directory" },
304 { F_SC_NB_1, "Query Log - Request archive file" },
309 /* Cause of Transmision (CauseTx) */
347 #define UkComAdrASDU 46
349 static const value_string causetx_types [] = {
350 { Per_Cyc ,"Per/Cyc" },
356 { ActCon ,"ActCon" },
358 { DeactCon ,"DeactCon" },
359 { ActTerm ,"ActTerm" },
360 { Retrem ,"Retrem" },
361 { Retloc ,"Retloc" },
363 { Inrogen ,"Inrogen" },
373 { Inro10 ,"Inro10" },
374 { Inro11 ,"Inro11" },
375 { Inro12 ,"Inro12" },
376 { Inro13 ,"Inro13" },
377 { Inro14 ,"Inro14" },
378 { Inro15 ,"Inro15" },
379 { Inro16 ,"Inro16" },
380 { Reqcogen ,"Reqcogen" },
381 { Reqco1 ,"Reqco1" },
382 { Reqco2 ,"Reqco2" },
383 { Reqco3 ,"Reqco3" },
384 { Reqco4 ,"Reqco4" },
385 { UkTypeId ,"UkTypeId" },
386 { UkCauseTx ,"UkCauseTx" },
387 { UkComAdrASDU ,"UkComAdrASDU" },
393 /* Protocol fields to be filtered */
394 static int hf_apdulen = -1;
395 static int hf_apcitype = -1;
396 static int hf_apciutype = -1;
398 static int hf_addr = -1;
399 static int hf_oa = -1;
400 static int hf_typeid = -1;
401 static int hf_causetx = -1;
402 static int hf_nega = -1;
403 static int hf_test = -1;
404 static int hf_ioa = -1;
405 static int hf_numix = -1;
406 static int hf_sq = -1;
409 static gint ett_apci = -1;
410 static gint ett_asdu = -1;
413 /* Find the APDU 104 (APDU=APCI+ASDU) length.
414 Includes possible tvb_length-1 bytes that don't form an APDU */
415 static guint get_iec104apdu_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset)
420 for (Off= 0; Off <= tvb_length(tvb)- 2; Off++) {
421 Val = tvb_get_guint8(tvb, offset+ Off);
422 if (Val == APCI_START) {
423 return (guint)(Off+ tvb_get_guint8(tvb, offset+ Off+ 1)+ 2);
427 return (guint)(tvb_length(tvb));
431 /* Is is called twice: For 'Packet List' and for 'Packet Details' */
432 static void dissect_iec104asdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
434 guint8 Len = tvb_length(tvb); /* Investigate also: 'reported Length' */
437 struct asduheader * asduh;
439 proto_item * it104 = NULL;
442 if (!(check_col(pinfo->cinfo, COL_INFO) || tree)) return; /* Be sure that the function is only called twice */
444 asduh = ep_alloc(sizeof(struct asduheader));
445 res = ep_alloc(MAXS);
448 if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
449 col_set_str(pinfo->cinfo, COL_PROTOCOL, "104asdu");
451 if (check_col(pinfo->cinfo, COL_INFO)) {
452 col_clear(pinfo->cinfo, COL_INFO);
455 /*** *** START: Common to 'Packet List' and 'Packet Details' *** ***/
456 if (Len >= ASDU_HEAD_LEN) {
458 asduh->AddrLow = tvb_get_guint8(tvb, 4);
459 asduh->AddrHigh = tvb_get_guint8(tvb, 5);
460 asduh->OA = tvb_get_guint8(tvb, 3);
461 asduh->TypeId = tvb_get_guint8(tvb, 0);
462 asduh->TNCause = tvb_get_guint8(tvb, 2);
463 asduh->IOA = tvb_get_letoh24(tvb, 6);
464 Bytex = tvb_get_guint8(tvb, 1);
465 asduh->NumIx = Bytex & 0x7F;
466 asduh->SQ = Bytex & F_SQ;
467 /* Build common string for 'Packet List' and 'Packet Details' */
468 g_snprintf(res, MAXS, "%u,%u%s%u ", asduh->AddrLow, asduh->AddrHigh, pinfo->srcport == iec104port ? "->" : "<-", asduh->OA);
469 g_strlcat(res, val_to_str(asduh->TypeId, asdu_types, "<TypeId=%u>"), MAXS);
470 g_strlcat(res, " ", MAXS);
471 g_strlcat(res, val_to_str(asduh->TNCause & F_CAUSE, causetx_types, " <CauseTx=%u>"), MAXS);
472 if (asduh->TNCause & F_NEGA) g_strlcat(res, "_NEGA", MAXS);
473 if (asduh->TNCause & F_TEST) g_strlcat(res, "_TEST", MAXS);
474 if (asduh->TNCause & (F_TEST | F_NEGA)) {
475 Bytex = val_to_strlen(asduh->TNCause & F_CAUSE, causetx_types);
476 for (Ind=Bytex; Ind< 7; Ind++) g_strlcat(res, " ", MAXS);
478 g_snprintf(res+strlen(res), MAXS-strlen(res), " IOA=%d", asduh->IOA);
479 if (asduh->NumIx > 1) {
480 if (asduh->SQ == F_SQ) g_snprintf(res+strlen(res), MAXS-strlen(res), "-%d", asduh->IOA+ asduh->NumIx- 1);
481 else g_strlcat(res, ",...", MAXS);
482 g_snprintf(res+strlen(res), MAXS-strlen(res), " (%u)", asduh->NumIx);
486 g_snprintf(res, MAXS, "<ERR Short Asdu, Len=%u>", Len);
488 g_strlcat(res, " ", MAXS); /* We add an space to separate possible APCIs/ASDUs in the same packet */
489 /*** *** END: Common to 'Packet List' and 'Packet Details' *** ***/
491 /*** *** DISSECT 'Packet List' *** ***/
492 if (check_col(pinfo->cinfo, COL_INFO)) {
493 col_add_str(pinfo->cinfo, COL_INFO, res);
494 col_set_fence(pinfo->cinfo, COL_INFO);
499 /*** *** DISSECT 'Packet Details' *** ***/
501 it104 = proto_tree_add_item(tree, proto_iec104asdu, tvb, 0, -1, FALSE);
503 /* 'Packet Details': ROOT ITEM */
504 proto_item_append_text(it104, ": %s'%s'", res, Len >= ASDU_HEAD_LEN ? val_to_str(asduh->TypeId, asdu_lngtypes, "<Unknown TypeId>") : "");
506 /* 'Packet Details': TREE */
507 if (Len < ASDU_HEAD_LEN) return;
508 trHead = proto_item_add_subtree(it104, ett_asdu);
510 /* Remember: add_uint, add_boolean, _add_text: value from last parameter.
511 add_item: value from tvb. */
512 proto_tree_add_uint(trHead, hf_typeid, tvb, 0, 1, asduh->TypeId);
513 proto_tree_add_uint(trHead, hf_numix, tvb, 1, 1, asduh->NumIx);
514 proto_tree_add_uint(trHead, hf_causetx, tvb, 2, 1, asduh->TNCause & F_CAUSE);
515 proto_tree_add_boolean(trHead, hf_nega, tvb, 2, 1, asduh->TNCause);
516 proto_tree_add_boolean(trHead, hf_test, tvb, 2, 1, asduh->TNCause);
517 proto_tree_add_uint(trHead, hf_oa, tvb, 3, 1, asduh->OA);
518 proto_tree_add_uint(trHead, hf_addr, tvb, 4, 2, asduh->AddrLow+ 256* asduh->AddrHigh);
519 proto_tree_add_uint(trHead, hf_ioa, tvb, 6, 3, asduh->IOA);
520 if (asduh->NumIx > 1) proto_tree_add_boolean(trHead, hf_sq, tvb, 1, 1, asduh->SQ);
525 /* Is is called twice: For 'Packet List' and for 'Packet Details' */
526 static void dissect_iec104apci(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
528 guint8 TcpLen = tvb_length(tvb);
533 struct apciheader * apcih;
535 proto_item * it104 = NULL;
538 if (!(check_col(pinfo->cinfo, COL_INFO) || tree)) return; /* Be sure that the function is only called twice */
540 apcih = ep_alloc(sizeof(struct apciheader));
541 res = ep_alloc(MAXS);
544 if (check_col(pinfo->cinfo, COL_PROTOCOL)) {
545 col_set_str(pinfo->cinfo, COL_PROTOCOL, "104apci");
547 if (check_col(pinfo->cinfo, COL_INFO)) {
548 col_clear(pinfo->cinfo, COL_INFO);
551 /*** *** START: Common to 'Packet List' and 'Packet Details' *** ***/
553 for (Off= 0; Off <= TcpLen- 2; Off++) {
554 Start = tvb_get_guint8(tvb, Off);
555 if (Start == APCI_START) {
557 apcih->ApduLen = tvb_get_guint8(tvb, Off+ 1);
558 if (apcih->ApduLen >= APDU_MIN_LEN) {
559 Byte1 = tvb_get_guint8(tvb, Off+ 2);
560 apcih->Type = Byte1 & 0x03;
561 /* Type I is only lowest bit set to 0 */
562 if (apcih->Type == 2) apcih->Type = 0;
563 switch(apcih->Type) {
565 apcih->Tx = tvb_get_letohs(tvb, Off+ 2) >> 1;
567 apcih->Rx = tvb_get_letohs(tvb, Off+ 4) >> 1;
570 apcih->UType = (Byte1 & 0xFC); /* Don't shift */
575 /* WireShark can crash if we process packets with length less than expected (6). We consider that everything is bad */
578 /* Don't search more the APCI_START */
582 if (Start != APCI_START) {
583 /* Everything is bad (no APCI found) */
586 /* Construir string comu a List i Details */
587 if (Brossa > 0) g_snprintf(res, MAXS, "<ERR %u bytes> ", Brossa);
588 if (Brossa != TcpLen) {
589 if (apcih->ApduLen <= APDU_MAX_LEN) {
590 /* APCI in 'Paquet List' */
591 g_snprintf(res+strlen(res), MAXS-strlen(res), "%s%s(", pinfo->srcport == iec104port ? "->" : "<-", val_to_str(apcih->Type, apci_types, "<ERR>"));
592 switch(apcih->Type) { /* APCI in 'Packet List' */
594 g_snprintf(res+strlen(res), MAXS-strlen(res), "%d,", apcih->Tx);
596 g_snprintf(res+strlen(res), MAXS-strlen(res), "%d)", apcih->Rx);
597 /* Align first packets */
598 if (apcih->Tx < 10) g_strlcat(res, " ", MAXS);
599 if (apcih->Rx < 10) g_strlcat(res, " ", MAXS);
602 g_snprintf(res+strlen(res), MAXS-strlen(res), "%s)", val_to_str(apcih->UType >> 2, u_types, "<ERR>"));
605 if (apcih->Type != I_TYPE && apcih->ApduLen > APDU_MIN_LEN) g_snprintf(res+strlen(res), MAXS-strlen(res), "<ERR %u bytes> ", apcih->ApduLen- APDU_MIN_LEN);
608 g_snprintf(res+strlen(res), MAXS-strlen(res), "<ERR ApduLen=%u bytes> ", apcih->ApduLen);
611 g_strlcat(res, " ", MAXS); /* We add an space to separate possible APCIs/ASDUs in the same packet */
612 /*** *** END: Common to 'Packet List' and 'Packet Details' *** ***/
614 /*** *** Dissect 'Packet List' *** ***/
615 if (check_col(pinfo->cinfo, COL_INFO)) {
616 col_add_str(pinfo->cinfo, COL_INFO, res);
617 if(apcih->Type == I_TYPE && Brossa != TcpLen) {
618 call_dissector(iec104asdu_handle, tvb_new_subset(tvb, Off+ APCI_LEN, -1, apcih->ApduLen- APCI_LEN), pinfo, tree);
620 col_set_fence(pinfo->cinfo, COL_INFO);
626 /*** *** DISSECT 'Packet Details' *** ***/
628 it104 = proto_tree_add_item(tree, proto_iec104apci, tvb, 0, Off+ APCI_LEN, FALSE);
630 /* 'Packet Details': ROOT ITEM */
631 proto_item_append_text(it104, ": %s", res);
633 if(Brossa == TcpLen) return;
635 /* Don't call ASDU dissector if it was called before */
636 if(apcih->Type == I_TYPE && (!check_col(pinfo->cinfo, COL_INFO))) call_dissector(iec104asdu_handle, tvb_new_subset(tvb, Off+ APCI_LEN, -1, apcih->ApduLen- APCI_LEN), pinfo, tree);
638 /* 'Packet Details': TREE */
639 trHead = proto_item_add_subtree(it104, ett_apci);
640 /* Remember: add_uint, add_boolean, _add_text: value from last parameter.
641 add_item: value from tvb. */
642 proto_tree_add_uint(trHead, hf_apdulen, tvb, Off+ 1, 1, apcih->ApduLen);
643 proto_tree_add_uint(trHead, hf_apcitype, tvb, Off+ 2, 1, apcih->Type);
646 proto_tree_add_uint(trHead, hf_apciutype, tvb, Off+ 2, 1, apcih->UType); /* Don't shift the value */
655 static void dissect_iec104reas(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
657 /* 5th parameter = 6 = minimum bytes received to calculate the length. (Not 2 in order to find more APCIs in case of 'noisy' bytes between the APCIs) */
658 tcp_dissect_pdus(tvb, pinfo, tree, TRUE, APCI_LEN,
659 get_iec104apdu_len, dissect_iec104apci);
663 /* The protocol has two subprotocols: Register APCI */
665 proto_register_iec104apci(void)
668 static hf_register_info hf_ap[] = {
671 { "ApduLen", "104apci.apdulen", FT_UINT8, BASE_DEC, NULL, 0x0,
672 "APDU Len", HFILL }},
675 { "ApciType", "104apci.type", FT_UINT8, BASE_HEX, VALS(apci_types), 0x03,
676 "APCI type", HFILL }},
679 { "ApciUType", "104apci.utype", FT_UINT8, BASE_HEX, VALS(u_types), 0xFC,
680 "Apci U type", HFILL }},
684 static gint *ett_ap[] = {
688 proto_iec104apci = proto_register_protocol(
689 "IEC 60870-5-104,Apci",
693 proto_register_field_array(proto_iec104apci, hf_ap, array_length(hf_ap));
694 proto_register_subtree_array(ett_ap, array_length(ett_ap));
699 /* The protocol has two subprotocols: Register ASDU */
701 proto_register_iec104asdu(void)
704 static hf_register_info hf_as[] = {
707 { "Addr", "104asdu.addr", FT_UINT16, BASE_DEC, NULL, 0x0,
708 "Common Address of Asdu", HFILL }},
711 { "OA ", "104asdu.oa", FT_UINT8, BASE_DEC, NULL, 0x0,
712 "Originator Address", HFILL }},
715 { "TypeId ", "104asdu.typeid", FT_UINT8, BASE_DEC, VALS(asdu_types), 0x0,
716 "Asdu Type Id", HFILL }},
719 { "CauseTx", "104asdu.causetx", FT_UINT8, BASE_DEC, VALS(causetx_types), 0x3F,
720 "Cause of Transmision", HFILL }},
723 { "Negative", "104asdu.nega", FT_BOOLEAN, 8, NULL, F_NEGA,
724 "Negative", HFILL }},
727 { "Test", "104asdu.test", FT_BOOLEAN, 8, NULL, F_TEST,
732 { "IOA ", "104asdu.ioa", FT_UINT24, BASE_DEC, NULL, 0x0,
733 "Information Object Address", HFILL }},
736 { "NumIx", "104asdu.numix", FT_UINT8, BASE_DEC, NULL, 0x7F,
737 "Number of Information Objects/Elements", HFILL }},
740 { "SQ", "104asdu.sq", FT_BOOLEAN, 8, NULL, F_SQ,
741 "Sequence", HFILL }},
745 static gint *ett_as[] = {
749 proto_iec104asdu = proto_register_protocol(
750 "IEC 60870-5-104,Asdu",
754 proto_register_field_array(proto_iec104asdu, hf_as, array_length(hf_as));
755 proto_register_subtree_array(ett_as, array_length(ett_as));
761 /* The registration hand-off routine */
763 proto_reg_handoff_iec104(void)
765 dissector_handle_t iec104apci_handle;
767 iec104apci_handle = create_dissector_handle(dissect_iec104reas, proto_iec104apci);
768 iec104asdu_handle = create_dissector_handle(dissect_iec104asdu, proto_iec104asdu);
770 dissector_add("tcp.port", iec104port, iec104apci_handle);