Trivial warning fixes
[obnox/wireshark/wip.git] / epan / dissectors / packet-iec104.c
1 /* packet-iec104.c
2  * Routines for IEC-60870-5-104 (iec104) Protocol disassembly
3  *
4  *
5  * $Id$
6  *
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).
9  *
10  * Wireshark - Network traffic analyzer
11  * By Gerald Combs <gerald@wireshark.org>
12  * Copyright 1999 Gerald Combs
13  *
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.
18  *
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.
23  *
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.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <string.h>
35 #include <glib.h>
36
37 #include <epan/packet.h>
38 #include <epan/dissectors/packet-tcp.h>
39 #include <epan/prefs.h>
40 #include <epan/emem.h>
41
42 /* IEC-104 comment: Fields are little endian. */
43
44 #define MAXS 256
45
46 static dissector_handle_t iec104asdu_handle;
47
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);
53
54 guint8 val_to_strlen(guint32 val, const value_string *vs) {
55   const gchar *ret;
56   ret = match_strval(val, vs);
57   if (ret != NULL)
58     return strlen(ret);
59
60   return 100;
61 }
62
63
64 /* the asdu header structure */
65 struct asduheader {
66         guint8 AddrLow;
67         guint8 AddrHigh;
68         guint8 OA;
69         guint8 TypeId;
70         guint8 TNCause;
71         guint32 IOA;
72         guint8 NumIx;
73         guint8 SQ;
74 };
75
76 /* the apci header structure */
77 struct apciheader {
78         guint8 ApduLen;
79         guint8 Type;
80         guint8 UType;
81         guint16 Tx;
82         guint16 Rx;
83 };
84
85
86
87 static guint iec104port = 2404;
88
89 /* Define the iec104 proto */
90 static int proto_iec104apci = -1;
91 static int proto_iec104asdu = -1;
92
93 /* Protocol constants */
94 #define APCI_START      0x68
95 #define APCI_LEN        6
96 #define APDU_MIN_LEN    4
97 #define APDU_MAX_LEN    253
98
99 /* ASDU_HEAD_LEN: Includes Asdu head and first IOA */
100 #define ASDU_HEAD_LEN   9
101 #define F_TEST  0x80
102 #define F_NEGA  0x40
103 #define F_CAUSE 0x3F
104 #define F_SQ    0x80
105
106 /* APCI types */
107 #define I_TYPE          0
108 #define S_TYPE          1
109 #define U_TYPE          3
110 #define APCI_TYPE_UNKNOWN 4
111 static const value_string apci_types [] = {
112         { I_TYPE,               "I" },
113         { S_TYPE,               "S" },
114         { U_TYPE,               "U" },
115         { 0, NULL }
116 };
117
118
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" },
134         { 0, NULL }
135 };
136
137
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" },
248         { 0, NULL }
249 };
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" },
305         { 0, NULL }
306 };
307
308
309 /* Cause of Transmision (CauseTx) */
310 #define Per_Cyc         1
311 #define Back            2
312 #define Spont           3
313 #define Init            4
314 #define Req             5
315 #define Act             6
316 #define ActCon          7
317 #define Deact           8
318 #define DeactCon        9
319 #define ActTerm         10
320 #define Retrem          11
321 #define Retloc          12
322 #define File            13
323 #define Inrogen         20
324 #define Inro1           21
325 #define Inro2           22
326 #define Inro3           23
327 #define Inro4           24
328 #define Inro5           25
329 #define Inro6           26
330 #define Inro7           27
331 #define Inro8           28
332 #define Inro9           29
333 #define Inro10          30
334 #define Inro11          31
335 #define Inro12          32
336 #define Inro13          33
337 #define Inro14          34
338 #define Inro15          35
339 #define Inro16          36
340 #define Reqcogen        37
341 #define Reqco1          38
342 #define Reqco2          39
343 #define Reqco3          40
344 #define Reqco4          41
345 #define UkTypeId        44
346 #define UkCauseTx       45
347 #define UkComAdrASDU    46
348 #define UkIOA           47
349 static const value_string causetx_types [] = {
350         { Per_Cyc         ,"Per/Cyc" },
351         { Back            ,"Back" },
352         { Spont           ,"Spont" },
353         { Init            ,"Init" },
354         { Req             ,"Req" },
355         { Act             ,"Act" },
356         { ActCon          ,"ActCon" },
357         { Deact           ,"Deact" },
358         { DeactCon        ,"DeactCon" },
359         { ActTerm         ,"ActTerm" },
360         { Retrem          ,"Retrem" },
361         { Retloc          ,"Retloc" },
362         { File            ,"File" },
363         { Inrogen         ,"Inrogen" },
364         { Inro1           ,"Inro1" },
365         { Inro2           ,"Inro2" },
366         { Inro3           ,"Inro3" },
367         { Inro4           ,"Inro4" },
368         { Inro5           ,"Inro5" },
369         { Inro6           ,"Inro6" },
370         { Inro7           ,"Inro7" },
371         { Inro8           ,"Inro8" },
372         { Inro9           ,"Inro9" },
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" },
388         { UkIOA           ,"UkIOA" },
389         { 0, NULL }
390 };
391
392
393 /* Protocol fields to be filtered */
394 static int hf_apdulen = -1;
395 static int hf_apcitype = -1;
396 static int hf_apciutype    = -1;
397
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;
407
408
409 static gint ett_apci = -1;
410 static gint ett_asdu = -1;
411
412
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)
416 {
417         guint8 Val;
418         guint32 Off;
419
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);
424                 }
425         }
426
427         return (guint)(tvb_length(tvb));
428 }
429
430
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)
433 {
434         guint8 Len = tvb_length(tvb);  /* Investigate also: 'reported Length' */
435         guint8 Bytex = 0;
436         guint8 Ind = 0;
437         struct asduheader * asduh;
438         char * res = NULL;
439         proto_item * it104 = NULL;
440         proto_tree * trHead;
441
442         if (!(check_col(pinfo->cinfo, COL_INFO) || tree))   return; /* Be sure that the function is only called twice */
443
444         asduh = ep_alloc(sizeof(struct asduheader));
445         res = ep_alloc(MAXS);
446         res[0] = '\0';
447
448         if (check_col(pinfo->cinfo, COL_PROTOCOL))  {
449                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "104asdu");
450         }
451         if (check_col(pinfo->cinfo, COL_INFO))  {
452                 col_clear(pinfo->cinfo, COL_INFO);
453         }
454
455         /*** *** START: Common to 'Packet List' and 'Packet Details' *** ***/
456         if (Len >= ASDU_HEAD_LEN)  {
457                 /* Get fields */
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);
477                 }
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);
483                 }
484         }
485         else   {
486                 g_snprintf(res, MAXS, "<ERR Short Asdu, Len=%u>", Len);
487         }
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' *** ***/
490
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);
495         }
496
497         if(!tree)   return;
498
499         /*** *** DISSECT 'Packet Details' *** ***/
500
501         it104 = proto_tree_add_item(tree, proto_iec104asdu, tvb, 0, -1, FALSE);
502
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>") : "");
505
506         /* 'Packet Details': TREE */
507         if (Len < ASDU_HEAD_LEN)   return;
508         trHead = proto_item_add_subtree(it104, ett_asdu);
509
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);
521 }
522
523
524
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)
527 {
528         guint8 TcpLen = tvb_length(tvb);
529         guint16 Brossa = 0;
530         guint8 Start;
531         guint8 Off;
532         guint8 Byte1 = 0;
533         struct apciheader * apcih;
534         char * res = NULL;
535         proto_item * it104 = NULL;
536         proto_tree * trHead;
537
538         if (!(check_col(pinfo->cinfo, COL_INFO) || tree))   return; /* Be sure that the function is only called twice */
539
540         apcih = ep_alloc(sizeof(struct apciheader));
541         res = ep_alloc(MAXS);
542         res[0] = '\0';
543
544         if (check_col(pinfo->cinfo, COL_PROTOCOL))  {
545                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "104apci");
546         }
547         if (check_col(pinfo->cinfo, COL_INFO))  {
548                 col_clear(pinfo->cinfo, COL_INFO);
549         }
550
551         /*** *** START: Common to 'Packet List' and 'Packet Details' *** ***/
552         Start = 0;
553         for (Off= 0; Off <= TcpLen- 2; Off++)  {
554                 Start = tvb_get_guint8(tvb, Off);
555                 if (Start == APCI_START)  {
556                         Brossa = Off;
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)  {
564                                 case I_TYPE:
565                                         apcih->Tx = tvb_get_letohs(tvb, Off+ 2) >> 1;
566                                 case S_TYPE:
567                                         apcih->Rx = tvb_get_letohs(tvb, Off+ 4) >> 1;
568                                         break;
569                                 case U_TYPE:
570                                         apcih->UType = (Byte1 & 0xFC); /* Don't shift */
571                                         break;
572                                 }
573                         }
574                         else   {
575                                 /* WireShark can crash if we process packets with length less than expected (6). We consider that everything is bad */
576                                 Brossa = TcpLen;
577                         }
578                         /* Don't search more the APCI_START */
579                         break;
580                 }
581         }
582         if (Start != APCI_START)  {
583                 /* Everything is bad (no APCI found) */
584                 Brossa = TcpLen;
585         }
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' */
593                         case I_TYPE:
594                                 g_snprintf(res+strlen(res), MAXS-strlen(res), "%d,", apcih->Tx);
595                         case S_TYPE:
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);
600                                 break;
601                         case U_TYPE:
602                                 g_snprintf(res+strlen(res), MAXS-strlen(res), "%s)", val_to_str(apcih->UType >> 2, u_types, "<ERR>"));
603                                 break;
604                         }
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);
606                 }
607                 else  {
608                         g_snprintf(res+strlen(res), MAXS-strlen(res), "<ERR ApduLen=%u bytes> ", apcih->ApduLen);
609                 }
610         }
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' *** ***/
613
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);
619                 } else {
620                         col_set_fence(pinfo->cinfo, COL_INFO);
621                 }
622         }
623
624         if(!tree)   return;
625
626         /*** *** DISSECT 'Packet Details' *** ***/
627
628         it104 = proto_tree_add_item(tree, proto_iec104apci, tvb, 0, Off+ APCI_LEN, FALSE);
629
630         /* 'Packet Details': ROOT ITEM */
631         proto_item_append_text(it104, ": %s", res);
632
633         if(Brossa == TcpLen)   return;
634
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);
637
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);
644         switch(apcih->Type){
645         case U_TYPE:
646                 proto_tree_add_uint(trHead, hf_apciutype, tvb, Off+ 2, 1, apcih->UType); /* Don't shift the value */
647                 break;
648         }
649
650 }
651
652
653
654
655 static void dissect_iec104reas(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
656 {
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);
660 }
661
662
663 /* The protocol has two subprotocols: Register APCI */
664 void
665 proto_register_iec104apci(void)
666 {
667
668         static hf_register_info hf_ap[] = {
669
670                 { &hf_apdulen,
671                   { "ApduLen", "104apci.apdulen", FT_UINT8, BASE_DEC, NULL, 0x0,
672                     "APDU Len", HFILL }},
673
674                 { &hf_apcitype,
675                   { "ApciType", "104apci.type", FT_UINT8, BASE_HEX, VALS(apci_types), 0x03,
676                     "APCI type", HFILL }},
677
678                 { &hf_apciutype,
679                   { "ApciUType", "104apci.utype", FT_UINT8, BASE_HEX, VALS(u_types), 0xFC,
680                     "Apci U type", HFILL }},
681
682         };
683
684         static gint *ett_ap[] = {
685                 &ett_apci,
686         };
687
688         proto_iec104apci = proto_register_protocol(
689                 "IEC 60870-5-104,Apci",
690                 "104apci",
691                 "104apci"
692                 );
693         proto_register_field_array(proto_iec104apci, hf_ap, array_length(hf_ap));
694         proto_register_subtree_array(ett_ap, array_length(ett_ap));
695
696 }
697
698
699 /* The protocol has two subprotocols: Register ASDU */
700 void
701 proto_register_iec104asdu(void)
702 {
703
704         static hf_register_info hf_as[] = {
705
706                 { &hf_addr,
707                   { "Addr", "104asdu.addr", FT_UINT16, BASE_DEC, NULL, 0x0,
708                     "Common Address of Asdu", HFILL }},
709
710                 { &hf_oa,
711                   { "OA  ", "104asdu.oa", FT_UINT8, BASE_DEC, NULL, 0x0,
712                     "Originator Address", HFILL }},
713
714                 { &hf_typeid,
715                   { "TypeId ", "104asdu.typeid", FT_UINT8, BASE_DEC, VALS(asdu_types), 0x0,
716                     "Asdu Type Id", HFILL }},
717
718                 { &hf_causetx,
719                   { "CauseTx", "104asdu.causetx", FT_UINT8, BASE_DEC, VALS(causetx_types), 0x3F,
720                     "Cause of Transmision", HFILL }},
721
722                 { &hf_nega,
723                   { "Negative", "104asdu.nega", FT_BOOLEAN, 8, NULL, F_NEGA,
724                     "Negative", HFILL }},
725
726                 { &hf_test,
727                   { "Test", "104asdu.test", FT_BOOLEAN, 8, NULL, F_TEST,
728                     "Test", HFILL }},
729
730
731                 { &hf_ioa,
732                   { "IOA  ", "104asdu.ioa", FT_UINT24, BASE_DEC, NULL, 0x0,
733                     "Information Object Address", HFILL }},
734
735                 { &hf_numix,
736                   { "NumIx", "104asdu.numix", FT_UINT8, BASE_DEC, NULL, 0x7F,
737                     "Number of Information Objects/Elements", HFILL }},
738
739                 { &hf_sq,
740                   { "SQ", "104asdu.sq", FT_BOOLEAN, 8, NULL, F_SQ,
741                     "Sequence", HFILL }},
742
743         };
744
745         static gint *ett_as[] = {
746                 &ett_asdu,
747         };
748
749         proto_iec104asdu = proto_register_protocol(
750                 "IEC 60870-5-104,Asdu",
751                 "104asdu",
752                 "104asdu"
753                 );
754         proto_register_field_array(proto_iec104asdu, hf_as, array_length(hf_as));
755         proto_register_subtree_array(ett_as, array_length(ett_as));
756
757 }
758
759
760
761 /* The registration hand-off routine */
762 void
763 proto_reg_handoff_iec104(void)
764 {
765         dissector_handle_t iec104apci_handle;
766
767         iec104apci_handle = create_dissector_handle(dissect_iec104reas, proto_iec104apci);
768         iec104asdu_handle = create_dissector_handle(dissect_iec104asdu, proto_iec104asdu);
769
770         dissector_add("tcp.port", iec104port, iec104apci_handle);
771 }
772