1 /* packet-cemi.c
2  * Routines for cEMI (Common External Message Interface) dissection
3  * By Jan Kessler <kessler@ise.de>
4  * Copyright 2004, Jan Kessler <kessler@ise.de>
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * SPDX-License-Identifier: GPL-2.0-or-later
11  */
12 
13 #include "config.h"
14 
15 #include <epan/packet.h>
16 #include "packet-knxip.h"
17 
18 void proto_register_cemi(void);
19 void proto_reg_handoff_cemi(void);
20 
21 /* cEMI Message Codes
22 */
23 #define CEMI_L_BUSMON_IND     0x2B
24 #define CEMI_L_RAW_IND        0x2D
25 #define CEMI_L_RAW_REQ        0x10
26 #define CEMI_L_RAW_CON        0x2F
27 #define CEMI_L_DATA_REQ       0x11
28 #define CEMI_L_DATA_CON       0x2E
29 #define CEMI_L_DATA_IND       0x29
30 #define CEMI_L_POLL_DATA_REQ  0x13
31 #define CEMI_L_POLL_DATA_CON  0x25
32 #define CEMI_T_DATA_INDIVIDUAL_REQ 0x4A
33 #define CEMI_T_DATA_INDIVIDUAL_IND 0x94
34 #define CEMI_T_DATA_CONNECTED_REQ 0x41
35 #define CEMI_T_DATA_CONNECTED_IND 0x89
36 #define CEMI_M_PROPREAD_REQ   0xFC
37 #define CEMI_M_PROPREAD_CON   0xFB
38 #define CEMI_M_PROPWRITE_REQ  0xF6
39 #define CEMI_M_PROPWRITE_CON  0xF5
40 #define CEMI_M_PROPINFO_IND   0xF7
41 #define CEMI_M_FUNCPROPCMD_REQ  0xF8
42 #define CEMI_M_FUNCPROPREAD_REQ  0xF9
43 #define CEMI_M_FUNCPROP_CON   0xFA
44 #define CEMI_M_RESET_REQ      0xF1
45 #define CEMI_M_RESET_IND      0xF0
46 
47 /* Additional Information Types
48 */
49                                     /* 0x00  Reserved. */
50 #define CEMI_PL_MEDIUM_INFORMATION     0x01  /*!< (2 octets) Domain Address used by PL medium; Client <-> Server */
51 #define CEMI_RF_MEDIUM_INFORMATION     0x02  /*!< (7 octet)  RF-Control byte and serial number/DoA;
52                                                              Client <-> Server Busmonitor */
53 #define CEMI_STATUS_INFO               0x03  /*!< (1 octet)  Busmonitor Error Flags; see clause 2.5.5.5; Client <- Server */
54 #define CEMI_TIMESTAMP_RELATIVE        0x04  /*!< (2 octets) Relative timestamp; e.g. for L_Raw.ind; Client <- Server */
55 #define CEMI_TIME_DELAY_UNTIL_SENDING  0x05  /*!< (4 octets) Time delay (L_Raw.req, see clause 2.5.5.3); Client <- Server */
56                                     /* 0x06-0xFE  Not used. */
57                                     /* 0xFF       For future system extension (ESC Code). */
58 
59 /* Error Codes
60 */
61 #define CEMI_UNSPECIFIED_ERROR       0x00  /*!< Unknown error (R/W). */
62 #define CEMI_OUT_OF_RANGE            0x01  /*!< Write value not allowed (general, if not error 2 or 3) (W). */
63 #define CEMI_OUT_OF_MAXRANGE         0x02  /*!< Write value to high (W). */
64 #define CEMI_OUT_OF_MINRANGE         0x03  /*!< Write value to low (W). */
65 #define CEMI_MEMORY_ERROR            0x04  /*!< Memory can not be written or only with fault(s) (W). */
66 #define CEMI_READ_ONLY               0x05  /*!< Write access to a 'read only' or a write protected property (W). */
67 #define CEMI_ILLEGAL_COMMAND         0x06  /*!< COMMAND not valid or not supported (W). */
68 #define CEMI_VOID_DP                 0x07  /*!< Read or write access to an non existing property (R/W). */
69 #define CEMI_TYPE_CONFLICT           0x08  /*!< Write access with a wrong data type (datapoint length) (W). */
70 #define CEMI_PROP_INDEX_RANGE_ERROR  0x09  /* Read or write access to a non existing property array index (R/W). */
71 
72 /* Common EMI specific device server properties
73 */
74 #define CEMI_PID_DOMAIN_ADDRESS  0x70  /*!< Domain Address of a PL medium (cEMI server) device.
75                                             PDT_UNSIGNED_INT O - r/w */
76 #define CEMI_PID_IO_LIST         0x71  /*!< List of Interface Objects in the (cEMI server) device.
77                                             PDT_UNSIGNED_INT O - r/w */
78 
79 #define CEMI_PID_MEDIUM_TYPE          0x51  /*!< Media Type(s) supported by cEMI server.
80                                                  DPT_Media M - read only */
81 #define CEMI_PID_COMM_MODE            0x52  /*!< Link Layer / Raw (Busmonitor) / Transport L.
82                                                  DPT_CommMode O - r/w */
83 #define CEMI_PID_MEDIUM_AVAILABILITY  0x53  /*!< Bus available (1) or not (0) ?
84                                                  DPT_Media O - read only */
85 #define CEMI_PID_ADD_INFO_TYPES       0x54  /*!< cEMI supported Additional Information Types.
86                                                  DPT_AddInfoTypes O - read only */
87 #define CEMI_PID_TRANSP_ENABLE        0x56  /*!< LL Transparency Mode of cEMI server.
88                                                  DPT_Enable O - r/w */
89                                    /* 0x57  Reserved for cEMI client's subnetwork address.
90                                             PDT_UNSIGNED_CHAR O - r/w */
91                                    /* 0x58  Reserved for cEMI client's device address.
92                                             PDT_UNSIGNED_CHAR O - r/w */
93                                    /* 0x59  t.b.d.*/
94                                    /* 0x60  t.b.d.*/
95                                    /* 0x61  DoA Filter. t.b.d. O - read only */
96 
97 #define CEMI_PID_MEDIUM_TYPE_TP0    0x0001  /*!< TP 0 */
98 #define CEMI_PID_MEDIUM_TYPE_TP1    0x0002  /*!< TP 1 */
99 #define CEMI_PID_MEDIUM_TYPE_PL110  0x0004  /*!< PL 110 */
100 #define CEMI_PID_MEDIUM_TYPE_PL132  0x0008  /*!< PL 132 */
101 #define CEMI_PID_MEDIUM_TYPE_RF     0x0010  /*!< RF */
102 
103 #define CEMI_PID_COMM_MODE_LL   0x00  /*!< Link Layer = default comm. mode. */
104 #define CEMI_PID_COMM_MODE_LLB  0x01  /*!< Link Layer Busmonitor. */
105 #define CEMI_PID_COMM_MODE_LLR  0x02  /*!< Link Layer Raw Frames. */
106                              /* 0x03  Reserved for Network Layer. */
107                              /* 0x04  Reserved for TL group oriented. */
108                              /* 0x05  Reserved for TL connection oriented. */
109                              /* 0x05-0xFF  Reserved for other 'destination layers'. */
110 
111 /* - - - - - - -  T R E E   V I E W   I D E N T I F I E R  - - - - - - - -
112 */
113 
114 /* Initialize the protocol identifier that is needed for the
115  protocol hook and to register the fields in the protocol tree
116 */
117 static gint proto_cemi = -1;
118 
119 /* Initialize the registered fields identifiers. These fields
120  will be registered with the protocol during initialization.
121  Protocol fields are like type definitions. The protocol dissector
122  later on adds items of these types to the protocol tree.
123 */
124 static gint hf_bytes = -1;
125 static gint hf_folder = -1;
126 static gint hf_cemi_mc = -1;
127 static gint hf_cemi_error = -1;
128 static gint hf_cemi_ai_length = -1;
129 static gint hf_cemi_aie_type = -1;
130 static gint hf_cemi_aie_length = -1;
131 static gint hf_cemi_ot = -1;
132 static gint hf_cemi_oi = -1;
133 static gint hf_cemi_ox = -1;
134 static gint hf_cemi_px = -1;
135 static gint hf_cemi_pid = -1;
136 static gint hf_cemi_ne = -1;
137 static gint hf_cemi_sx = -1;
138 static gint hf_cemi_ft = -1;
139 static gint hf_cemi_rep = -1;
140 static gint hf_cemi_bt = -1;
141 static gint hf_cemi_prio = -1;
142 static gint hf_cemi_ack = -1;
143 static gint hf_cemi_ce = -1;
144 static gint hf_cemi_at = -1;
145 static gint hf_cemi_hc = -1;
146 static gint hf_cemi_eff = -1;
147 static gint hf_cemi_sa = -1;
148 static gint hf_cemi_da = -1;
149 static gint hf_cemi_len = -1;
150 static gint hf_cemi_tpt = -1;
151 static gint hf_cemi_tst = -1;
152 static gint hf_cemi_num = -1;
153 static gint hf_cemi_tc = -1;
154 static gint hf_cemi_ac = -1;
155 static gint hf_cemi_ad = -1;
156 static gint hf_cemi_ad_memory_length = -1;
157 static gint hf_cemi_ad_channel = -1;
158 static gint hf_cemi_ad_type = -1;
159 static gint hf_cemi_ax = -1;
160 static gint hf_cemi_pw = -1;
161 static gint hf_cemi_pdt = -1;
162 static gint hf_cemi_me = -1;
163 static gint hf_cemi_ra = -1;
164 static gint hf_cemi_wa = -1;
165 static gint hf_cemi_ext_oi = -1;
166 static gint hf_cemi_ext_pid = -1;
167 static gint hf_cemi_ext_ne = -1;
168 static gint hf_cemi_ext_sx = -1;
169 static gint hf_cemi_ext_dt = -1;
170 static gint hf_cemi_ext_px = -1;
171 static gint hf_cemi_ext_memory_length = -1;
172 static gint hf_cemi_ext_memory_address = -1;
173 static gint hf_cemi_memory_length = -1;
174 static gint hf_cemi_memory_address = -1;
175 static gint hf_cemi_memory_address_ext = -1;
176 static gint hf_cemi_level = -1;
177 static gint hf_cemi_snp_pid = -1;
178 static gint hf_cemi_snp_reserved = -1;
179 static gint hf_cemi_dpt_major = -1;
180 static gint hf_cemi_dpt_minor = -1;
181 static gint hf_cemi_scf = -1;
182 static gint hf_cemi_scf_t = -1;
183 static gint hf_cemi_scf_sai = -1;
184 static gint hf_cemi_scf_sbc = -1;
185 static gint hf_cemi_scf_svc = -1;
186 static gint hf_cemi_adc_count = -1;
187 
188 /* Initialize the subtree pointers. These pointers are needed to
189  display the protocol in a structured tree. Subtrees hook on
190  already defined fields or (the topmost) on the protocol itself
191 */
192 static gint ett_cemi = -1;
193 static gint ett_cemi_ai = -1;
194 static gint ett_cemi_aie = -1;
195 static gint ett_cemi_ctrl1 = -1;
196 static gint ett_cemi_ctrl2 = -1;
197 static gint ett_cemi_tpci = -1;
198 static gint ett_cemi_apci = -1;
199 static gint ett_cemi_range = -1;
200 static gint ett_cemi_pd = -1;
201 static gint ett_cemi_dpt = -1;
202 static gint ett_cemi_scf = -1;
203 static gint ett_cemi_decrypted = -1;
204 
205 /* - - - - - - - - - - -  V A L U E   T A B L E S  - - - - - - - - - - - -
206 */
207 
208 /* See following docs:
209 
210   "AN033 v03 cEMI.pdf",
211   "AN057 v01 System B RfV.pdf",
212   "KSG259 2004.02.03 Identifiers.pdf",
213   "03_07_03 Standardized Identifier Tables.pdf"
214 */
215 
216 /* Message Code
217 */
218 static const value_string mc_vals[] = {
219   { CEMI_L_BUSMON_IND, "L_Busmon.ind" },
220   { CEMI_L_RAW_IND, "L_Raw.ind" },
221   { CEMI_L_RAW_REQ, "L_Raw.req" },
222   { CEMI_L_RAW_CON, "L_Raw.con" },
223   { CEMI_L_DATA_REQ, "L_Data.req" },
224   { CEMI_L_DATA_CON, "L_Data.con" },
225   { CEMI_L_DATA_IND, "L_Data.ind" },
226   { CEMI_L_POLL_DATA_REQ, "L_PollData.req" },
227   { CEMI_L_POLL_DATA_CON, "L_PollData.con" },
228   { CEMI_T_DATA_INDIVIDUAL_REQ, "T_Data_Individual.req" },
229   { CEMI_T_DATA_INDIVIDUAL_IND, "T_Data_Individual.ind" },
230   { CEMI_T_DATA_CONNECTED_REQ, "T_Data_Connected.req" },
231   { CEMI_T_DATA_CONNECTED_IND, "T_Data_Connected.ind" },
232   { CEMI_M_PROPREAD_REQ, "M_PropRead.req" },
233   { CEMI_M_PROPREAD_CON, "M_PropRead.con" },
234   { CEMI_M_PROPWRITE_REQ, "M_PropWrite.req" },
235   { CEMI_M_PROPWRITE_CON, "M_PropWrite.con" },
236   { CEMI_M_PROPINFO_IND, "M_PropInfo.ind" },
237   { CEMI_M_FUNCPROPCMD_REQ, "M_FuncPropCmd.req" },
238   { CEMI_M_FUNCPROPREAD_REQ, "M_FuncPropRead.req" },
239   { CEMI_M_FUNCPROP_CON, "M_FuncProp.con" },
240   { CEMI_M_RESET_REQ, "M_Reset.req" },
241   { CEMI_M_RESET_IND, "M_Reset.ind" },
242   { 0, NULL }
243 };
244 
245 /* Property access flags
246 */
247 #define PA_RESPONSE     0x01
248 #define PA_DATA         0x02
249 
250 /* Additional Info Element Type
251 */
252 static const value_string aiet_vals[] = {
253   { 1, "PL Medium Info" },
254   { 2, "RF Medium Info" },
255   { 3, "BusMonitor Status Info" },
256   { 4, "Timestamp Relative" },
257   { 5, "Time Delay Until Sending" },
258   { 6, "Extended Relative Timestamp" },
259   { 7, "BiBat Info" },
260   { 0, NULL }
261 };
262 
263 /* Frame Type
264 */
265 static const value_string ft_vals[] = {
266   { 0, "Extended" },
267   { 1, "Standard" },
268   { 0, NULL }
269 };
270 
271 /* Repeat on error?
272 */
273 static const value_string rep_vals[] = {
274   { 0, "Yes" },
275   { 1, "No" },
276   { 0, NULL }
277 };
278 
279 /* Broadcast Type
280 */
281 static const value_string bt_vals[] = {
282   { 0, "System" },
283   { 1, "Domain" },
284   { 0, NULL }
285 };
286 
287 /* Priority
288 */
289 static const value_string prio_vals[] = {
290   { 0, "System" },
291   { 2, "Urgent" },
292   { 1, "Normal" },
293   { 3, "Low" },
294   { 0, NULL }
295 };
296 
297 /* Ack requested?
298 */
299 static const value_string ack_vals[] = {
300   { 0, "No" },
301   { 1, "Yes" },
302   { 0, NULL }
303 };
304 
305 /* Confirmation Error?
306 */
307 static const value_string ce_vals[] = {
308   { 0, "No" },
309   { 1, "Yes" },
310   { 0, NULL }
311 };
312 
313 /* Address Type
314 */
315 static const value_string at_vals[] = {
316   { 0, "Individual" },
317   { 1, "Group" },
318   { 0, NULL }
319 };
320 
321 /* Packet Type
322 */
323 static const value_string pt_vals[] = {
324   { 0, "Data" },
325   { 1, "Control" },
326   { 0, NULL }
327 };
328 
329 /* Sequence Type
330 */
331 static const value_string st_vals[] = {
332   { 0, "Unnumbered" },
333   { 1, "Numbered" },
334   { 0, NULL }
335 };
336 
337 /* Transport Layer Code
338 */
339 static const value_string tc_vals[] = {
340   { 0, "Connect" },
341   { 1, "Disconnect" },
342   { 2, "ACK" },
343   { 3, "NAK" },
344   { 0, NULL }
345 };
346 
347 /* Application Layer Code
348 */
349 #define AC_GroupValueRead     0
350 #define AC_GroupValueResp     1
351 #define AC_GroupValueWrite    2
352 #define AC_IndAddrWrite       3
353 #define AC_IndAddrRead        4
354 #define AC_IndAddrResp        5
355 #define AC_AdcRead            6
356 #define AC_AdcResp            7
357 #define AC_MemRead            8
358 #define AC_MemResp            9
359 #define AC_MemWrite           10
360 #define AC_UserMsg            11
361 #define AC_DevDescrRead       12
362 #define AC_DevDescrResp       13
363 #define AC_Restart            14
364 #define AC_Escape             15
365 
366 static const value_string ac_vals[] =
367 {
368   { AC_GroupValueRead, "GroupValueRead" },
369   { AC_GroupValueResp, "GroupValueResp" },
370   { AC_GroupValueWrite, "GroupValueWrite" },
371   { AC_IndAddrWrite, "IndAddrWrite" },
372   { AC_IndAddrRead, "IndAddrRead" },
373   { AC_IndAddrResp, "IndAddrResp" },
374   { AC_AdcRead, "AdcRead" },
375   { AC_AdcResp, "AdcResp" },
376   { AC_MemRead, "MemRead" },
377   { AC_MemResp, "MemResp" },
378   { AC_MemWrite, "MemWrite" },
379   { AC_UserMsg, "UserMsg" },
380   { AC_DevDescrRead, "DevDescrRead" },
381   { AC_DevDescrResp, "DevDescrResp" },
382   { AC_Restart, "Restart" },
383   { AC_Escape, "Escape" },
384   { 0, NULL }
385 };
386 
387 /* Extended AL codes
388 */
389 #define AX_SysNwkParamRead  0x1C8
390 #define AX_SysNwkParamResp  0x1C9
391 #define AX_SysNwkParamWrite  0x1CA
392 #define AX_PropExtValueRead  0x1CC
393 #define AX_PropExtValueResp  0x1CD
394 #define AX_PropExtValueWriteCon  0x1CE
395 #define AX_PropExtValueWriteConRes  0x1CF
396 #define AX_PropExtValueWriteUnCon  0x1D0
397 #define AX_PropExtValueInfoReport  0x1D1
398 #define AX_PropExtDescrRead  0x1D2
399 #define AX_PropExtDescrResp  0x1D3
400 #define AX_FuncPropExtCmd  0x1D4
401 #define AX_FuncPropExtRead  0x1D5
402 #define AX_FuncPropExtResp  0x1D6
403 #define AX_MemExtWrite  0x1FB
404 #define AX_MemExtWriteResp  0x1FC
405 #define AX_MemExtRead  0x1FD
406 #define AX_MemExtReadResp  0x1FE
407 #define AX_UserMemRead  0x2C0
408 #define AX_UserMemResp  0x2C1
409 #define AX_UserMemWrite  0x2C2
410 #define AX_UserMemBitWrite  0x2C4
411 #define AX_UserMfrInfoRead  0x2C5
412 #define AX_UserMfrInfoResp  0x2C6
413 #define AX_FuncPropCmd  0x2C7
414 #define AX_FuncPropRead  0x2C8
415 #define AX_FuncPropResp  0x2C9
416 #define AX_Restart  0x380
417 #define AX_RestartReq  0x381
418 #define AX_RestartResp  0x3A1
419 #define AX_RoutingTableOpen  0x3C0
420 #define AX_RoutingTableRead  0x3C1
421 #define AX_RoutingTableResp  0x3C2
422 #define AX_RoutingTableWrite  0x3C3
423 #define AX_RouterMemRead  0x3C8
424 #define AX_RouterMemResp  0x3C9
425 #define AX_RouterMemWrite  0x3CA
426 #define AX_RouterStatusRead  0x3CD
427 #define AX_RouterStatusResp  0x3CE
428 #define AX_RouterStatusWrite  0x3CF
429 #define AX_MemBitWrite  0x3D0
430 #define AX_AuthReq  0x3D1
431 #define AX_AuthResp  0x3D2
432 #define AX_KeyWrite  0x3D3
433 #define AX_KeyResp  0x3D4
434 #define AX_PropValueRead  0x3D5
435 #define AX_PropValueResp  0x3D6
436 #define AX_PropValueWrite  0x3D7
437 #define AX_PropDescrRead  0x3D8
438 #define AX_PropDescrResp  0x3D9
439 #define AX_NwkParamRead  0x3DA
440 #define AX_NwkParamResp  0x3DB
441 #define AX_IndAddrSerNumRead  0x3DC
442 #define AX_IndAddrSerNumResp  0x3DD
443 #define AX_IndAddrSerNumWrite  0x3DE
444 #define AX_DomAddrWrite  0x3E0
445 #define AX_DomAddrRead  0x3E1
446 #define AX_DomAddrResp  0x3E2
447 #define AX_DomAddrSelRead  0x3E3
448 #define AX_NwkParamWrite  0x3E4
449 #define AX_LinkRead  0x3E5
450 #define AX_LinkResp  0x3E6
451 #define AX_LinkWrite  0x3E7
452 #define AX_GroupPropValueRead  0x3E8
453 #define AX_GroupPropValueResp  0x3E9
454 #define AX_GroupPropValueWrite  0x3EA
455 #define AX_GroupPropValueInfo  0x3EB
456 #define AX_DomAddrSerNumRead  0x3EC
457 #define AX_DomAddrSerNumResp  0x3ED
458 #define AX_DomAddrSerNumWrite  0x3EE
459 #define AX_FileStreamInfo  0x3F0
460 #define AX_DataSec  0x3F1
461 
462 static const value_string ax_vals[] =
463 {
464   { AX_SysNwkParamRead, "SysNwkParamRead" },
465   { AX_SysNwkParamResp, "SysNwkParamResp" },
466   { AX_SysNwkParamWrite, "SysNwkParamWrite" },
467   { AX_PropExtValueRead, "PropExtValueRead" },
468   { AX_PropExtValueResp, "PropExtValueResp" },
469   { AX_PropExtValueWriteCon, "PropExtValueWriteCon" },
470   { AX_PropExtValueWriteConRes, "PropExtValueWriteConRes" },
471   { AX_PropExtValueWriteUnCon, "PropExtValueWriteUnCon" },
472   { AX_PropExtDescrRead, "PropExtDescrRead" },
473   { AX_PropExtDescrResp, "PropExtDescrResp" },
474   { AX_FuncPropExtCmd, "FuncPropExtCmd" },
475   { AX_FuncPropExtRead, "FuncPropExtRead" },
476   { AX_FuncPropExtResp, "FuncPropExtResp" },
477   { AX_MemExtWrite, "MemExtWrite" },
478   { AX_MemExtWriteResp, "MemExtWriteResp" },
479   { AX_MemExtRead, "MemExtRead" },
480   { AX_MemExtReadResp, "MemExtReadResp" },
481   { AX_UserMemRead, "UserMemRead" },
482   { AX_UserMemResp, "UserMemResp" },
483   { AX_UserMemWrite, "UserMemWrite" },
484   { AX_UserMemBitWrite, "UserMemBitWrite" },
485   { AX_UserMfrInfoRead, "UserMfrInfoRead" },
486   { AX_UserMfrInfoResp, "UserMfrInfoResp" },
487   { AX_FuncPropCmd, "FuncPropCmd" },
488   { AX_FuncPropRead, "FuncPropRead" },
489   { AX_FuncPropResp, "FuncPropResp" },
490   { AX_Restart, "Restart" },
491   { AX_RestartReq, "RestartReq" },
492   { AX_RestartResp, "RestartResp" },
493   { AX_RoutingTableOpen, "RoutingTableOpen" },
494   { AX_RoutingTableRead, "RoutingTableRead" },
495   { AX_RoutingTableResp, "RoutingTableResp" },
496   { AX_RoutingTableWrite, "RoutingTableWrite" },
497   { AX_RouterMemRead, "RouterMemRead" },
498   { AX_RouterMemResp, "RouterMemResp" },
499   { AX_RouterMemWrite, "RouterMemWrite" },
500   { AX_RouterStatusRead, "RouterStatusRead" },
501   { AX_RouterStatusResp, "RouterStatusResp" },
502   { AX_RouterStatusWrite, "RouterStatusWrite" },
503   { AX_MemBitWrite, "MemBitWrite" },
504   { AX_AuthReq, "AuthReq" },
505   { AX_AuthResp, "AuthResp" },
506   { AX_KeyWrite, "KeyWrite" },
507   { AX_KeyResp, "KeyResp" },
508   { AX_PropValueRead, "PropValueRead" },
509   { AX_PropValueResp, "PropValueResp" },
510   { AX_PropValueWrite, "PropValueWrite" },
511   { AX_PropDescrRead, "PropDescrRead" },
512   { AX_PropDescrResp, "PropDescrResp" },
513   { AX_NwkParamRead, "NwkParamRead" },
514   { AX_NwkParamResp, "NwkParamResp" },
515   { AX_IndAddrSerNumRead, "IndAddrSerNumRead" },
516   { AX_IndAddrSerNumResp, "IndAddrSerNumResp" },
517   { AX_IndAddrSerNumWrite, "IndAddrSerNumWrite" },
518   { AX_DomAddrWrite, "DomAddrWrite" },
519   { AX_DomAddrRead, "DomAddrRead" },
520   { AX_DomAddrResp, "DomAddrResp" },
521   { AX_DomAddrSelRead, "DomAddrSelRead" },
522   { AX_NwkParamWrite, "NwkParamWrite" },
523   { AX_LinkRead, "LinkRead" },
524   { AX_LinkResp, "LinkResp" },
525   { AX_LinkWrite, "LinkWrite" },
526   { AX_GroupPropValueRead, "GroupPropValueRead" },
527   { AX_GroupPropValueResp, "GroupPropValueResp" },
528   { AX_GroupPropValueWrite, "GroupPropValueWrite" },
529   { AX_GroupPropValueInfo, "GroupPropValueInfo" },
530   { AX_DomAddrSerNumRead, "DomAddrSerNumRead" },
531   { AX_DomAddrSerNumResp, "DomAddrSerNumResp" },
532   { AX_DomAddrSerNumWrite, "DomAddrSerNumWrite" },
533   { AX_FileStreamInfo, "FileStreamInfo" },
534   { AX_DataSec, "DataSec" },
535   { 0, NULL }
536 };
537 
538 /* SCF (Security Control Field)
539 */
540 static const value_string scf_vals[] =
541 {
542   { 0x00, "CCM S-A_Data with Authentication-only" },
543   { 0x10, "CCM S-A_Data with Authentication+Confidentiality" },
544   { 0x12, "CCM S-A_Sync_Req with Authentication+Confidentiality" },
545   { 0x13, "CCM S-A_Sync_Res with Authentication+Confidentiality" },
546   { 0x08, "CCM S-A_Data with Authentication-only, System Broadcast" },
547   { 0x18, "CCM S-A_Data with Authentication+Confidentiality, System Broadcast" },
548   { 0x1a, "CCM S-A_Sync_Req with Authentication+Confidentiality, System Broadcast" },
549   { 0x1b, "CCM S-A_Sync_Res with Authentication+Confidentiality, System Broadcast" },
550   { 0x80, "CCM S-A_Data with Authentication-only, Tool Access" },
551   { 0x90, "CCM S-A_Data with Authentication+Confidentiality, Tool Access" },
552   { 0x92, "CCM S-A_Sync_Req with Authentication+Confidentiality, Tool Access" },
553   { 0x93, "CCM S-A_Sync_Res with Authentication+Confidentiality, Tool Access" },
554   { 0x88, "CCM S-A_Data with Authentication-only, System Broadcast, Tool Access" },
555   { 0x98, "CCM S-A_Data with Authentication+Confidentiality, Tool Access, System Broadcast" },
556   { 0x9a, "CCM S-A_Sync_Req with Authentication+Confidentiality, Tool Access, System Broadcast" },
557   { 0x9b, "CCM S-A_Sync_Res with Authentication+Confidentiality, Tool Access, System Broadcast" },
558   { 0, NULL }
559 };
560 
561 /* SCF (Security Control Field).
562 */
563 static const value_string scf_short_vals[] =
564 {
565   { 0x00, "Data+A" },
566   { 0x10, "Data+A+C" },
567   { 0x12, "SyncReq" },
568   { 0x13, "SyncRes" },
569   { 0x08, "Data+A+SBC" },
570   { 0x18, "Data+A+C+SBC" },
571   { 0x1a, "SyncReq+SBC" },
572   { 0x1b, "SyncRes+SBC" },
573   { 0x80, "Data+A+T" },
574   { 0x90, "Data+A+C+T" },
575   { 0x92, "SyncReq+T" },
576   { 0x93, "SyncRes+T" },
577   { 0x88, "Data+A+T+SBC" },
578   { 0x98, "Data+A+C+T+SBC" },
579   { 0x9a, "SyncReq+T+SBC" },
580   { 0x9b, "SyncRes+T+SBC" },
581   { 0, NULL }
582 };
583 
584 /* SCF.SAI (Security Algorithm Identifier)
585 */
586 static const value_string scf_sai_vals[] =
587 {
588   { 0, "CCM A" },
589   { 1, "CCM A+S" },
590   { 0, NULL }
591 };
592 
593 /* SCF.Service
594 */
595 static const value_string scf_svc_vals[] =
596 {
597   { 0, "Data" },
598   { 2, "Sync_Req" },
599   { 3, "Sync_Res" },
600   { 0, NULL }
601 };
602 
603 /* See KNX documents:
604 * "03_07_03 Standardized Identifier Tables v01.03.01 AS"
605 * "03_05_01 Resources v01.09.03 AS"
606 */
607 
608 /* Property Data Types
609 * See "4 Property Datatypes Identifiers" in "03_07_03 Standardized Identifier Tables v01.03.01 AS"
610 */
611 static const value_string pdt_vals[] = {
612   { 0x00, "PDT_CONTROL" },
613   { 0x01, "PDT_CHAR" },
614   { 0x02, "PDT_UNSIGNED_CHAR" },
615   { 0x03, "PDT_INT" },
616   { 0x04, "PDT_UNSIGNED_INT" },
617   { 0x05, "PDT_KNX_FLOAT" },
618   { 0x06, "PDT_DATE" },
619   { 0x07, "PDT_TIME" },
620   { 0x08, "PDT_LONG" },
621   { 0x09, "PDT_UNSIGNED_LONG" },
622   { 0x0A, "PDT_FLOAT" },
623   { 0x0B, "PDT_DOUBLE" },
624   { 0x0C, "PDT_CHAR_BLOCK" },
625   { 0x0D, "PDT_POLL_GROUP_SETTINGS" },
626   { 0x0E, "PDT_SHORT_CHAR_BLOCK" },
627   { 0x0F, "PDT_DATE_TIME" },
628   { 0x10, "PDT_VARIABLE_LENGTH" },
629   { 0x11, "PDT_GENERIC_01" },
630   { 0x12, "PDT_GENERIC_02" },
631   { 0x13, "PDT_GENERIC_03" },
632   { 0x14, "PDT_GENERIC_04" },
633   { 0x15, "PDT_GENERIC_05" },
634   { 0x16, "PDT_GENERIC_06" },
635   { 0x17, "PDT_GENERIC_07" },
636   { 0x18, "PDT_GENERIC_08" },
637   { 0x19, "PDT_GENERIC_09" },
638   { 0x1A, "PDT_GENERIC_10" },
639   { 0x1B, "PDT_GENERIC_11" },
640   { 0x1C, "PDT_GENERIC_12" },
641   { 0x1D, "PDT_GENERIC_13" },
642   { 0x1E, "PDT_GENERIC_14" },
643   { 0x1F, "PDT_GENERIC_15" },
644   { 0x20, "PDT_GENERIC_16" },
645   { 0x21, "PDT_GENERIC_17" },
646   { 0x22, "PDT_GENERIC_18" },
647   { 0x23, "PDT_GENERIC_19" },
648   { 0x24, "PDT_GENERIC_20" },
649   { 0x2F, "PDT_UTF-8" },
650   { 0x30, "PDT_VERSION" },
651   { 0x31, "PDT_ALARM_INFO" },
652   { 0x32, "PDT_BINARY_INFORMATION" },
653   { 0x33, "PDT_BITSET8" },
654   { 0x34, "PDT_BITSET16" },
655   { 0x35, "PDT_ENUM8" },
656   { 0x36, "PDT_SCALING" },
657   { 0x3C, "PDT_NE_VL" },
658   { 0x3D, "PDT_NE_FL" },
659   { 0x3E, "PDT_FUNCTION" },
660   { 0x3F, "PDT_ESCAPE" },
661   { 0, NULL }
662 };
663 
664 /* Interface Object Types
665 * See "2 Interface Object Types" in "03_07_03 Standardized Identifier Tables v01.03.01 AS"
666 */
667 static const value_string ot_vals[] = {
668   { 0, "Device" },
669   { 1, "Address Table" },
670   { 2, "Association Table" },
671   { 3, "Application Program" },
672   { 4, "Interface Program" },
673   { 5, "KNX-Object Association Table" },
674   { 6, "Router" },
675   { 7, "LTE Address Routing Table" },
676   { 8, "cEMI Server" },
677   { 9, "Group Object Table" },
678   { 10, "Polling Master" },
679   { 11, "KNXnet/IP Parameter" },
680   { 13, "File Server" },
681   { 17, "Data Security" },
682   { 0, NULL }
683 };
684 
685 /* IOT independent PIDs
686 * See "3.2 Interface Object Type independent standard Properties" in "03_07_03 Standardized Identifier Tables v01.03.01 AS"
687 * See "4.2 Interface Object Type independent Properties" in "03_05_01 Resources v01.09.03 AS"
688 */
689 static const value_string pid_vals[] = {
690   { 1, "PID_OBJECT_TYPE" },
691   { 2, "PID_OBJECT_NAME" },
692   { 3, "PID_SEMAPHOR" },
693   { 4, "PID_GROUP_OBJECT_REFERENCE" },
694   { 5, "PID_LOAD_STATE_CONTROL" },
695   { 6, "PID_RUN_STATE_CONTROL" },
696   { 7, "PID_TABLE_REFERENCE" },
697   { 8, "PID_SERVICE_CONTROL" },
698   { 9, "PID_FIRMWARE_REVISION" },
699   { 10, "PID_SERVICES_SUPPORTED" },
700   { 11, "PID_SERIAL_NUMBER" },
701   { 12, "PID_MANUFACTURER_ID" },
702   { 13, "PID_PROGRAM_VERSION" },
703   { 14, "PID_DEVICE_CONTROL" },
704   { 15, "PID_ORDER_INFO" },
705   { 16, "PID_PEI_TYPE" },
706   { 17, "PID_PORT_CONFIGURATION" },
707   { 18, "PID_POLL_GROUP_SETTINGS" },
708   { 19, "PID_MANUFACTURER_DATA" },
709   { 21, "PID_DESCRIPTION" },
710   { 23, "PID_TABLE" },
711   { 24, "PID_ENROL" },
712   { 25, "PID_VERSION" },
713   { 26, "PID_GROUP_OBJECT_LINK" },
714   { 27, "PID_MCB_TABLE" },
715   { 28, "PID_ERROR_CODE" },
716   { 29, "PID_OBJECT_INDEX" },
717   { 30, "PID_DOWNLOAD_COUNTER" },
718   { 0, NULL }
719 };
720 
721 /* PIDs for IOT = 0 (Device)
722 * See "3.3.1 Device Object Interface Object (Object Type = 0)" in "03_07_03 Standardized Identifier Tables v01.03.01 AS"
723 * See "4.3 Device Object (Object Type 0)" in "03_05_01 Resources v01.09.03 AS"
724 */
725 static const value_string pid0_vals[] = {
726   { 51, "PID_ROUTING_COUNT" },
727   { 52, "PID_MAX_RETRY_COUNT" },
728   { 53, "PID_ERROR_FLAGS" },
729   { 54, "PID_PROGMODE" },
730   { 55, "PID_PRODUCT_ID" },
731   { 56, "PID_MAX_APDULENGTH" },
732   { 57, "PID_SUBNET_ADDR" },
733   { 58, "PID_DEVICE_ADDR" },
734   { 59, "PID_PB_CONFIG" },
735   { 60, "PID_ADDR_REPORT" },
736   { 61, "PID_ADDR_CHECK" },
737   { 62, "PID_OBJECT_VALUE" },
738   { 63, "PID_OBJECTLINK" },
739   { 64, "PID_APPLICATION" },
740   { 65, "PID_PARAMETER" },
741   { 66, "PID_OBJECTADDRESS" },
742   { 67, "PID_PSU_TYPE" },
743   { 68, "PID_PSU_STATUS" },
744   { 69, "PID_PSU_ENABLE" },
745   { 70, "PID_DOMAIN_ADDRESS" },
746   { 71, "PID_IO_LIST" },
747   { 72, "PID_MGT_DESCRIPTOR_01" },
748   { 73, "PID_PL110_PARAM" },
749   { 74, "PID_RF_REPEAT_COUNTER" },
750   { 75, "PID_RECEIVE_BLOCK_TABLE" },
751   { 76, "PID_RANDOM_PAUSE_TABLE" },
752   { 77, "PID_RECEIVE_BLOCK_NR" },
753   { 78, "PID_HARDWARE_TYPE" },
754   { 79, "PID_RETRANSMITTER_NUMBER" },
755   { 80, "PID_SERIAL_NR_TABLE" },
756   { 81, "PID_BIBATMASTER_ADDRESS" },
757   { 82, "PID_RF_DOMAIN_ADDRESS" },
758   { 83, "PID_DEVICE_DESCRIPTOR" },
759   { 84, "PID_METERING_FILTER_TABLE" },
760   { 85, "PID_GROUP_TELEGR_RATE_LIMIT_TIME_BASE" },
761   { 86, "PID_GROUP_TELEGR_RATE_LIMIT_NO_OF_TELEGR" },
762   { 0, NULL }
763 };
764 
765 /* PIDs for IOT = 1 (Address Table)
766 * See "4.10.6 Group Address Table - Realisation Type 6" in "03_05_01 Resources v01.09.03 AS"
767 * See "4.10.7 Group Address Table - Realisation Type 7" in "03_05_01 Resources v01.09.03 AS"
768 */
769 static const value_string pid1_vals[] = {
770   { 51, "PID_EXT_FRAMEFORMAT" },
771   { 52, "PID_ADDRTAB1" },
772   { 53, "PID_GROUP_RESPONSER_TABLE" },
773   { 0, NULL }
774 };
775 
776 /* PIDs for IOT = 6 (Router)
777 * See "4.4 Router Object (Object Type 6)" in "03_05_01 Resources v01.09.03 AS"
778 * See "2.4.4 Router Object" in "AN161 v05 Coupler Model 2.0 AS"
779 */
780 static const value_string pid6_vals[] = {
781   { 51, "PID_MEDIUM_STATUS" },  /* alias "PID_LINE_STATUS" */
782   { 52, "PID_MAIN_LCCONFIG" },
783   { 53, "PID_SUB_LCCONFIG" },
784   { 54, "PID_MAIN_LCGRPCONFIG" },
785   { 55, "PID_SUB_LCGRPCONFIG" },
786   { 56, "PID_ROUTETABLE_CONTROL" },
787   { 57, "PID_COUPL_SERV_CONTROL" },
788   { 58, "PID_MAX_APDU_LENGTH" },
789   { 59, "PID_L2_COUPLER_TYPE" },
790   { 61, "PID_HOP_COUNT" },
791   { 63, "PID_MEDIUM" },
792   { 67, "PID_FILTER_TABLE_USE" },
793   { 104, "PID_PL110_SBC_CONTROL" },
794   { 105, "PID_PL110_DOA" },
795   { 112, "PID_RF_SBC_CONTROL" },
796   { 0, NULL }
797 };
798 
799 /* PIDs for IOT = 7 (LTE Address Routing Table)
800 * See "4.5 LTE Address Routing Table Object (Object Type 7)" in "03_05_01 Resources v01.09.03 AS"
801 */
802 static const value_string pid7_vals[] = {
803   { 51, "PID_LTE_ROUTESELECT" },
804   { 52, "PID_LTE_ROUTETABLE" },
805   { 0, NULL }
806 };
807 
808 /* PIDs for IOT = 8 (cEMI Server)
809 * See "4.6 cEMI Server Object (Object Type 8)" in "03_05_01 Resources v01.09.03 AS"
810 */
811 static const value_string pid8_vals[] = {
812   { 51, "PID_MEDIUM_TYPE" },
813   { 52, "PID_COMM_MODE" },
814   { 53, "PID_MEDIUM_AVAILABILITY" },
815   { 54, "PID_ADD_INFO_TYPES" },
816   { 55, "PID_TIME_BASE" },
817   { 56, "PID_TRANSP_ENABLE" },
818   { 59, "PID_BIBAT_NEXTBLOCK" },
819   { 60, "PID_RF_MODE_SELECT" },
820   { 61, "PID_RF_MODE_SUPPORT" },
821   { 62, "PID_RF_FILTERING_MODE_SELECT" },
822   { 63, "PID_RF_FILTERING_MODE_SUPPORT" },
823   { 0, NULL }
824 };
825 
826 /* PIDs for IOT = 9 (Group Object Table)
827 * See "4.12.4 Group Object Table - Realisation Type 6" in "03_05_01 Resources v01.09.03 AS"
828 */
829 static const value_string pid9_vals[] = {
830   { 51, "PID_GRPOBJTABLE" },
831   { 52, "PID_EXT_GRPOBJREFERENCE" },
832   { 0, NULL }
833 };
834 
835 /* PIDs for IOT = 11 (KNXnet/IP Parameter),
836 * See "2.5 KNXnet/IP Parameter Object" in "03_08_03 Management v01.06.02 AS"
837 * See "2.3.1 KNXnet/IP Parameter Object" in "AN159 v06 KNXnet-IP Secure AS"
838 */
839 static const value_string pid11_vals[] = {
840   { 51, "PID_PROJECT_INSTALLATION_ID" },
841   { 52, "PID_KNX_INDIVIDUAL_ADDRESS" },
842   { 53, "PID_ADDITIONAL_INDIVIDUAL_ADDRESSES" },
843   { 54, "PID_CURRENT_IP_ASSIGNMENT_METHOD" },
844   { 55, "PID_IP_ASSIGNMENT_METHOD" },
845   { 56, "PID_IP_CAPABILITIES" },
846   { 57, "PID_CURRENT_IP_ADDRESS" },
847   { 58, "PID_CURRENT_SUBNET_MASK" },
848   { 59, "PID_CURRENT_DEFAULT_GATEWAY" },
849   { 60, "PID_IP_ADDRESS" },
850   { 61, "PID_SUBNET_MASK" },
851   { 62, "PID_DEFAULT_GATEWAY" },
852   { 63, "PID_DHCP_BOOTP_SERVER" },
853   { 64, "PID_MAC_ADDRESS" },
854   { 65, "PID_SYSTEM_SETUP_MULTICAST_ADDRESS" },
855   { 66, "PID_ROUTING_MULTICAST_ADDRESS" },
856   { 67, "PID_TTL" },
857   { 68, "PID_KNXNETIP_DEVICE_CAPABILITIES" },
858   { 69, "PID_KNXNETIP_DEVICE_STATE" },
859   { 70, "PID_KNXNETIP_ROUTING_CAPABILITIES" },
860   { 71, "PID_PRIORITY_FIFO_ENABLED" },
861   { 72, "PID_QUEUE_OVERFLOW_TO_IP" },
862   { 73, "PID_QUEUE_OVERFLOW_TO_KNX" },
863   { 74, "PID_MSG_TRANSMIT_TO_IP" },
864   { 75, "PID_MSG_TRANSMIT_TO_KNX" },
865   { 76, "PID_FRIENDLY_NAME" },
866   { 78, "PID_ROUTING_BUSY_WAIT_TIME" },
867   { 91, "PID_BACKBONE_KEY" },
868   { 92, "PID_DEVICE_AUTHENTICATION_CODE" },
869   { 93, "PID_PASSWORD_HASHES" },
870   { 94, "PID_SECURED_SERVICE_FAMILIES" },
871   { 95, "PID_MULTICAST_LATENCY_TOLERANCE" },
872   { 96, "PID_SYNC_LATENCY_FRACTION" },
873   { 97, "PID_TUNNELLING_USERS" },
874   { 0, NULL }
875 };
876 
877 /* PIDs for IOT = 17 (Security)
878 * See "2.3.5 Security Interface Object" in "KSG638-26.03 KNX Data Security"
879 */
880 static const value_string pid17_vals[] = {
881   { 51, "PID_SECURITY_MODE" },
882   { 52, "PID_P2P_KEY_TABLE" },
883   { 53, "PID_GRP_KEY_TABLE" },
884   { 54, "PID_SECURITY_INDIVIDUAL_ADDRESS_TABLE" },
885   { 55, "PID_SECURITY_FAILURES_LOG" },
886   { 56, "PID_TOOL_KEY" },
887   { 57, "PID_SECURITY_REPORT" },
888   { 58, "PID_SECURITY_REPORT_CONTROL" },
889   { 59, "PID_SEQUENCE_NUMBER_SENDING" },
890   { 60, "PID_ZONE_KEY_TABLE" },
891   { 61, "PID_GO_SECURITY_FLAGS" },
892   { 62, "PID_ROLE_TABLE" },
893   { 0, NULL }
894 };
895 
896 /* - - - - - - - - -  H E L P E R   F U N C T I O N S  - - - - - - - - - -
897 */
898 
899 /* Add raw data to list view, tree view, and parent folder
900 */
proto_tree_add_data(proto_tree * tree,tvbuff_t * tvb,gint offset,gint length,column_info * cinfo,proto_item * item,const gchar * name,const gchar * text1,const gchar * text2)901 static proto_item* proto_tree_add_data( proto_tree* tree, tvbuff_t* tvb, gint offset, gint length, column_info* cinfo, proto_item* item,
902   const gchar* name, const gchar* text1, const gchar* text2 )
903 {
904   proto_item* new_item = proto_tree_add_bytes_format( tree, hf_bytes, tvb, offset, length, NULL, "%s: $", name );
905   if( text1 ) col_append_str( cinfo, COL_INFO, text1 );
906   if( text2 ) proto_item_append_text( item, "%s", text2 );
907 
908   while( length > 0 )
909   {
910     guint8 value = tvb_get_guint8( tvb, offset );
911     if( text1 ) col_append_fstr( cinfo, COL_INFO, "%02X", value );
912     if( text2 ) proto_item_append_text( item, "%02X", value );
913     proto_item_append_text( new_item, " %02X", value );
914     offset++;
915     length--;
916   }
917 
918   return new_item;
919 }
920 
get_pid_name(gint ot,gint pid)921 static const gchar* get_pid_name( gint ot, gint pid )
922 {
923   if( pid <= 50 )
924   {
925     return try_val_to_str( pid, pid_vals );
926   }
927   {
928     const value_string* vals = NULL;
929     switch( ot )
930     {
931     case 0:
932       vals = pid0_vals;
933       break;
934     case 1:
935       vals = pid1_vals;
936       break;
937     case 6:
938       vals = pid6_vals;
939       break;
940     case 7:
941       vals = pid7_vals;
942       break;
943     case 8:
944       vals = pid8_vals;
945       break;
946     case 9:
947       vals = pid9_vals;
948       break;
949     case 11:
950       vals = pid11_vals;
951       break;
952     case 17:
953       vals = pid17_vals;
954       break;
955     }
956     if( vals )
957     {
958       return try_val_to_str( pid, vals );
959     }
960   }
961   return NULL;
962 }
963 
964 /* Decrypt data security APDU with a specific key.
965 */
decrypt_data_security_data_with_key(wmem_allocator_t * pool,const guint8 * key,const guint8 * encrypted,gint encrypted_size,const guint8 * cemi,gint cemi_size)966 static const guint8* decrypt_data_security_data_with_key( wmem_allocator_t *pool, const guint8* key, const guint8* encrypted, gint encrypted_size, const guint8* cemi, gint cemi_size )
967 {
968   guint8 ctr_0[ KNX_KEY_LENGTH ];
969   guint8 b_0[ KNX_KEY_LENGTH ];
970   guint8 mac[ KNX_KEY_LENGTH ];
971   guint8* a_bytes = 0;
972   const guint8* p_bytes = NULL;
973   gint a_length = 0;
974   gint p_length = 0;
975 
976   guint8* decrypted = NULL;
977 
978   if( encrypted_size > 4 )  // contains 4 bytes MAC
979   {
980     if( cemi_size >= 2 )
981     {
982       gint additionalInfoLength = cemi[ 1 ];
983       gint offsetToData = additionalInfoLength + 11;
984       if( offsetToData + 6 <= cemi_size )
985       {
986         /* 1 byte Security Control Field */
987         guint8 scf = cemi[ offsetToData ];
988 
989         // Get A and P.
990         if( (scf & 0x30) == 0x10 ) // A+C
991         {
992           p_bytes = encrypted;
993           p_length = encrypted_size - 4;
994         }
995 
996         // Build b_0.
997         b_0[ 0 ] = cemi[ offsetToData + 1 ]; // SeqNr
998         b_0[ 1 ] = cemi[ offsetToData + 2 ];
999         b_0[ 2 ] = cemi[ offsetToData + 3 ];
1000         b_0[ 3 ] = cemi[ offsetToData + 4 ];
1001         b_0[ 4 ] = cemi[ offsetToData + 5 ];
1002         b_0[ 5 ] = cemi[ offsetToData + 6 ];
1003         b_0[ 6 ] = cemi[ additionalInfoLength + 4 ]; // SA
1004         b_0[ 7 ] = cemi[ additionalInfoLength + 5 ];
1005         b_0[ 8 ] = cemi[ additionalInfoLength + 6 ]; // DA
1006         b_0[ 9 ] = cemi[ additionalInfoLength + 7 ];
1007         b_0[ 10 ] = 0; // cemi[additionalInfoLength + 2] & 0x80; // FT
1008         b_0[ 11 ] = cemi[ additionalInfoLength + 3 ] & 0x8F; // AT (AT+EFF)
1009         b_0[ 12 ] = cemi[ additionalInfoLength + 9 ]; // TPCI + ApciSec
1010         b_0[ 13 ] = cemi[ additionalInfoLength + 10 ];
1011         b_0[ 14 ] = 0;
1012         b_0[ 15 ] = (guint8) p_length;
1013 
1014         // Build ctr_0.
1015         ctr_0[ 0 ] = cemi[ offsetToData + 1 ]; // SeqNr
1016         ctr_0[ 1 ] = cemi[ offsetToData + 2 ];
1017         ctr_0[ 2 ] = cemi[ offsetToData + 3 ];
1018         ctr_0[ 3 ] = cemi[ offsetToData + 4 ];
1019         ctr_0[ 4 ] = cemi[ offsetToData + 5 ];
1020         ctr_0[ 5 ] = cemi[ offsetToData + 6 ];
1021         ctr_0[ 6 ] = cemi[ additionalInfoLength + 4 ]; // SA
1022         ctr_0[ 7 ] = cemi[ additionalInfoLength + 5 ];
1023         ctr_0[ 8 ] = cemi[ additionalInfoLength + 6 ]; // DA
1024         ctr_0[ 9 ] = cemi[ additionalInfoLength + 7 ];
1025         ctr_0[ 10 ] = 0;
1026         ctr_0[ 11 ] = 0;
1027         ctr_0[ 12 ] = 0;
1028         ctr_0[ 13 ] = 0;
1029         ctr_0[ 14 ] = 0x01;
1030         ctr_0[ 15 ] = 0;
1031 
1032         decrypted = knx_ccm_encrypt( 0, key, p_bytes, p_length, encrypted + encrypted_size - 4, 4, ctr_0, 4 );
1033 
1034         a_bytes = (guint8*) wmem_alloc( pool, encrypted_size );
1035         if( (scf & 0x30) == 0x10 ) // A+C
1036         {
1037           a_bytes[ 0 ] = scf;
1038           a_length = 1;
1039           p_bytes = decrypted;
1040           p_length = encrypted_size - 4;
1041         }
1042         else if( (scf & 0x30) == 0x00 ) // A
1043         {
1044           a_bytes[ 0 ] = scf;
1045           memcpy( a_bytes + 1, decrypted, encrypted_size - 4 );
1046           a_length = encrypted_size - 3;
1047         }
1048 
1049         knx_ccm_calc_cbc_mac( mac, key, a_bytes, a_length, p_bytes, p_length, b_0 );
1050         wmem_free( pool, a_bytes );
1051 
1052         if( memcmp( mac, decrypted + p_length, 4 ) != 0 )
1053         {
1054           // Wrong mac. Return 0.
1055           wmem_free( pool, decrypted );
1056           decrypted = NULL;
1057         }
1058       }
1059     }
1060   }
1061 
1062   return decrypted;
1063 }
1064 
1065 /* Context info for decrypt_data_security_data
1066 */
1067 struct data_security_info
1068 {
1069   guint16 source;       // KNX source address
1070   guint16 dest;         // KNX source address
1071   guint8 multicast;     // KNX multicast (group addressed)?
1072   guint64 seq_nr;       // 6-byte data security sequence number
1073   gchar output_text[ 128 ];  // buffer for diagnostic output text
1074 };
1075 
1076 /* Decrypt data security APDU.
1077 */
decrypt_data_security_data(wmem_allocator_t * pool,const guint8 * encrypted,gint encrypted_size,const guint8 * cemi,gint cemi_size,struct data_security_info * info)1078 static const guint8* decrypt_data_security_data( wmem_allocator_t *pool, const guint8* encrypted, gint encrypted_size, const guint8* cemi, gint cemi_size, struct data_security_info* info )
1079 {
1080   const guint8* key = NULL;
1081   const guint8* decrypted = NULL;
1082   guint8 keys_found = 0;
1083 
1084   // Get context info
1085   guint16 source = info->source;
1086   guint16 dest = info->dest;
1087   guint8 multicast = info->multicast;
1088 
1089   gchar* output = info->output_text;
1090   gint output_max = sizeof info->output_text;
1091   g_snprintf( output, output_max, "with " );
1092   while( *output ) { ++output; --output_max; }
1093 
1094   // Try keys from keyring.XML
1095   if( multicast )
1096   {
1097     // Try keys associated with GA
1098     struct knx_keyring_ga_keys* ga_key;
1099 
1100     for( ga_key = knx_keyring_ga_keys; ga_key; ga_key = ga_key->next )
1101     {
1102       if( ga_key->ga == dest )
1103       {
1104         keys_found = 1;
1105         key = ga_key->key;
1106         decrypted = decrypt_data_security_data_with_key( pool, key, encrypted, encrypted_size, cemi, cemi_size );
1107 
1108         if( decrypted )
1109         {
1110           g_snprintf( output, output_max, "GA " );
1111           while( *output ) { ++output; --output_max; }
1112           break;
1113         }
1114       }
1115     }
1116   }
1117   else
1118   {
1119     // Try keys associated with dest IA
1120     struct knx_keyring_ia_keys* ia_key;
1121 
1122     for( ia_key = knx_keyring_ia_keys; ia_key; ia_key = ia_key->next )
1123     {
1124       if( ia_key->ia == dest )
1125       {
1126         keys_found = 1;
1127         key = ia_key->key;
1128         decrypted = decrypt_data_security_data_with_key( pool, key, encrypted, encrypted_size, cemi, cemi_size );
1129 
1130         if( decrypted )
1131         {
1132           g_snprintf( output, output_max, "dest IA " );
1133           while( *output ) { ++output; --output_max; }
1134           break;
1135         }
1136       }
1137     }
1138   }
1139 
1140   if( !decrypted )
1141   {
1142     // Try keys associated with source IA
1143     struct knx_keyring_ia_keys* ia_key;
1144 
1145     for( ia_key = knx_keyring_ia_keys; ia_key; ia_key = ia_key->next )
1146     {
1147       if( ia_key->ia == source )
1148       {
1149         keys_found = 1;
1150         key = ia_key->key;
1151         decrypted = decrypt_data_security_data_with_key( pool, key, encrypted, encrypted_size, cemi, cemi_size );
1152 
1153         if( decrypted )
1154         {
1155           g_snprintf( output, output_max, "source IA " );
1156           while( *output ) { ++output; --output_max; }
1157           break;
1158         }
1159       }
1160     }
1161   }
1162 
1163   if( !decrypted && knx_decryption_key_count )
1164   {
1165     // Try all explicitly specified keys
1166     guint8 key_index;
1167 
1168     for( key_index = 0; key_index < knx_decryption_key_count; ++key_index )
1169     {
1170       keys_found = 1;
1171       key = knx_decryption_keys[ key_index ];
1172       decrypted = decrypt_data_security_data_with_key( pool, key, encrypted, encrypted_size, cemi, cemi_size );
1173 
1174       if( decrypted )
1175       {
1176         break;
1177       }
1178     }
1179   }
1180 
1181   if( decrypted )
1182   {
1183     guint8 count;
1184 
1185     g_snprintf( output, output_max, "key" );
1186 
1187     for( count = 16; count; --count )
1188     {
1189       while( *output ) { ++output; --output_max; }
1190       g_snprintf( output, output_max, " %02X", *key++ );
1191     }
1192   }
1193   else
1194   {
1195     g_snprintf( info->output_text, sizeof info->output_text, keys_found ? "failed" : "no keys found" );
1196   }
1197 
1198   return decrypted;
1199 }
1200 
1201 /* Dissect Object Index (1 byte)
1202 */
dissect_ox(tvbuff_t * tvb,packet_info * pinfo,proto_item * node,proto_tree * list,gint * p_offset,gint end_pos,guint8 * p_error)1203 static guint8 dissect_ox( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, guint8 *p_error )
1204 {
1205   gint offset = *p_offset;
1206 
1207   if( offset < end_pos )
1208   {
1209     guint8 ox = tvb_get_guint8( tvb, offset );
1210     column_info *cinfo = pinfo->cinfo;
1211 
1212     col_append_fstr( cinfo, COL_INFO, " OX=%u", ox );
1213     proto_item_append_text( node, ", OX=%u", ox );
1214     proto_tree_add_item( list, hf_cemi_ox, tvb, offset, 1, ENC_BIG_ENDIAN );
1215 
1216     *p_offset = ++offset;
1217     return ox;
1218   }
1219 
1220   proto_tree_add_expert_format( list, pinfo, KIP_ERROR, tvb, offset, 0, "? Object Index: expected 1 byte" );
1221 
1222   if( p_error )
1223   {
1224     *p_error = 1;
1225   }
1226 
1227   return 0;
1228 }
1229 
1230 /* Dissect Object Type (2 bytes)
1231 */
dissect_ot(tvbuff_t * tvb,packet_info * pinfo,proto_item * node,proto_tree * list,gint * p_offset,gint end_pos,guint8 * p_error)1232 static guint16 dissect_ot( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, guint8 *p_error )
1233 {
1234   gint offset = *p_offset;
1235 
1236   if( offset + 1 < end_pos )
1237   {
1238     guint16 ot = tvb_get_ntohs( tvb, offset );
1239     column_info *cinfo = pinfo->cinfo;
1240 
1241     col_append_fstr( cinfo, COL_INFO, " OT=%u", ot );
1242     proto_item_append_text( node, ", OT=%u", ot );
1243 
1244     proto_tree_add_item( list, hf_cemi_ot, tvb, offset, 2, ENC_BIG_ENDIAN );
1245     offset += 2;
1246 
1247     *p_offset = offset;
1248     return ot;
1249   }
1250 
1251   node = proto_tree_add_bytes_format( list, hf_bytes, tvb, offset, end_pos - offset, NULL, "? Object Type" );
1252   expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
1253 
1254   if( p_error )
1255   {
1256     *p_error = 1;
1257   }
1258 
1259   *p_offset = end_pos;
1260   return 0;
1261 }
1262 
1263 /* Dissect Property Identifier (1 byte)
1264 */
dissect_pid(tvbuff_t * tvb,packet_info * pinfo,proto_item * node,proto_tree * list,gint * p_offset,gint end_pos,gint ot,guint8 show,guint8 * p_error)1265 static guint8 dissect_pid( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, gint ot, guint8 show, guint8 *p_error )
1266 {
1267   gint offset = *p_offset;
1268 
1269   if( offset < end_pos )
1270   {
1271     guint8 pid = tvb_get_guint8( tvb, offset );
1272     column_info *cinfo = pinfo->cinfo;
1273     const gchar* name;
1274 
1275     if( pid || show )
1276     {
1277       col_append_fstr( cinfo, COL_INFO, " P=%u", pid );
1278       proto_item_append_text( node, ", PID=%u", pid );
1279     }
1280 
1281     if( list )
1282     {
1283       node = proto_tree_add_item( list, hf_cemi_pid, tvb, offset, 1, ENC_BIG_ENDIAN );
1284       name = get_pid_name( ot, pid );
1285       if( name )
1286       {
1287         proto_item_append_text( node, " = %s", name );
1288       }
1289     }
1290 
1291     *p_offset = ++offset;
1292     return pid;
1293   }
1294 
1295   proto_tree_add_expert_format( list, pinfo, KIP_ERROR, tvb, offset, 0, "? Property ID: expected 1 byte" );
1296 
1297   if( p_error )
1298   {
1299     *p_error = 1;
1300   }
1301 
1302   return 0;
1303 }
1304 
1305 /* Dissect Property Index (1 byte)
1306 */
dissect_px(tvbuff_t * tvb,packet_info * pinfo,proto_item * node,proto_tree * list,gint * p_offset,gint end_pos,guint8 show,guint8 * p_error)1307 static guint8 dissect_px( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, guint8 show, guint8 *p_error )
1308 {
1309   gint offset = *p_offset;
1310 
1311   if( offset < end_pos )
1312   {
1313     guint8 px = tvb_get_guint8( tvb, offset );
1314 
1315     if( show )
1316     {
1317       column_info *cinfo = pinfo->cinfo;
1318       col_append_fstr( cinfo, COL_INFO, " PX=%u", px );
1319       proto_item_append_text( node, ", PX=%u", px );
1320     }
1321 
1322     proto_tree_add_item( list, hf_cemi_px, tvb, offset, 1, ENC_BIG_ENDIAN );
1323 
1324     *p_offset = ++offset;
1325     return px;
1326   }
1327 
1328   proto_tree_add_expert_format( list, pinfo, KIP_ERROR, tvb, offset, 0, "? Property Index: expected 1 byte" );
1329 
1330   if( p_error )
1331   {
1332     *p_error = 1;
1333   }
1334 
1335   return 0;
1336 }
1337 
1338 /* Dissect Property Range (2 bytes: Number Of Elements (4 bits), Start Index (12 bits))
1339   and subsequent Data
1340 */
dissect_range(tvbuff_t * tvb,packet_info * pinfo,proto_item * node,proto_tree * list,gint * p_offset,gint end_pos,guint8 pa_flags,guint8 * p_error)1341 static void dissect_range( tvbuff_t *tvb, packet_info *pinfo, proto_item *node, proto_tree *list, gint *p_offset, gint end_pos, guint8 pa_flags, guint8 *p_error )
1342 {
1343   gint offset = *p_offset;
1344   guint8 error = 0;
1345 
1346   if( offset + 1 >= end_pos )
1347   {
1348     node = proto_tree_add_bytes_format( list, hf_bytes, tvb, offset, end_pos - offset, NULL, "? Range" );
1349     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
1350 
1351     *p_offset = end_pos;
1352     error = 1;
1353   }
1354   else
1355   {
1356     column_info *cinfo = pinfo->cinfo;
1357     proto_item *cemi_node = node;
1358     guint16 sx = tvb_get_ntohs( tvb, offset );
1359     guint8 ne = sx >> 12;
1360     sx &= 0x0FFF;
1361 
1362     /* 4 bits Number Of Elements */
1363     if( ne != 1 )
1364     {
1365       col_append_fstr( cinfo, COL_INFO, " N=%u", ne );
1366       proto_item_append_text( node, ", N=%u", ne );
1367 
1368       if( ne == 0 && !(pa_flags & PA_RESPONSE) )
1369       {
1370         error = 1;
1371       }
1372       else if( sx == 0 )
1373       {
1374         error = 2;
1375       }
1376     }
1377 
1378     /* 12 bits Start Index */
1379     if( sx != 1 )
1380     {
1381       col_append_fstr( cinfo, COL_INFO, " X=%u", sx );
1382       proto_item_append_text( node, ", X=%u", sx );
1383     }
1384 
1385     if( list )
1386     {
1387       proto_item *range_node = proto_tree_add_none_format( list, hf_folder, tvb, offset, 2, "Range: %u element%s at position %u", ne, (ne == 1) ? "" : "s", sx );
1388       proto_tree *range_list = proto_item_add_subtree( range_node, ett_cemi_range );
1389 
1390       /* 4 bits Number Of Elements */
1391       node = proto_tree_add_item( range_list, hf_cemi_ne, tvb, offset, 1, ENC_BIG_ENDIAN );
1392 
1393       if( error )
1394       {
1395         proto_item_prepend_text( range_node, "? " );
1396         proto_item_prepend_text( node, "? " );
1397         expert_add_info_format( pinfo, node, KIP_ERROR, (error == 1) ? "Expected: >= 1 element(s)" : "Expected: 1 element" );
1398       }
1399 
1400       /* 12 bits Start Index */
1401       proto_tree_add_item( range_list, hf_cemi_sx, tvb, offset, 2, ENC_BIG_ENDIAN );
1402     }
1403 
1404     offset += 2;
1405 
1406     /* Data */
1407     {
1408       gint length = end_pos - offset;
1409       if( length > 0 )
1410       {
1411         node = proto_tree_add_data( list, tvb, offset, length, cinfo, cemi_node, "Data", " $", ", $" );
1412         if( !pa_flags )
1413         {
1414           proto_item_prepend_text( node, "? " );
1415           expert_add_info_format( pinfo, node, KIP_ERROR, "Unexpected" );
1416           error = 1;
1417         }
1418         else if( (pa_flags & PA_RESPONSE) && (!(pa_flags & PA_DATA) || ne == 0) && length != 1 )
1419         {
1420           proto_item_prepend_text( node, "? " );
1421           expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: max 1 byte" );
1422           error = 1;
1423         }
1424         else if( pa_flags & PA_DATA )
1425         {
1426           if( ne == 1 && sx == 0 && length != 2 )
1427           {
1428             proto_item_prepend_text( node, "? " );
1429             expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
1430             error = 1;
1431           }
1432           else if( ne >= 2 && (length % ne) != 0 )
1433           {
1434             proto_item_prepend_text( node, "? " );
1435             expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: multiple of %u bytes", ne );
1436             error = 1;
1437           }
1438         }
1439       }
1440     }
1441 
1442     *p_offset = end_pos;
1443   }
1444 
1445   if( error && p_error )
1446   {
1447     *p_error = 1;
1448   }
1449 }
1450 
1451 /* Dissect Property Description: PDT (1 byte), Max Count (2 bytes), Access Levels (1 byte)
1452 */
dissect_prop_descr(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_error)1453 static void dissect_prop_descr( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list, gint* p_offset, gint size, guint8* p_error )
1454 {
1455   gint offset = *p_offset;
1456   column_info* cinfo = pinfo->cinfo;
1457   guint8 error = 0;
1458 
1459   /* 4 bytes Property Description */
1460   if( offset + 4 > size )
1461   {
1462     proto_item* node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Property Description" );
1463     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bytes" );
1464 
1465     error = 1;
1466     offset = size;
1467   }
1468   else
1469   {
1470     /* 1 bit Writability, 1 bit reserved, 6 bits Property Data Type */
1471     guint8 pdt = tvb_get_guint8( tvb, offset );
1472     guint8 writable = (pdt & 0x80) != 0;
1473     pdt &= 0x3F;
1474     col_append_fstr( cinfo, COL_INFO, " T=%u", pdt );
1475     proto_item_append_text( cemi_node, ", T=%u", pdt );
1476 
1477     /* 4 bits reserved, 12 bits Max Elements */
1478     guint16 me = tvb_get_ntohs( tvb, offset + 1) & 0x0FFF;
1479     if( me != 1 )
1480     {
1481       col_append_fstr( cinfo, COL_INFO, " N=%u", me );
1482       proto_item_append_text( cemi_node, ", N=%u", me );
1483     }
1484 
1485     /* 4 bits Read Access, 4 bits Write Access */
1486     guint8 wa = tvb_get_guint8( tvb, offset + 3 );
1487     guint8 ra = (wa & 0xF0) >> 4;
1488     wa &= 0x0F;
1489     col_append_fstr( cinfo, COL_INFO, " R=%u", ra );
1490     if( writable )
1491       col_append_fstr( cinfo, COL_INFO, " W=%u", wa );
1492     proto_item_append_text( cemi_node, ", R=%u", ra );
1493     if( writable )
1494       proto_item_append_text( cemi_node, ", W=%u", wa );
1495 
1496     if( cemi_list )
1497     {
1498       proto_item *pd_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 4, "Property Description: " );
1499       proto_tree *pd_list = proto_item_add_subtree( pd_node, ett_cemi_pd );
1500 
1501       const gchar* pdt_name = try_val_to_str( pdt, pdt_vals );
1502       if( pdt_name )
1503         proto_item_append_text( pd_node, "%s", pdt_name );
1504       else
1505         proto_item_append_text( pd_node, "PDT = 0x%02X", pdt );
1506 
1507       if( me != 1 )
1508         proto_item_append_text( pd_node, ", Max Elements = %u", me );
1509 
1510       proto_item_append_text( pd_node, ", Read = %u", ra );
1511       if( writable )
1512         proto_item_append_text( pd_node, ", Write = %u", wa );
1513 
1514       proto_tree_add_item( pd_list, hf_cemi_pw, tvb, offset, 1, ENC_BIG_ENDIAN );
1515       proto_tree_add_item( pd_list, hf_cemi_pdt, tvb, offset, 1, ENC_BIG_ENDIAN );
1516       proto_tree_add_item( pd_list, hf_cemi_me, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
1517       proto_tree_add_item( pd_list, hf_cemi_ra, tvb, offset + 3, 1, ENC_BIG_ENDIAN );
1518       proto_tree_add_item( pd_list, hf_cemi_wa, tvb, offset + 3, 1, ENC_BIG_ENDIAN );
1519     }
1520 
1521     offset += 4;
1522   }
1523 
1524   if( error && p_error )
1525   {
1526     *p_error = 1;
1527   }
1528 
1529   *p_offset = offset;
1530 }
1531 
1532 /* Dissect OT (2 bytes), OI (12 bits), and PID (12 bits) for PropertyExt services
1533 */
dissect_pid_ext(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_error)1534 static void dissect_pid_ext( tvbuff_t *tvb, packet_info *pinfo, proto_item *cemi_node, proto_tree *cemi_list, gint *p_offset, gint size, guint8 *p_error )
1535 {
1536   gint offset = *p_offset;
1537   column_info* cinfo = pinfo->cinfo;
1538   guint8 error = 0;
1539 
1540   /* 2 bytes Object Type */
1541   guint16 ot = dissect_ot( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
1542 
1543   if( offset + 3 > size )
1544   {
1545     proto_item* node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Object Instance, PID" );
1546     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 3 bytes" );
1547 
1548     error = 1;
1549     offset = size;
1550   }
1551   else
1552   {
1553     /* 12 bits Object Instance */
1554     guint16 cc = tvb_get_ntohs( tvb, offset ) >> 4;
1555     col_append_fstr( cinfo, COL_INFO, " OI=%u", cc );
1556     proto_item_append_text( cemi_node, ", OI=%u", cc );
1557     proto_tree_add_item( cemi_list, hf_cemi_ext_oi, tvb, offset, 2, ENC_BIG_ENDIAN );
1558     ++offset;
1559 
1560     /* 12 bits Property ID */
1561     cc = tvb_get_ntohs( tvb, offset ) & 0x0FFF;
1562     col_append_fstr( cinfo, COL_INFO, " P=%u", cc );
1563     proto_item_append_text( cemi_node, ", PID=%u", cc );
1564 
1565     if( cemi_list )
1566     {
1567       proto_item* node = proto_tree_add_item( cemi_list, hf_cemi_ext_pid, tvb, offset, 2, ENC_BIG_ENDIAN );
1568       const gchar* name = get_pid_name( ot, cc );
1569       if( name )
1570       {
1571         proto_item_append_text( node, " = %s", name );
1572       }
1573     }
1574 
1575     offset += 2;
1576   }
1577 
1578   if( error && p_error )
1579   {
1580     *p_error = 1;
1581   }
1582 
1583   *p_offset = offset;
1584 }
1585 
1586 /* Dissect cEMI Management packet
1587   (M_PropRead.req, M_PropRead.con, M_PropWrite.req, M_PropWrite.con, M_PropInfo.ind, M_Reset.req, M_Reset.ind)
1588 */
dissect_cemi_mgmt_packet(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,guint8 mc,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1589 static void dissect_cemi_mgmt_packet( tvbuff_t* tvb, packet_info* pinfo, proto_item *cemi_node, proto_tree *cemi_list, guint8 mc, gint *p_offset, gint size, guint8* p_pa_flags, guint8 *p_error )
1590 {
1591   column_info* cinfo = pinfo->cinfo;
1592   gint offset = *p_offset;
1593   guint8 pa_flags = *p_pa_flags;
1594   guint8 error = *p_error;
1595 
1596   guint8 min_size = 1;
1597 
1598   switch( mc )
1599   {
1600   case CEMI_M_PROPREAD_REQ:
1601     pa_flags = 0;
1602     goto case_CEMI_M_PROP;
1603 
1604   case CEMI_M_PROPWRITE_CON:
1605     pa_flags = PA_RESPONSE;
1606     goto case_CEMI_M_PROP;
1607 
1608   case CEMI_M_PROPREAD_CON:
1609     pa_flags = PA_RESPONSE | PA_DATA;
1610     goto case_CEMI_M_PROP;
1611 
1612   case CEMI_M_PROPWRITE_REQ:
1613   case CEMI_M_PROPINFO_IND:
1614     //pa_flags = PA_DATA;
1615 
1616   case_CEMI_M_PROP:
1617     min_size = 7;
1618     break;
1619 
1620   case CEMI_M_FUNCPROPCMD_REQ:
1621   case CEMI_M_FUNCPROPREAD_REQ:
1622   case CEMI_M_FUNCPROP_CON:
1623     //pa_flags = PA_DATA;
1624     min_size = 5;
1625     break;
1626 
1627   case CEMI_M_RESET_REQ:
1628   case CEMI_M_RESET_IND:
1629     pa_flags = 0;
1630     break;
1631   }
1632 
1633   if( min_size >= 5 )
1634   {
1635     /* 2 bytes Object Type */
1636     guint16 ot = dissect_ot( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
1637 
1638     /* 1 byte Object Instance */
1639     if( size < 4 )
1640     {
1641       proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Object Instance: expected 1 byte" );
1642       error = 1;
1643     }
1644     else
1645     {
1646       guint8 oi = tvb_get_guint8( tvb, 3 );
1647       if( oi != 1 )
1648       {
1649         col_append_fstr( cinfo, COL_INFO, " OI=%u", oi );
1650         proto_item_append_text( cemi_node, ", OI=%u", oi );
1651       }
1652       proto_tree_add_item( cemi_list, hf_cemi_oi, tvb, 3, 1, ENC_BIG_ENDIAN );
1653       offset = 4;
1654     }
1655 
1656     /* 1 byte Property ID
1657     */
1658     dissect_pid( tvb, pinfo, cemi_node, cemi_list, &offset, size, ot, 1, &error );
1659 
1660     if( min_size >= 7 )
1661     {
1662       /* Range (Start Index, Number Of Elements) and Data
1663       */
1664       dissect_range( tvb, pinfo, cemi_node, cemi_list, &offset, size, pa_flags, &error );
1665       pa_flags = 0;
1666     }
1667   }
1668 
1669   *p_offset = offset;
1670   *p_pa_flags = pa_flags;
1671   *p_error = error;
1672 }
1673 
1674 /* Dissect A_MemoryExt service */
dissect_memory_ext_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,guint16 ax,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1675 static void dissect_memory_ext_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
1676   guint16 ax, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
1677 {
1678   column_info* cinfo = pinfo->cinfo;
1679   gint offset = *p_offset;
1680   guint8 pa_flags = *p_pa_flags;
1681   guint8 error = *p_error;
1682 
1683   /* 4 bytes Range (1 byte Memory Length, 3 bytes Memory Address) */
1684   if( offset + 4 > size )
1685   {
1686     proto_item* node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Range" );
1687     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bytes" );
1688     error = 1;
1689     offset = size;
1690   }
1691   else
1692   {
1693     /* 1 byte Memory Length or Error Code */
1694     guint8 is_response = (ax == AX_MemExtReadResp || ax == AX_MemExtWriteResp);
1695     guint8 n = tvb_get_guint8( tvb, offset );
1696     if( is_response )
1697     {
1698       if( n != 0 )
1699       {
1700         col_append_fstr( cinfo, COL_INFO, " E=$%02X", n );
1701         proto_item_append_text( cemi_node, ", E=$%02X", n );
1702       }
1703     }
1704     else
1705     {
1706       if( n != 1 )
1707       {
1708         col_append_fstr( cinfo, COL_INFO, " N=%u", n );
1709         proto_item_append_text( cemi_node, ", N=%u", n );
1710       }
1711     }
1712 
1713     /* 3 bytes Memory Address */
1714     guint32 x = tvb_get_guint24( tvb, offset + 1, ENC_BIG_ENDIAN );
1715     col_append_fstr( cinfo, COL_INFO, " X=$%06" G_GINT32_MODIFIER "X", x );
1716     proto_item_append_text( cemi_node, ", X=$%06" G_GINT32_MODIFIER "X", x );
1717 
1718     if( is_response )
1719     {
1720       proto_tree_add_item( cemi_list, hf_cemi_error, tvb, offset, 1, ENC_BIG_ENDIAN );
1721     }
1722     else
1723     {
1724       proto_tree_add_item( cemi_list, hf_cemi_ext_memory_length, tvb, offset, 1, ENC_BIG_ENDIAN );
1725     }
1726 
1727     proto_tree_add_item( cemi_list, hf_cemi_ext_memory_address, tvb, offset + 1, 3, ENC_BIG_ENDIAN );
1728 
1729     offset += 4;
1730   }
1731 
1732   *p_offset = offset;
1733   *p_pa_flags = pa_flags;
1734   *p_error = error;
1735 }
1736 
1737 /* Dissect A_UserMemory service */
dissect_user_memory_service(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1738 static void dissect_user_memory_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
1739   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
1740 {
1741   column_info* cinfo = pinfo->cinfo;
1742   gint offset = *p_offset;
1743   guint8 pa_flags = *p_pa_flags;
1744   guint8 error = *p_error;
1745 
1746   proto_item* node;
1747   proto_tree* list;
1748 
1749   /* 3 bytes Range (Memory Length, Memory Address) */
1750   if( offset + 3 > size )
1751   {
1752     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Range" );
1753     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 3 bytes" );
1754     error = 1;
1755     offset = size;
1756   }
1757   else
1758   {
1759     guint8 c2 = tvb_get_guint8( tvb, offset );
1760     guint8 c1 = c2 >> 4;
1761     guint32 c3 = tvb_get_ntohs( tvb, offset + 1 );
1762     c2 &= 0x0F;
1763     c3 |= c1 << 16UL;
1764     if( c2 != 1 )
1765       col_append_fstr( cinfo, COL_INFO, " N=%u", c2 );
1766     col_append_fstr( cinfo, COL_INFO, " X=$%05X", c3 );
1767     if( tree )
1768     {
1769       if( c2 != 1 )
1770         proto_item_append_text( cemi_node, ", N=%u", c2 );
1771       proto_item_append_text( cemi_node, ", X=$%05X", c3 );
1772       node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1,
1773         "Range: %u byte%s at address $%05X", c2, (c2 == 1) ? "" : "s", c3 );
1774       list = proto_item_add_subtree( node, ett_cemi_range );
1775       proto_tree_add_item( list, hf_cemi_memory_address_ext, tvb, offset, 1, ENC_BIG_ENDIAN );
1776       proto_tree_add_item( list, hf_cemi_memory_length, tvb, offset, 1, ENC_BIG_ENDIAN );
1777       proto_tree_add_item( list, hf_cemi_memory_address, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
1778     }
1779     offset += 3;
1780   }
1781 
1782   *p_offset = offset;
1783   *p_pa_flags = pa_flags;
1784   *p_error = error;
1785 }
1786 
1787 /* Dissect A_FunctionProperty service */
dissect_function_property_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_error)1788 static void dissect_function_property_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
1789   gint* p_offset, gint size, guint8* p_error )
1790 {
1791   /* 1 byte Object Index */
1792   dissect_ox( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
1793 
1794   /* 1 byte Property ID */
1795   dissect_pid( tvb, pinfo, cemi_node, cemi_list, p_offset, size, -1, 1, p_error );
1796 }
1797 
1798 /* Dissect (obsolete) A_Router service */
dissect_router_service(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1799 static void dissect_router_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
1800   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
1801 {
1802   column_info* cinfo = pinfo->cinfo;
1803   gint offset = *p_offset;
1804   guint8 pa_flags = *p_pa_flags;
1805   guint8 error = *p_error;
1806 
1807   proto_item* node;
1808   proto_tree* list;
1809 
1810   /* 3 bytes Range (1 byte Memory Length, 2 bytes Memory Address) */
1811   if( offset + 3 > size )
1812   {
1813     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Range" );
1814     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 3 bytes" );
1815     error = 1;
1816     offset = size;
1817   }
1818   else
1819   {
1820     guint8 c = tvb_get_guint8( tvb, offset );
1821     guint16 cc = tvb_get_ntohs( tvb, offset + 1 );
1822     if( c != 1 )
1823       col_append_fstr( cinfo, COL_INFO, " N=%u", c );
1824     col_append_fstr( cinfo, COL_INFO, " X=$%04X", cc );
1825     if( tree )
1826     {
1827       proto_item_append_text( cemi_node, ", N=%u, X=$%04X", c, cc );
1828       node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 3,
1829         "Range: %u byte%s at address $%04X", c, (c == 1) ? "" : "s", cc );
1830       list = proto_item_add_subtree( node, ett_cemi_range );
1831       proto_tree_add_item( list, hf_cemi_ext_memory_length, tvb, offset, 1, ENC_BIG_ENDIAN );
1832       proto_tree_add_item( list, hf_cemi_ext_memory_address, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
1833     }
1834     offset += 3;
1835   }
1836 
1837   *p_offset = offset;
1838   *p_pa_flags = pa_flags;
1839   *p_error = error;
1840 }
1841 
1842 /* Dissect A_Authenticate or A_Key service */
dissect_authenticate_service(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,guint16 ax,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1843 static void dissect_authenticate_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
1844   guint16 ax, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
1845 {
1846   column_info* cinfo = pinfo->cinfo;
1847   gint offset = *p_offset;
1848   guint8 pa_flags = *p_pa_flags;
1849   guint8 error = *p_error;
1850 
1851   /* 1 byte Level */
1852   if( offset >= size )
1853   {
1854     proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Level: expected 1 byte" );
1855     error = 1;
1856   }
1857   else
1858   {
1859     guint8 c = tvb_get_guint8( tvb, offset );
1860     if( ax != AX_AuthReq || c != 0 )
1861     {
1862       col_append_fstr( cinfo, COL_INFO, " L=%u", c );
1863       if( tree )
1864       {
1865         proto_item_append_text( cemi_node, ", L=%u", c );
1866       }
1867     }
1868     if( tree )
1869     {
1870       proto_tree_add_item( cemi_list, hf_cemi_level, tvb, offset, 1, ENC_BIG_ENDIAN );
1871     }
1872     offset++;
1873   }
1874 
1875   *p_offset = offset;
1876   *p_pa_flags = pa_flags;
1877   *p_error = error;
1878 }
1879 
1880 /* Dissect A_PropertyValue service */
dissect_property_value_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1881 static void dissect_property_value_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
1882   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
1883 {
1884   /* 1 byte Object Index */
1885   dissect_ox( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
1886 
1887   /* 1 byte Property ID */
1888   dissect_pid( tvb, pinfo, cemi_node, cemi_list, p_offset, size, -1, 1, p_error );
1889 
1890   /* 2 bytes Range */
1891   dissect_range( tvb, pinfo, cemi_node, cemi_list, p_offset, size, *p_pa_flags, p_error );
1892 }
1893 
1894 /* Dissect A_PropertyDescription service */
dissect_property_description_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1895 static void dissect_property_description_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
1896   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
1897 {
1898   /* 1 byte Object Index */
1899   dissect_ox( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
1900 
1901   /* 1 byte Property ID */
1902   {
1903     guint8 pa_flags = *p_pa_flags;
1904     guint8 pid = dissect_pid( tvb, pinfo, cemi_node, cemi_list, p_offset, size, -1, pa_flags, p_error );
1905 
1906     /* 1 byte Property Index */
1907     dissect_px( tvb, pinfo, cemi_node, cemi_list, p_offset, size, pa_flags || !pid, p_error );
1908 
1909     if( pa_flags )  /* A_PropertyDescription_Response */
1910     {
1911       /* 1 byte PDT, 2 bytes Max Elements, 1 byte Access Levels */
1912       dissect_prop_descr( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
1913 
1914       /* No further trailing data */
1915       *p_pa_flags = 0;
1916     }
1917   }
1918 }
1919 
1920 /* Dissect A_NetworkParameter or A_GroupPropertyValue service */
dissect_network_parameter_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_error)1921 static void dissect_network_parameter_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
1922   gint* p_offset, gint size, guint8* p_error )
1923 {
1924   /* 2 bytes Object Type */
1925   guint16 ot = dissect_ot( tvb, pinfo, cemi_node, cemi_list, p_offset, size, p_error );
1926 
1927   /* 1 byte Property ID */
1928   dissect_pid( tvb, pinfo, cemi_node, cemi_list, p_offset, size, ot, 1, p_error );
1929 }
1930 
1931 /* Dissect A_IndividualAddressSerialNumber or A_DomainAddressSerialNumber service */
dissect_ia_serial_number_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1932 static void dissect_ia_serial_number_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
1933   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
1934 {
1935   column_info* cinfo = pinfo->cinfo;
1936   gint offset = *p_offset;
1937   guint8 pa_flags = *p_pa_flags;
1938   guint8 error = *p_error;
1939 
1940   proto_item* node;
1941 
1942   /* 6 bytes Serial Nr */
1943   if( offset + 6 > size )
1944   {
1945     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Serial Number" );
1946     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
1947     error = 1;
1948     offset = size;
1949   }
1950   else
1951   {
1952     proto_tree_add_data( cemi_list, tvb, offset, 6, cinfo, cemi_node, "Serial Number", " SN=$", ", SerNr=$" );
1953     offset += 6;
1954   }
1955 
1956   if( pa_flags )
1957   {
1958     if( offset >= size )
1959     {
1960       proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Data: missing" );
1961       error = 1;
1962     }
1963   }
1964 
1965   *p_offset = offset;
1966   *p_pa_flags = pa_flags;
1967   *p_error = error;
1968 }
1969 
1970 /* Dissect A_SystemNetworkParameter service */
dissect_system_network_parameter_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)1971 static void dissect_system_network_parameter_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
1972   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
1973 {
1974   column_info* cinfo = pinfo->cinfo;
1975   gint offset = *p_offset;
1976   guint8 pa_flags = *p_pa_flags;
1977   guint8 error = *p_error;
1978 
1979   proto_item* node;
1980   const gchar* name;
1981   guint16 ot;
1982   guint16 cc;
1983   guint8 c;
1984 
1985   /* 2 bytes Object Type */
1986   if( offset + 1 >= size )
1987   {
1988     ot = 0;
1989     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Object Type" );
1990     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
1991     error = 1;
1992     offset = size;
1993   }
1994   else
1995   {
1996     ot = cc = tvb_get_ntohs( tvb, offset );
1997 
1998     if( cc )
1999     {
2000       col_append_fstr( cinfo, COL_INFO, " OT=%u", cc );
2001       proto_item_append_text( cemi_node, ", OT=%u", cc );
2002     }
2003 
2004     if( cemi_list )
2005     {
2006       node = proto_tree_add_item( cemi_list, hf_cemi_ot, tvb, offset, 2, ENC_BIG_ENDIAN );
2007       name = try_val_to_str( cc, ot_vals );
2008       if( name )
2009       {
2010         proto_item_append_text( node, " = %s", name );
2011       }
2012     }
2013 
2014     offset += 2;
2015   }
2016 
2017   /* 2 bytes Property ID (12 bits) and Reserved (4 bits) */
2018   if( offset + 1 >= size )
2019   {
2020     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Property ID" );
2021     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
2022     error = 1;
2023     offset = size;
2024   }
2025   else
2026   {
2027     /* 12 bits Property ID */
2028     cc = tvb_get_ntohs( tvb, offset );
2029     c = cc & 0x000F;
2030     cc >>= 4;
2031 
2032     col_append_fstr( cinfo, COL_INFO, " P=%u", cc );
2033     proto_item_append_text( cemi_node, ", PID=%u", cc );
2034 
2035     if( cemi_list )
2036     {
2037       node = proto_tree_add_item( cemi_list, hf_cemi_snp_pid, tvb, offset, 2, ENC_BIG_ENDIAN );
2038       name = get_pid_name( ot, cc );
2039       if( name )
2040       {
2041         proto_item_append_text( node, " = %s", name );
2042       }
2043     }
2044 
2045     ++offset;
2046 
2047     /* 4 bits Reserved */
2048     if( c )
2049     {
2050       col_append_fstr( cinfo, COL_INFO, " $%X", c );
2051       proto_item_append_text( cemi_node, ", $%X", c );
2052       node = proto_tree_add_item( cemi_list, hf_cemi_snp_reserved, tvb, offset, 1, ENC_BIG_ENDIAN );
2053       expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
2054       error = 1;
2055     }
2056 
2057     ++offset;
2058   }
2059 
2060   *p_offset = offset;
2061   *p_pa_flags = pa_flags;
2062   *p_error = error;
2063 }
2064 
2065 /* Dissect A_PropertyExtValue service */
dissect_property_ext_value_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)2066 static void dissect_property_ext_value_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
2067   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
2068 {
2069   column_info* cinfo = pinfo->cinfo;
2070   gint offset = *p_offset;
2071   guint8 pa_flags = *p_pa_flags;
2072   guint8 error = *p_error;
2073 
2074   proto_item* node;
2075 
2076   /* 2 bytes OT, 12 bits OI, 12 bits PID */
2077   dissect_pid_ext( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
2078 
2079   /* 3 bytes Range (1 byte Count, 2 bytes Index) */
2080   if( offset + 3 > size )
2081   {
2082     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Range" );
2083     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 3 bytes" );
2084     error = 1;
2085     offset = size;
2086   }
2087   else
2088   {
2089     /* 1 byte Count */
2090     guint8 ne = tvb_get_guint8( tvb, offset );
2091     if( ne != 1 )
2092     {
2093       col_append_fstr( cinfo, COL_INFO, " N=%u", ne );
2094       proto_item_append_text( cemi_node, ", N=%u", ne );
2095     }
2096 
2097     /* 2 bytes Index */
2098     guint16 sx = tvb_get_ntohs( tvb, offset + 1 );
2099     if( sx != 1 )
2100     {
2101       col_append_fstr( cinfo, COL_INFO, " X=%u", sx );
2102       proto_item_append_text( cemi_node, ", X=%u", sx );
2103     }
2104 
2105     if( cemi_list )
2106     {
2107       proto_item *range_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 3, "Range: %u element%s at position %u", ne, (ne == 1) ? "" : "s", sx );
2108       proto_tree *range_list = proto_item_add_subtree( range_node, ett_cemi_range );
2109       proto_tree_add_item( range_list, hf_cemi_ext_ne, tvb, offset, 1, ENC_BIG_ENDIAN );
2110       proto_tree_add_item( range_list, hf_cemi_ext_sx, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
2111     }
2112 
2113     offset += 3;
2114   }
2115 
2116   *p_offset = offset;
2117   *p_pa_flags = pa_flags;
2118   *p_error = error;
2119 }
2120 
2121 /* Dissect A_PropertyExtDescription service */
dissect_property_ext_description_service(tvbuff_t * tvb,packet_info * pinfo,proto_item * cemi_node,proto_tree * cemi_list,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)2122 static void dissect_property_ext_description_service( tvbuff_t* tvb, packet_info* pinfo, proto_item* cemi_node, proto_tree* cemi_list,
2123   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
2124 {
2125   column_info* cinfo = pinfo->cinfo;
2126   gint offset = *p_offset;
2127   guint8 pa_flags = *p_pa_flags;
2128   guint8 error = *p_error;
2129 
2130   proto_item* node;
2131   guint16 cc;
2132   guint8 c;
2133 
2134   /* 2 bytes OT, 12 bits OI, 12 bits PID */
2135   dissect_pid_ext( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
2136 
2137   /* 4 bits Description Type */
2138   if( offset >= size )
2139   {
2140     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Description Type" );
2141     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bits" );
2142     error = 1;
2143   }
2144   else
2145   {
2146     c = tvb_get_guint8( tvb, offset ) >> 4;
2147     col_append_fstr( cinfo, COL_INFO, " D=%u", c );
2148     proto_item_append_text( cemi_node, ", D=%u", c );
2149     proto_tree_add_item( cemi_list, hf_cemi_ext_dt, tvb, offset, 1, ENC_BIG_ENDIAN );
2150   }
2151 
2152   /* 12 bits Property Index */
2153   if( offset + 2 > size )
2154   {
2155     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Property Index" );
2156     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 12 bits" );
2157     error = 1;
2158     offset = size;
2159   }
2160   else
2161   {
2162     cc = tvb_get_ntohs( tvb, offset ) & 0x0FFF;
2163     col_append_fstr( cinfo, COL_INFO, " PX=%u", cc );
2164     proto_item_append_text( cemi_node, ", PX=%u", cc );
2165     proto_tree_add_item( cemi_list, hf_cemi_ext_px, tvb, offset, 2, ENC_BIG_ENDIAN );
2166     offset += 2;
2167   }
2168 
2169   if( pa_flags ) /* AX_PropExtDescrResp */
2170   {
2171     /* 4 bytes DPT (2 bytes DPT Major, 2 bytes DPT Minor) */
2172     if( offset + 4 > size )
2173     {
2174       node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Data Point Type" );
2175       expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bytes" );
2176       error = 1;
2177       offset = size;
2178     }
2179     else
2180     {
2181       guint16 dpt_major = tvb_get_ntohs( tvb, offset );
2182       guint16 dpt_minor = tvb_get_ntohs( tvb, offset + 2 );
2183 
2184       if( cemi_list )
2185       {
2186         proto_item *dpt_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 2, "Data Point Type: %u.%u", dpt_major, dpt_minor );
2187         proto_tree *dpt_list = proto_item_add_subtree( dpt_node, ett_cemi_dpt );
2188         proto_tree_add_item( dpt_list, hf_cemi_dpt_major, tvb, offset, 2, ENC_BIG_ENDIAN );
2189         proto_tree_add_item( dpt_list, hf_cemi_dpt_minor, tvb, offset + 2, 2, ENC_BIG_ENDIAN );
2190       }
2191 
2192       offset += 4;
2193 
2194       if( dpt_major || dpt_minor )
2195       {
2196         col_append_fstr( cinfo, COL_INFO, " DPT=%u.%u", dpt_major, dpt_minor );
2197         proto_item_append_text( cemi_node, ", DPT=%u.%u", dpt_major, dpt_minor );
2198       }
2199     }
2200 
2201     /* 1 byte PDT, 2 bytes Max Elements, 1 byte Access Levels */
2202     dissect_prop_descr( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
2203 
2204     /* No further trailing data */
2205     pa_flags = 0;
2206   }
2207 
2208   *p_offset = offset;
2209   *p_pa_flags = pa_flags;
2210   *p_error = error;
2211 }
2212 
2213 /* Dissect A_DataSecurity service */
dissect_data_security_service(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,guint16 source_addr,proto_item * source_node,guint16 dest_addr,proto_item * dest_node,guint8 unicast,const gchar * name,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)2214 static void dissect_data_security_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
2215   guint16 source_addr, proto_item* source_node, guint16 dest_addr, proto_item* dest_node, guint8 unicast,
2216   const gchar* name, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
2217 {
2218   column_info* cinfo = pinfo->cinfo;
2219   proto_tree* root_tree = tree;
2220   gint offset = *p_offset;
2221   guint8 pa_flags = *p_pa_flags;
2222   guint8 error = *p_error;
2223 
2224   proto_item* node;
2225   proto_tree* list;
2226 
2227   // 1 byte SCF, 6 bytes SeqNr, ...
2228   // and either another SeqNr for sync or Apci+Mac (2+4 bytes) for data.
2229   if( offset + 13 > size )
2230   {
2231     node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? SCF, SeqNr, ..." );
2232     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: min 13 bytes" );
2233     error = 1;
2234     offset = size;
2235   }
2236   else
2237   {
2238     /* 1 byte SCF */
2239     guint8 scf = tvb_get_guint8( tvb, offset );
2240     guint8 is_sync = (scf & 6) == 0x02;
2241     guint8 is_sync_req = is_sync && (scf & 1) == 0;
2242     guint8 is_sync_res = is_sync && !is_sync_req;
2243     guint64 seq_nr;
2244 
2245     name = try_val_to_str( scf, scf_short_vals );
2246     if( !name ) name = "?";
2247     col_append_fstr( cinfo, COL_INFO, " %s", name );
2248     proto_item_append_text( cemi_node, ", %s", name );
2249 
2250     node = proto_tree_add_item( cemi_list, hf_cemi_scf, tvb, offset, 1, ENC_BIG_ENDIAN );
2251     list = proto_item_add_subtree( node, ett_cemi_scf );
2252     proto_tree_add_item( list, hf_cemi_scf_t, tvb, offset, 1, ENC_BIG_ENDIAN );
2253     proto_tree_add_item( list, hf_cemi_scf_sai, tvb, offset, 1, ENC_BIG_ENDIAN );
2254     proto_tree_add_item( list, hf_cemi_scf_sbc, tvb, offset, 1, ENC_BIG_ENDIAN );
2255     proto_tree_add_item( list, hf_cemi_scf_svc, tvb, offset, 1, ENC_BIG_ENDIAN );
2256 
2257     ++offset;
2258 
2259     /*  6 bytes SeqNr */
2260     name = is_sync_req ? "SeqNrLocal" : is_sync_res ? "Challenge" : "SeqNr";
2261     seq_nr = tvb_get_ntoh48( tvb, offset );
2262     proto_tree_add_data( cemi_list, tvb, offset, 6, cinfo, cemi_node, name, NULL, is_sync_res ? NULL : ", SeqNrLocal=$" );
2263     offset += 6;
2264 
2265     if( is_sync )
2266     {
2267       /* 6 bytes SyncReq SerNr or SyncRes SeqNrRemote */
2268       name = is_sync_req ? "SerNr" : "SeqNrRemote";
2269       proto_tree_add_data( cemi_list, tvb, offset, 6, cinfo, cemi_node, name, NULL, is_sync_res ? ", SeqNrRemote=$" : NULL );
2270       offset += 6;
2271 
2272       /* 6 bytes SyncReq Challenge or SyncRes SeqNrLocal */
2273       name = is_sync_req ? "Challenge" : "SeqNrLocal";
2274       if( offset + 6 > size )
2275       {
2276         node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "%s", name );
2277         proto_item_prepend_text( node, "? " );
2278         expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
2279         error = 1;
2280         offset = size;
2281       }
2282       else
2283       {
2284         proto_tree_add_data( cemi_list, tvb, offset, 6, NULL, NULL, name, NULL, NULL );
2285         offset += 6;
2286 
2287         if( offset < size )
2288         {
2289           /* 4 bytes MAC */
2290           node = proto_tree_add_data( cemi_list, tvb, offset, size - offset, NULL, NULL, "Message Authentication Code", NULL, NULL );
2291           if( offset + 4 != size )
2292           {
2293             proto_item_prepend_text( node, "? " );
2294             expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 4 bytes" );
2295             error = 1;
2296           }
2297           offset = size;
2298         }
2299       }
2300     }
2301     else // Data
2302     {
2303       struct data_security_info info;
2304       struct knx_keyring_ia_seqs* ia_seq;
2305       const guint8* cemi;
2306       const guint8* encrypted;
2307       gint encrypted_size;
2308       const guint8* decrypted;
2309       proto_item* item;
2310 
2311       info.source = source_addr;
2312       info.dest = dest_addr;
2313       info.multicast = !unicast;
2314       info.seq_nr = seq_nr;
2315       *info.output_text = '\0';
2316 
2317       if( !unicast )  // multicast or broadcast
2318       {
2319         // Check sending IA
2320         guint8 ga_found = 0;
2321         guint8 ia_ok = 0;
2322         struct knx_keyring_ga_senders* ga_sender = knx_keyring_ga_senders;
2323         for( ; ga_sender; ga_sender = ga_sender->next )
2324         {
2325           if( ga_sender->ga == dest_addr )
2326           {
2327             ga_found = 1;
2328 
2329             if( ga_sender->ia == source_addr )
2330             {
2331               ia_ok = 1;
2332               break;
2333             }
2334           }
2335         }
2336 
2337         if( !ia_ok )
2338         {
2339           if( ga_found )
2340           {
2341             expert_add_info_format( pinfo, source_node, KIP_ERROR, "Unknown sender" );
2342             error = 1;
2343           }
2344           else
2345           {
2346             expert_add_info_format( pinfo, dest_node, KIP_WARNING, "Unknown group address" );
2347           }
2348         }
2349       }
2350 
2351       // Check SeqNr
2352       for( ia_seq = knx_keyring_ia_seqs; ia_seq; ia_seq = ia_seq->next )
2353       {
2354         if( ia_seq->ia == source_addr )
2355         {
2356           if( ia_seq->seq > seq_nr )
2357           {
2358             expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: min $%012" G_GINT64_MODIFIER "X", ia_seq->seq );
2359             break;
2360           }
2361         }
2362       }
2363 
2364       // Get encrypted data.
2365       cemi = tvb_get_ptr( tvb, 0, size );
2366       encrypted = cemi + offset;
2367       encrypted_size = size - offset;
2368 
2369       // Decrypt.
2370       decrypted = decrypt_data_security_data( pinfo->pool, encrypted, encrypted_size, cemi, size, &info );
2371 
2372       if( decrypted )
2373       {
2374         tvbuff_t* tvb2 = tvb_new_child_real_data( tvb, decrypted, encrypted_size, encrypted_size );
2375         gint size2 = encrypted_size - 4;  // > 0, guaranteed by decrypt_data_security_data
2376         proto_item_append_text( cemi_node, ", MAC OK" );
2377         //tvb_set_free_cb(tvb2, wmem_free);
2378         add_new_data_source( pinfo, tvb2, "Decrypted" );
2379 
2380         item = proto_tree_add_none_format( cemi_list, hf_folder, tvb2, 0, encrypted_size, "Decrypted" );
2381         tree = proto_item_add_subtree( item, ett_cemi_decrypted );
2382 
2383         if( *info.output_text )
2384         {
2385           proto_item_append_text( item, " (%s)", info.output_text );
2386         }
2387 
2388         proto_tree_add_data( tree, tvb2, 0, size2, NULL, NULL, "Embedded APDU", NULL, NULL );
2389         proto_tree_add_data( tree, tvb2, size2, 4, NULL, NULL, "Message Authentication Code", NULL, NULL );
2390 
2391         /* Dissect embedded APDU */
2392         {
2393           // Hack: To save us from splitting another sub dissector which only
2394           // dissects the Apci+Apdu
2395           // we synthesize a telegram from the outer ApciSec telegram fields and the inner
2396           // decrypted apci+apdu and then we dissect this as a new cEMI frame.
2397           gint innerTelegramSize = size - 13;     // > 0, already checked above
2398           gint additionalInfoLength = cemi[ 1 ];  // cemi size > 13, already checked above
2399           gint offsetToApci = additionalInfoLength + 9;
2400           if( offsetToApci < size )
2401           {
2402             if( offsetToApci + size2 <= innerTelegramSize )
2403             {
2404               guint8* innerTelegram = (guint8*) wmem_alloc( pinfo->pool, innerTelegramSize );
2405 
2406               memcpy( innerTelegram, cemi, offsetToApci );
2407               memcpy( innerTelegram + offsetToApci, decrypted, size2 );
2408               innerTelegram[ additionalInfoLength + 8 ] = (guint8) (size2 - 1);
2409 
2410               tvbuff_t* tvb3 = tvb_new_child_real_data( tvb, innerTelegram, innerTelegramSize, innerTelegramSize );
2411               //tvb_set_free_cb(tvb3, wmem_free);
2412               add_new_data_source( pinfo, tvb3, "Inner Decrypted Telegram" );
2413 
2414               dissector_handle_t cemi_handle = find_dissector( "cemi" );
2415               if( cemi_handle )
2416               {
2417                 call_dissector( cemi_handle, tvb3, pinfo, root_tree );
2418               }
2419             }
2420           }
2421         }
2422       }
2423       else
2424       {
2425         // Could not be decrypted.
2426         proto_item_append_text( cemi_node, ", Could not be decrypted" );
2427 
2428         if( *info.output_text )
2429         {
2430           proto_item_append_text( cemi_node, " (%s)", info.output_text );
2431         }
2432       }
2433 
2434       offset = size;
2435     }
2436   }
2437 
2438   *p_offset = offset;
2439   *p_pa_flags = pa_flags;
2440   *p_error = error;
2441 }
2442 
2443 /* Dissect extended AL service (10 bit AL service code)
2444 */
dissect_extended_app_service(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,guint16 source_addr,proto_item * source_node,guint16 dest_addr,proto_item * dest_node,guint8 unicast,guint16 ax,const gchar * name,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)2445 static void dissect_extended_app_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
2446   guint16 source_addr, proto_item* source_node, guint16 dest_addr, proto_item* dest_node, guint8 unicast,
2447   guint16 ax, const gchar* name,
2448   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
2449 {
2450   column_info* cinfo = pinfo->cinfo;
2451   gint offset = *p_offset;
2452   guint8 pa_flags = *p_pa_flags;
2453   guint8 error = *p_error;
2454 
2455   proto_item* node = NULL;
2456   proto_tree* list = NULL;
2457 
2458   col_append_fstr( cinfo, COL_INFO, " %s", name );
2459 
2460   if( tree )
2461   {
2462     proto_item_append_text( cemi_node, ", %s", name );
2463     node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 2, "APCI: %s", name );
2464     list = proto_item_add_subtree( node, ett_cemi_apci );
2465     proto_tree_add_item( list, hf_cemi_ax, tvb, offset, 2, ENC_BIG_ENDIAN );
2466   }
2467 
2468   offset += 2;
2469 
2470   pa_flags = PA_RESPONSE | PA_DATA;
2471 
2472   switch( ax )
2473   {
2474   case AX_UserMemRead:
2475   case AX_MemExtRead:
2476   case AX_RoutingTableRead:
2477   case AX_RouterMemRead:
2478   case AX_PropValueRead:
2479   case AX_PropDescrRead:
2480   case AX_IndAddrSerNumRead:
2481   case AX_DomAddrSerNumRead:
2482   case AX_PropExtValueRead:
2483   case AX_PropExtDescrRead:
2484     pa_flags = 0;
2485     break;
2486   }
2487 
2488   switch( ax )
2489   {
2490   case AX_MemExtRead:
2491   case AX_MemExtReadResp:
2492   case AX_MemExtWrite:
2493   case AX_MemExtWriteResp:
2494     dissect_memory_ext_service( tvb, pinfo, cemi_node, cemi_list, ax, &offset, size, &pa_flags, &error );
2495     break;
2496 
2497   case AX_UserMemRead:
2498   case AX_UserMemResp:
2499   case AX_UserMemWrite:
2500   case AX_UserMemBitWrite:
2501     dissect_user_memory_service( tvb, pinfo, tree, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
2502     break;
2503 
2504   case AX_FuncPropCmd:
2505   case AX_FuncPropRead:
2506   case AX_FuncPropResp:
2507     dissect_function_property_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
2508     break;
2509 
2510   case AX_RoutingTableRead:
2511   case AX_RouterMemRead:
2512   case AX_RoutingTableResp:
2513   case AX_RoutingTableWrite:
2514   case AX_RouterMemResp:
2515   case AX_RouterMemWrite:
2516   case AX_MemBitWrite:
2517     dissect_router_service( tvb, pinfo, tree, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
2518     break;
2519 
2520   case AX_AuthReq:
2521   case AX_AuthResp:
2522   case AX_KeyWrite:
2523   case AX_KeyResp:
2524     dissect_authenticate_service( tvb, pinfo, tree, cemi_node, cemi_list, ax, &offset, size, &pa_flags, &error );
2525     break;
2526 
2527   case AX_PropValueRead:
2528   case AX_PropValueResp:
2529   case AX_PropValueWrite:
2530     dissect_property_value_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
2531     break;
2532 
2533   case AX_PropDescrRead:
2534   case AX_PropDescrResp:
2535     dissect_property_description_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
2536     break;
2537 
2538   case AX_NwkParamRead:
2539   case AX_NwkParamResp:
2540   case AX_NwkParamWrite:
2541   case AX_GroupPropValueRead:
2542   case AX_GroupPropValueResp:
2543   case AX_GroupPropValueWrite:
2544   case AX_GroupPropValueInfo:
2545     dissect_network_parameter_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
2546     break;
2547 
2548   case AX_IndAddrSerNumRead:
2549   case AX_DomAddrSerNumRead:
2550   case AX_IndAddrSerNumResp:
2551   case AX_IndAddrSerNumWrite:
2552   case AX_DomAddrSerNumResp:
2553   case AX_DomAddrSerNumWrite:
2554     dissect_ia_serial_number_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
2555     break;
2556 
2557   case AX_SysNwkParamRead:
2558   case AX_SysNwkParamResp:
2559   case AX_SysNwkParamWrite:
2560     dissect_system_network_parameter_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
2561     break;
2562 
2563   case AX_PropExtValueRead:
2564   case AX_PropExtValueResp:
2565   case AX_PropExtValueWriteCon:
2566   case AX_PropExtValueWriteConRes:
2567   case AX_PropExtValueWriteUnCon:
2568     dissect_property_ext_value_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
2569     break;
2570 
2571   case AX_PropExtDescrRead:
2572   case AX_PropExtDescrResp:
2573     dissect_property_ext_description_service( tvb, pinfo, cemi_node, cemi_list, &offset, size, &pa_flags, &error );
2574     break;
2575 
2576   case AX_FuncPropExtCmd:
2577   case AX_FuncPropExtRead:
2578   case AX_FuncPropExtResp:
2579 
2580     /* 2 bytes OT, 12 bits OI, 12 bits PID */
2581     dissect_pid_ext( tvb, pinfo, cemi_node, cemi_list, &offset, size, &error );
2582     break;
2583 
2584   case AX_DataSec:
2585     dissect_data_security_service( tvb, pinfo, tree, cemi_node, cemi_list, source_addr, source_node, dest_addr, dest_node, unicast,
2586       name, &offset, size, &pa_flags, &error );
2587     break;
2588   }
2589 
2590   *p_offset = offset;
2591   *p_pa_flags = pa_flags;
2592   *p_error = error;
2593 }
2594 
2595 /* Dissect simple AL service (4 bit AL service code)
2596 */
dissect_simple_app_service(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,guint8 ac,guint8 ad,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)2597 static void dissect_simple_app_service( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
2598   guint8 ac, guint8 ad, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
2599 {
2600   column_info* cinfo = pinfo->cinfo;
2601   gint offset = *p_offset;
2602   guint8 pa_flags = *p_pa_flags;
2603   guint8 error = *p_error;
2604 
2605   proto_item* node = NULL;
2606   proto_tree* list = NULL;
2607 
2608   guint8 c;
2609   guint16 cc;
2610 
2611   const gchar* name = val_to_str( ac, ac_vals, "AC=%u" );
2612   col_append_fstr( cinfo, COL_INFO, " %s", name );
2613   if( tree )
2614   {
2615     proto_item_append_text( cemi_node, ", %s", name );
2616     node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 2, "APCI: %s", name );
2617     list = proto_item_add_subtree( node, ett_cemi_apci );
2618     proto_tree_add_item( list, hf_cemi_ac, tvb, offset, 2, ENC_BIG_ENDIAN );
2619   }
2620 
2621   offset++;
2622 
2623   switch( ac )
2624   {
2625   case AC_GroupValueRead:
2626   case AC_MemRead:
2627   case AC_AdcRead:
2628   case AC_DevDescrRead:
2629     pa_flags = 0;
2630     break;
2631   }
2632 
2633   switch( ac )
2634   {
2635   case AC_GroupValueRead:
2636   case AC_GroupValueResp:
2637   case AC_GroupValueWrite:
2638   case AC_Restart:
2639     {
2640       guint8 expected = ((pa_flags && offset + 1 >= size) || ac == AC_Restart);
2641 
2642       if( expected || ad != 0 )
2643       {
2644         /* Show APCI 6-bit data
2645         */
2646         if( !expected )
2647         {
2648           error = 1;
2649         }
2650         else if( ad != 0 || ac != AC_Restart || offset + 1 < size )
2651         {
2652           col_append_fstr( cinfo, COL_INFO, " $%02X", ad );
2653           proto_item_append_text( cemi_node, " $%02X", ad );
2654         }
2655 
2656         if( tree )
2657         {
2658           node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Data: %02X", ad );
2659           list = proto_item_add_subtree( node, ett_cemi_apci );
2660           proto_tree_add_item( list, hf_cemi_ad, tvb, offset, 1, ENC_BIG_ENDIAN );
2661 
2662           if( !expected )
2663           {
2664             proto_item_prepend_text( node, "? " );
2665             expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 0x00" );
2666           }
2667         }
2668       }
2669     }
2670     break;
2671 
2672   case AC_MemRead:
2673   case AC_MemResp:
2674   case AC_MemWrite:
2675 
2676     /* 6 bits Memory Length, 2 bytes Memory Address */
2677     if( offset + 3 > size )
2678     {
2679       node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset + 1, size - offset - 1, NULL, "? Memory Address" );
2680       expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
2681       error = 1;
2682       offset = size - 1;
2683     }
2684     else
2685     {
2686       cc = tvb_get_ntohs( tvb, offset + 1 );
2687       if( ad != 1 )
2688         col_append_fstr( cinfo, COL_INFO, " N=%u", ad );
2689       col_append_fstr( cinfo, COL_INFO, " X=$%04X", cc );
2690       if( tree )
2691       {
2692         if( ad != 1 )
2693           proto_item_append_text( cemi_node, ", N=%u", ad );
2694         proto_item_append_text( cemi_node, ", X=$%04X", cc );
2695         node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 3, "Range: %u byte%s at address $%04X", ad, (ad == 1) ? "" : "s", cc );
2696         list = proto_item_add_subtree( node, ett_cemi_range );
2697         proto_tree_add_item( list, hf_cemi_ad_memory_length, tvb, offset, 1, ENC_BIG_ENDIAN );
2698         proto_tree_add_item( list, hf_cemi_memory_address, tvb, offset + 1, 2, ENC_BIG_ENDIAN );
2699       }
2700       offset += 2;
2701     }
2702     break;
2703 
2704   case AC_AdcRead:
2705   case AC_AdcResp:
2706 
2707     /* 6 bits Channel */
2708     col_append_fstr( cinfo, COL_INFO, " #%u", ad );
2709     if( tree )
2710     {
2711       proto_item_append_text( cemi_node, " #%u", ad );
2712       node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Channel: %u", ad );
2713       list = proto_item_add_subtree( node, ett_cemi_apci );
2714       proto_tree_add_item( list, hf_cemi_ad_channel, tvb, offset, 1, ENC_BIG_ENDIAN );
2715     }
2716     ++offset;
2717 
2718     /* 1 byte Count */
2719     if( offset >= size )
2720     {
2721       proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Count: expected 1 byte" );
2722       error = 1;
2723       --offset;
2724     }
2725     else
2726     {
2727       c = tvb_get_guint8( tvb, offset );
2728       if( c != 1 )
2729       {
2730         col_append_fstr( cinfo, COL_INFO, " N=%u", c );
2731         proto_item_append_text( cemi_node, ", N=%u", c );
2732       }
2733       proto_tree_add_item( cemi_list, hf_cemi_adc_count, tvb, offset, 1, ENC_BIG_ENDIAN );
2734     }
2735     break;
2736 
2737   case AC_DevDescrRead:
2738   case AC_DevDescrResp:
2739 
2740     /* 6 bits Descriptor Type */
2741     if( ad != 0 )
2742       col_append_fstr( cinfo, COL_INFO, " #%u", ad );
2743     if( tree )
2744     {
2745       if( ad != 0 )
2746         proto_item_append_text( cemi_node, " #%u", ad );
2747       node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Descriptor Type: %u", ad );
2748       list = proto_item_add_subtree( node, ett_cemi_apci );
2749       proto_tree_add_item( list, hf_cemi_ad_type, tvb, offset, 1, ENC_BIG_ENDIAN );
2750     }
2751     break;
2752 
2753   case AC_UserMsg:
2754   case AC_Escape:
2755 
2756     /* 6 bits Data */
2757     col_append_fstr( cinfo, COL_INFO, " #%u", ad );
2758     if( tree )
2759     {
2760       proto_item_append_text( cemi_node, " #%u", ad );
2761       proto_item_append_text( node, " $%02X", ad );
2762       proto_tree_add_item( list, hf_cemi_ad, tvb, offset, 1, ENC_BIG_ENDIAN );
2763     }
2764     break;
2765   }
2766 
2767   offset++;
2768 
2769   *p_offset = offset;
2770   *p_pa_flags = pa_flags;
2771   *p_error = error;
2772 }
2773 
2774 /* Dissect cEMI Application Layer
2775 */
dissect_cemi_app_layer(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,guint16 source_addr,proto_item * source_node,guint16 dest_addr,proto_item * dest_node,guint8 unicast,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)2776 static void dissect_cemi_app_layer( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
2777   guint16 source_addr, proto_item* source_node, guint16 dest_addr, proto_item* dest_node, guint8 unicast,
2778   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
2779 {
2780   gint offset = *p_offset;
2781   guint8 pa_flags = *p_pa_flags;
2782   guint8 error = *p_error;
2783 
2784   /* 10 bits APCI
2785   */
2786   if( offset + 1 >= size )
2787   {
2788     proto_item* node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? APCI" );
2789     expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
2790     error = 1;
2791     offset = size;
2792   }
2793   else
2794   {
2795     /* Extract and split AL service code */
2796     guint8 tb = tvb_get_guint8( tvb, offset );
2797     guint8 ab = tvb_get_guint8( tvb, offset + 1 );
2798 
2799     /* 4 bits simple AL service code */
2800     guint8 ac = ((tb & 0x03) << 2) | ((ab & 0xC0) >> 6);
2801 
2802     /* 6 bits data */
2803     guint8 ad = ab & 0x3F;
2804 
2805     /* 10 = 4 + 6 bits extended AL service code */
2806     guint16 ax = (ac << 6) | ad;
2807 
2808     const gchar* name = try_val_to_str( ax, ax_vals );
2809 
2810     if( name )  /* Extended AL code (10 bits) */
2811     {
2812       dissect_extended_app_service( tvb, pinfo, tree, cemi_node, cemi_list, source_addr, source_node, dest_addr, dest_node, unicast,
2813         ax, name, &offset, size, &pa_flags, &error );
2814     }
2815     else  /* Simple AL code (4 bits) followed by data (6 bits) */
2816     {
2817       dissect_simple_app_service( tvb, pinfo, tree, cemi_node, cemi_list, ac, ad, &offset, size, &pa_flags, &error );
2818     }
2819   }
2820 
2821   *p_offset = offset;
2822   *p_pa_flags = pa_flags;
2823   *p_error = error;
2824 }
2825 
2826 /* Dissect cEMI Transport Layer
2827 */
dissect_cemi_transport_layer(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,guint8 is_tdata,guint16 source_addr,proto_item * source_node,guint16 dest_addr,proto_item * dest_node,guint8 unicast,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)2828 static void dissect_cemi_transport_layer( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list,
2829   guint8 is_tdata, guint16 source_addr, proto_item* source_node, guint16 dest_addr, proto_item* dest_node, guint8 unicast,
2830   gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
2831 {
2832   column_info* cinfo = pinfo->cinfo;
2833   gint offset = *p_offset;
2834   guint8 pa_flags = *p_pa_flags;
2835   guint8 error = *p_error;
2836 
2837   proto_item* node;
2838   const gchar* name;
2839   gchar text[ 128 ];
2840   guint8 c;
2841 
2842   /* 6 bits TPCI */
2843   if( offset >= size )
2844   {
2845     proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? TPCI: expected 1 byte" );
2846     error = 1;
2847   }
2848   else
2849   {
2850     guint8 tb = tvb_get_guint8( tvb, offset );
2851     proto_item *tpci_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "TPCI" );
2852     proto_tree *tpci_list = proto_item_add_subtree( tpci_node, ett_cemi_tpci );
2853     guint8 tpci_error = 0;
2854 
2855     node = proto_tree_add_item( tpci_list, hf_cemi_tpt, tvb, offset, 1, ENC_BIG_ENDIAN );
2856     if( is_tdata && (tb & 0x80) )
2857     {
2858       proto_item_prepend_text( node, "? " );
2859       expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
2860       tpci_error = 1;
2861     }
2862 
2863     node = proto_tree_add_item( tpci_list, hf_cemi_tst, tvb, offset, 1, ENC_BIG_ENDIAN );
2864     if( is_tdata && (tb & 0x40) )
2865     {
2866       proto_item_prepend_text( node, "? " );
2867       expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
2868       tpci_error = 1;
2869     }
2870 
2871     c = (tb & 0x3C) >> 2;
2872 
2873     if( c || tb & 0x40 )  /* Numbered Packet? */
2874     {
2875       node = proto_tree_add_item( tpci_list, hf_cemi_num, tvb, offset, 1, ENC_BIG_ENDIAN );
2876       proto_item_append_text( tpci_node, ", SeqNum = %u", c );
2877       if( !(tb & 0x40) )
2878       {
2879         expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
2880         tpci_error = 1;
2881       }
2882     }
2883 
2884     if( tb & 0x80 )  /* Control Packet */
2885     {
2886       /* 2 bits TPCI Code */
2887       guint8 tc = tb & 0x03;
2888       name = try_val_to_str( tc, tc_vals );
2889       if( !name )
2890       {
2891         g_snprintf( text, sizeof text, "TC=%u", tc );
2892         name = text;
2893       }
2894       col_append_fstr( cinfo, COL_INFO, " %s", name );
2895       if( tree )
2896       {
2897         proto_item_append_text( cemi_node, ", %s", name );
2898         proto_item_append_text( tpci_node, ": %s", name );
2899         proto_tree_add_item( tpci_list, hf_cemi_tc, tvb, offset, 1, ENC_BIG_ENDIAN );
2900       }
2901     }
2902 
2903     if( tpci_error )
2904     {
2905       proto_item_prepend_text( tpci_node, "? " );
2906       error = 1;
2907     }
2908 
2909     if( tb & 0x80 )  /* Control Packet */
2910     {
2911       pa_flags = 0;
2912       offset++;
2913     }
2914     else  /* Data Packet */
2915     {
2916       /* APCI etc */
2917       dissect_cemi_app_layer( tvb, pinfo, tree, cemi_node, cemi_list, source_addr, source_node, dest_addr, dest_node, unicast, &offset, size, &pa_flags, &error );
2918     }
2919   }
2920 
2921   *p_offset = offset;
2922   *p_pa_flags = pa_flags;
2923   *p_error = error;
2924 }
2925 
2926 /* Dissect cEMI Link Layer
2927   (typically L_Data or T_Data)
2928 */
dissect_cemi_link_layer(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,proto_item * cemi_node,proto_tree * cemi_list,guint8 mc,gint * p_offset,gint size,guint8 * p_pa_flags,guint8 * p_error)2929 static void dissect_cemi_link_layer( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, proto_item* cemi_node, proto_tree* cemi_list, guint8 mc, gint* p_offset, gint size, guint8* p_pa_flags, guint8* p_error )
2930 {
2931   column_info* cinfo = pinfo->cinfo;
2932   gint offset = *p_offset;
2933   guint8 pa_flags = *p_pa_flags;
2934   guint8 error = *p_error;
2935 
2936   proto_item* node = NULL;
2937   proto_tree* list = NULL;
2938 
2939   const gchar* name;
2940   gchar text[ 128 ];
2941   guint8 c;
2942 
2943   guint8 is_tdata = 0;
2944   guint8 is_ldata = 0;
2945   guint16 source_addr = 0;
2946   guint16 dest_addr = 0;
2947   guint8 unicast = 0;
2948 
2949   proto_item* source_node = NULL;
2950   proto_item* dest_node = NULL;
2951 
2952   proto_item* ai_node;
2953   proto_tree* ai_list;
2954 
2955   if( size < 2 )
2956   {
2957     ai_list = proto_tree_add_subtree( cemi_list, tvb, offset, 0, ett_cemi_ai, &ai_node, "? Additional Info" );
2958     proto_tree_add_expert_format( ai_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Length: expected 1 byte" );
2959     offset = size;
2960     error = 1;
2961   }
2962   else
2963   {
2964     /* Additional Information */
2965     guint8 ai_len = tvb_get_guint8( tvb, 1 );
2966     gint ai_end = 2 + ai_len;
2967     gint ai_size = ai_len;
2968 
2969     if( ai_end > size )
2970     {
2971       error = 2;
2972       ai_size = size - 2;
2973       ai_end = size;
2974     }
2975 
2976     ai_node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, 1, ai_size + 1, "Additional Info (%u bytes)", ai_len );
2977     ai_list = proto_item_add_subtree( ai_node, ett_cemi_ai );
2978     node = proto_tree_add_item( ai_list, hf_cemi_ai_length, tvb, 1, 1, ENC_BIG_ENDIAN );
2979 
2980     if( error == 2 )
2981     {
2982       proto_item_prepend_text( node, "? " );
2983       expert_add_info_format( pinfo, node, KIP_ERROR, "Available: %d bytes", ai_size );
2984     }
2985 
2986     offset = 2;
2987     while( offset < ai_end )
2988     {
2989       /* Additional Information Element */
2990       guint8 aie_type = tvb_get_guint8( tvb, offset );
2991       guint8 aie_len;
2992       gint aie_size;
2993       proto_item *aie_node;
2994       proto_tree *aie_list;
2995 
2996       name = try_val_to_str( aie_type, aiet_vals );
2997 
2998       if( offset + 1 >= ai_end )
2999       {
3000         error = 3;
3001         aie_len = 0;
3002         aie_size = 1;
3003       }
3004       else
3005       {
3006         aie_len = tvb_get_guint8( tvb, offset + 1 );
3007         aie_size = ai_end - offset - 2;
3008         if( aie_size < aie_len )
3009         {
3010           error = 4;
3011         }
3012         else
3013         {
3014           aie_size = aie_len;
3015         }
3016         aie_size += 2;
3017       }
3018 
3019       aie_node = proto_tree_add_none_format( ai_list, hf_folder, tvb, offset, aie_size, "Additional Info: %s", name ? name : "?" );
3020       aie_list = proto_item_add_subtree( aie_node, ett_cemi_aie );
3021       node = proto_tree_add_item( aie_list, hf_cemi_aie_type, tvb, offset, 1, ENC_BIG_ENDIAN );
3022       if( name ) proto_item_append_text( node, " = %s", name );
3023       offset++;
3024 
3025       if( error == 3 )
3026       {
3027         proto_item_prepend_text( aie_node, "? " );
3028         proto_tree_add_expert_format( aie_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Length: expected 1 byte" );
3029         break;
3030       }
3031 
3032       proto_item_append_text( aie_node, " (%u bytes)", aie_len );
3033       node = proto_tree_add_item( aie_list, hf_cemi_aie_length, tvb, offset, 1, ENC_BIG_ENDIAN );
3034       offset++;
3035 
3036       if( error == 4 )
3037       {
3038         proto_item_prepend_text( aie_node, "? " );
3039         proto_item_prepend_text( node, "? " );
3040         expert_add_info_format( pinfo, node, KIP_ERROR, "Available: %d bytes", aie_size - 2 );
3041         break;
3042       }
3043 
3044       if( aie_len > 0 )
3045       {
3046         proto_tree_add_data( aie_list, tvb, offset, aie_len, NULL, NULL, "Data", NULL, NULL );
3047         offset += aie_len;
3048       }
3049       else
3050       {
3051         proto_item_prepend_text( aie_node, "? " );
3052         proto_item_append_text( node, " (?)" );
3053         expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: >= 1 byte(s)" );
3054         error = 5;
3055       }
3056     }
3057 
3058     if( error >= 2 )
3059     {
3060       proto_item_prepend_text( ai_node, "? " );
3061     }
3062 
3063     offset = ai_end;
3064   }
3065 
3066   switch( mc )
3067   {
3068   case CEMI_L_BUSMON_IND:
3069   case CEMI_L_RAW_IND:
3070   case CEMI_L_RAW_REQ:
3071   case CEMI_L_RAW_CON:
3072     break;
3073 
3074   default:
3075 
3076     switch( mc )
3077     {
3078     case CEMI_L_DATA_REQ:
3079     case CEMI_L_DATA_CON:
3080     case CEMI_L_DATA_IND:
3081       is_ldata = 1;
3082       break;
3083 
3084     case CEMI_T_DATA_INDIVIDUAL_REQ:
3085     case CEMI_T_DATA_INDIVIDUAL_IND:
3086     case CEMI_T_DATA_CONNECTED_REQ:
3087     case CEMI_T_DATA_CONNECTED_IND:
3088       is_tdata = 1;
3089       break;
3090     }
3091 
3092     if( is_tdata )
3093     {
3094       gint length = (size >= offset + 6) ? 6 : size - offset;
3095       node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, length, NULL, "Reserved" );
3096       if( length < 6 )
3097       {
3098         proto_item_prepend_text( node, "? " );
3099         expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 6 bytes" );
3100         error = 1;
3101       }
3102       else
3103       {
3104         gint pos = 0;
3105         for( ; pos < 6; pos++ )
3106         {
3107           if( tvb_get_guint8( tvb, offset + pos ) != 0 )
3108           {
3109             proto_item_prepend_text( node, "? " );
3110             expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: zero" );
3111             error = 1;
3112             break;
3113           }
3114         }
3115       }
3116 
3117       is_tdata = 1;
3118       offset += length;
3119     }
3120     else
3121     {
3122       /* 1 byte Control Field 1 */
3123       if( offset >= size )
3124       {
3125         proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Ctrl1: expected 1 byte" );
3126         error = 1;
3127       }
3128       else
3129       {
3130         if( tree )
3131         {
3132           c = tvb_get_guint8( tvb, offset );
3133           proto_item_append_text( cemi_node, ", " );
3134           node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Ctrl1: " );
3135           if( !(c & 0x80) )
3136           {
3137             proto_item_append_text( cemi_node, "X " );
3138             proto_item_append_text( node, "Extended, " );
3139           }
3140           if( !(c & 0x20) )
3141           {
3142             proto_item_append_text( cemi_node, "R " );
3143             proto_item_append_text( node, "Repeat On Error, " );
3144           }
3145           if( !(c & 0x10) )
3146           {
3147             proto_item_append_text( cemi_node, "B " );
3148             proto_item_append_text( node, "System Broadcast, " );
3149           }
3150           if( c & 0x02 )
3151           {
3152             proto_item_append_text( cemi_node, "A " );
3153             proto_item_append_text( node, "Ack Wanted, " );
3154           }
3155           if( c & 0x01 )
3156           {
3157             proto_item_append_text( cemi_node, "C " );
3158             proto_item_append_text( node, "Unconfirmed, " );
3159           }
3160 
3161           name = try_val_to_str( (c & 0x0C) >> 2, prio_vals );
3162           if( !name )
3163             name = "?";
3164           proto_item_append_text( cemi_node, "P=%s", name );
3165           proto_item_append_text( node, "Prio = %s", name );
3166           list = proto_item_add_subtree( node, ett_cemi_ctrl1 );
3167           proto_tree_add_item( list, hf_cemi_ft, tvb, offset, 1, ENC_BIG_ENDIAN );
3168           proto_tree_add_item( list, hf_cemi_rep, tvb, offset, 1, ENC_BIG_ENDIAN );
3169           proto_tree_add_item( list, hf_cemi_bt, tvb, offset, 1, ENC_BIG_ENDIAN );
3170           proto_tree_add_item( list, hf_cemi_prio, tvb, offset, 1, ENC_BIG_ENDIAN );
3171           proto_tree_add_item( list, hf_cemi_ack, tvb, offset, 1, ENC_BIG_ENDIAN );
3172           proto_tree_add_item( list, hf_cemi_ce, tvb, offset, 1, ENC_BIG_ENDIAN );
3173         }
3174 
3175         offset++;
3176       }
3177 
3178       /* 1 byte Control Field 2 */
3179       if( offset >= size )
3180       {
3181         proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Ctrl2: expected 1 byte" );
3182         error = 1;
3183       }
3184       else
3185       {
3186         c = tvb_get_guint8( tvb, offset );
3187 
3188         unicast = !(c & 0x80);  /* Address Type (IA or GA) */
3189 
3190         if( tree )
3191         {
3192           guint8 hc = (c & 0x70) >> 4;  /* Hop Count */
3193           guint8 eff = c & 0x0F;  /* Extended Frame Format (0 = standard) */
3194 
3195           g_snprintf( text, sizeof text, "%u", (c & 0x70) >> 4 );   /* hop count */
3196           proto_item_append_text( cemi_node, ", H=%u", hc );
3197           node = proto_tree_add_none_format( cemi_list, hf_folder, tvb, offset, 1, "Ctrl2: Hops = %u", hc );
3198           if( eff )
3199           {
3200             proto_item_append_text( cemi_node, " F=%u", eff );
3201             proto_item_append_text( cemi_node, " Frame = %u", eff );
3202           }
3203           list = proto_item_add_subtree( node, ett_cemi_ctrl2 );
3204           proto_tree_add_item( list, hf_cemi_at, tvb, offset, 1, ENC_BIG_ENDIAN );
3205           proto_tree_add_item( list, hf_cemi_hc, tvb, offset, 1, ENC_BIG_ENDIAN );
3206           proto_tree_add_item( list, hf_cemi_eff, tvb, offset, 1, ENC_BIG_ENDIAN );
3207         }
3208 
3209         offset++;
3210       }
3211 
3212       /* 2 bytes Source Address */
3213       if( offset + 1 >= size )
3214       {
3215         node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Source" );
3216         expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
3217         error = 1;
3218         offset = size;
3219       }
3220       else
3221       {
3222         source_addr = tvb_get_ntohs( tvb, offset );
3223         g_snprintf( text, sizeof text, "%u.%u.%u", (source_addr >> 12) & 0xF, (source_addr >> 8) & 0xF, source_addr & 0xFF );
3224         col_append_fstr( cinfo, COL_INFO, " %s", text );
3225         if( tree )
3226         {
3227           proto_item_append_text( cemi_node, ", Src=%s", text );
3228           source_node = proto_tree_add_item( cemi_list, hf_cemi_sa, tvb, offset, 2, ENC_BIG_ENDIAN );
3229           proto_item_append_text( source_node, " = %s", text );
3230         }
3231 
3232         offset += 2;
3233       }
3234 
3235       /* 2 bytes Destination Address */
3236       if( offset + 1 >= size )
3237       {
3238         node = proto_tree_add_bytes_format( cemi_list, hf_bytes, tvb, offset, size - offset, NULL, "? Destination" );
3239         expert_add_info_format( pinfo, node, KIP_ERROR, "Expected: 2 bytes" );
3240         error = 1;
3241         offset = size;
3242       }
3243       else
3244       {
3245         dest_addr = tvb_get_ntohs( tvb, offset );
3246 
3247         if( unicast )
3248         {
3249           /* Individual Address */
3250           g_snprintf( text, sizeof text, "%u.%u.%u", (dest_addr >> 12) & 0xF, (dest_addr >> 8) & 0xF, dest_addr & 0xFF );
3251         }
3252         else
3253         {
3254           /* Group Address */
3255           g_snprintf( text, sizeof text, "%u/%u/%u", (dest_addr >> 11) & 0x1F, (dest_addr >> 8) & 0x7, dest_addr & 0xFF );
3256         }
3257 
3258         col_append_fstr( cinfo, COL_INFO, "->%s", text );
3259 
3260         if( tree )
3261         {
3262           proto_item_append_text( cemi_node, ", Dst=%s", text );
3263           dest_node = proto_tree_add_item( cemi_list, hf_cemi_da, tvb, offset, 2, ENC_BIG_ENDIAN );
3264           proto_item_append_text( dest_node, " = %s", text );
3265         }
3266 
3267         offset += 2;
3268       }
3269     }
3270 
3271     if( is_ldata || is_tdata )
3272     {
3273       /* 1 byte NPDU Length */
3274       if( offset >= size )
3275       {
3276         proto_tree_add_expert_format( cemi_list, pinfo, KIP_ERROR, tvb, offset, 0, "? Length: expected 1 byte" );
3277         error = 1;
3278       }
3279       else
3280       {
3281         guint8 data_len = tvb_get_guint8( tvb, offset );
3282         node = proto_tree_add_item( cemi_list, hf_cemi_len, tvb, offset, 1, ENC_BIG_ENDIAN );
3283 
3284         if( offset + 2 + data_len != size )
3285         {
3286           proto_item_prepend_text( node, "? " );
3287           expert_add_info_format( pinfo, node, KIP_ERROR, "Available: %d bytes", size - offset - 2 );
3288           error = 1;
3289         }
3290 
3291         offset++;
3292       }
3293 
3294       /* TPCI etc */
3295       dissect_cemi_transport_layer( tvb, pinfo, tree, cemi_node, cemi_list, is_tdata, source_addr, source_node, dest_addr, dest_node, unicast, &offset, size, &pa_flags, &error );
3296     }
3297 
3298     break;
3299   }
3300 
3301   *p_offset = offset;
3302   *p_pa_flags = pa_flags;
3303   *p_error = error;
3304 }
3305 
dissect_cemi(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)3306 static gint dissect_cemi( tvbuff_t* tvb, packet_info* pinfo, proto_tree* tree, void* data _U_ )
3307 {
3308   gint offset = 0;
3309   gint size = tvb_captured_length_remaining( tvb, 0 );
3310   guint8 error = 0;
3311   column_info* cinfo = pinfo->cinfo;
3312 
3313   /* cEMI node in tree view */
3314   proto_item* cemi_node = proto_tree_add_item( tree, proto_cemi, tvb, 0, size, ENC_BIG_ENDIAN );
3315 
3316   /* Subnodes of cEMI node */
3317   proto_tree* cemi_list = proto_item_add_subtree( cemi_node, ett_cemi );
3318 
3319   guint8 pa_flags = PA_DATA;
3320 
3321   /* Only add cEMI information to the info column (not replacing it).
3322     This means that we do not have to clear that column here, but
3323     are adding a seperator here.
3324   */
3325   col_append_str( cinfo, COL_INFO, " " );
3326 
3327   /* Replace long name "Common External Message Interface" by short name "cEMI" */
3328   proto_item_set_text( cemi_node, "cEMI" );
3329 
3330   if( size <= 0 )
3331   {
3332     expert_add_info_format( pinfo, cemi_node, KIP_ERROR, "Expected: min 1 byte" );
3333     error = 1;
3334   }
3335   else
3336   {
3337     /* 1 byte cEMI Message Code */
3338     guint8 mc = tvb_get_guint8( tvb, 0 );
3339     const gchar* name = try_val_to_str( mc, mc_vals );
3340 
3341     if( !name )
3342     {
3343       /* Unknown Message Code */
3344       col_append_str( cinfo, COL_INFO, "cEMI" );
3345       pa_flags = 0;
3346     }
3347     else
3348     {
3349       /* Add cEMI message code to info column */
3350       col_append_str( cinfo, COL_INFO, name );
3351 
3352       /* Show MC in cEMI node, and more detailed in a subnode */
3353       proto_item_append_text( cemi_node, " %s", name );
3354       proto_tree_add_item( cemi_list, hf_cemi_mc, tvb, 0, 1, ENC_BIG_ENDIAN );
3355 
3356       offset = 1;
3357 
3358       if( mc >= 0xF0 )
3359       {
3360         /* cEMI Management packet */
3361         dissect_cemi_mgmt_packet( tvb, pinfo, cemi_node, cemi_list, mc, &offset, size, &pa_flags, &error );
3362       }
3363       else
3364       {
3365         /* cEMI Link Layer packet */
3366         dissect_cemi_link_layer( tvb, pinfo, tree, cemi_node, cemi_list, mc, &offset, size, &pa_flags, &error );
3367       }
3368     }
3369   }
3370 
3371   if( offset < size )
3372   {
3373     /* Trailing data */
3374     proto_item* node = proto_tree_add_data( cemi_list, tvb, offset, size - offset, cinfo, cemi_node, "Data", " $", ", $" );
3375 
3376     if( !pa_flags )
3377     {
3378       proto_item_prepend_text( node, "? " );
3379       expert_add_info_format( pinfo, node, KIP_ERROR, "Unexpected" );
3380       error = 1;
3381     }
3382 
3383     offset = size;
3384   }
3385 
3386   if( error )
3387   {
3388     /* If not already done */
3389     if( !knxip_error )
3390     {
3391       knxip_error = 1;
3392       col_prepend_fstr( cinfo, COL_INFO, "? " );
3393     }
3394 
3395     proto_item_prepend_text( cemi_node, "? " );
3396   }
3397 
3398   return size;
3399 }
3400 
proto_register_cemi(void)3401 void proto_register_cemi( void )
3402 {
3403   /* Header fields */
3404   static hf_register_info hf[] = {
3405     { &hf_bytes, { "Data", "cemi.data", FT_BYTES, BASE_NONE, NULL, 0, NULL, HFILL } },
3406     { &hf_folder, { "Folder", "cemi.folder", FT_NONE, BASE_NONE, NULL, 0, NULL, HFILL } },
3407     { &hf_cemi_mc, { "Message Code", "cemi.mc", FT_UINT8, BASE_HEX, VALS( mc_vals ), 0, NULL, HFILL } },
3408     { &hf_cemi_error, { "Error", "cemi.e", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
3409     { &hf_cemi_ai_length, { "Additional Information Length", "cemi.ai.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3410     { &hf_cemi_aie_type, { "Additional Information Element Type", "cemi.ait.n", FT_UINT8, BASE_HEX, NULL, 0, NULL, HFILL } },
3411     { &hf_cemi_aie_length, { "Additional Information Element Length", "cemi.aie.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3412     { &hf_cemi_ot, { "Object Type", "cemi.ot", FT_UINT16, BASE_DEC, VALS( ot_vals ), 0, NULL, HFILL } },
3413     { &hf_cemi_oi, { "Object Instance", "cemi.oi", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3414     { &hf_cemi_ox, { "Object Index", "cemi.ox", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3415     { &hf_cemi_px, { "Property Index", "cemi.px",FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3416     { &hf_cemi_pid, { "Property ID", "cemi.pid", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3417     { &hf_cemi_ne, { "Count", "cemi.n", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL } },
3418     { &hf_cemi_sx, { "Index", "cemi.x", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL } },
3419     { &hf_cemi_ft, { "Frame Type", "cemi.ft", FT_UINT8, BASE_DEC, VALS( ft_vals ), 0x80, NULL, HFILL } },
3420     { &hf_cemi_rep, { "Repeat On Error", "cemi.rep", FT_UINT8, BASE_DEC, VALS( rep_vals ), 0x20, NULL, HFILL } },
3421     { &hf_cemi_bt, { "Broadcast Type", "cemi.bt", FT_UINT8, BASE_DEC, VALS( bt_vals ), 0x10, NULL, HFILL } },
3422     { &hf_cemi_prio, { "Priority", "cemi.prio", FT_UINT8, BASE_DEC, VALS( prio_vals ), 0x0C, NULL, HFILL } },
3423     { &hf_cemi_ack, { "Ack Wanted", "cemi.ack", FT_UINT8, BASE_DEC, VALS( ack_vals ), 0x02, NULL, HFILL } },
3424     { &hf_cemi_ce, { "Confirmation Error", "cemi.ce", FT_UINT8, BASE_DEC, VALS( ce_vals ), 0x01, NULL, HFILL } },
3425     { &hf_cemi_at, { "Address Type", "cemi.at", FT_UINT8, BASE_DEC, VALS( at_vals ), 0x80, NULL, HFILL } },
3426     { &hf_cemi_hc, { "Hop Count", "cemi.hc", FT_UINT8, BASE_DEC, NULL, 0x70, NULL, HFILL } },
3427     { &hf_cemi_eff, { "Extended Frame Format", "cemi.eff", FT_UINT8, BASE_HEX, NULL, 0x0F, NULL, HFILL } },
3428     { &hf_cemi_sa, { "Source", "cemi.sa", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
3429     { &hf_cemi_da, { "Destination", "cemi.da", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
3430     { &hf_cemi_len, { "Length", "cemi.len", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3431     { &hf_cemi_tpt, { "Packet Type", "cemi.tpt", FT_UINT8, BASE_DEC, VALS( pt_vals ), 0x80, NULL, HFILL } },
3432     { &hf_cemi_tst, { "Sequence Type", "cemi.st", FT_UINT8, BASE_DEC, VALS( st_vals ), 0x40, NULL, HFILL } },
3433     { &hf_cemi_num, { "Sequence Number", "cemi.num", FT_UINT8, BASE_DEC, NULL, 0x3C, NULL, HFILL } },
3434     { &hf_cemi_tc, { "Service", "cemi.tc", FT_UINT8, BASE_HEX, VALS( tc_vals ), 0x03, NULL, HFILL } },
3435     { &hf_cemi_ac, { "Service", "cemi.ac", FT_UINT16, BASE_HEX, VALS( ac_vals ), 0x03C0, NULL, HFILL } },
3436     { &hf_cemi_ad, { "Data", "cemi.ad", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
3437     { &hf_cemi_ad_memory_length, { "Memory Length", "cemi.ad.ml", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
3438     { &hf_cemi_ad_channel, { "Channel", "cemi.ad.ch", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
3439     { &hf_cemi_ad_type, { "Data", "cemi.ad.type", FT_UINT8, BASE_HEX, NULL, 0x3F, NULL, HFILL } },
3440     { &hf_cemi_ax, { "Service", "cemi.ax", FT_UINT16, BASE_HEX, VALS( ax_vals ), 0x03FF, NULL, HFILL } },
3441     { &hf_cemi_pw, { "Writable", "cemi.pw", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
3442     { &hf_cemi_pdt, { "Property Data Type", "cemi.pdt", FT_UINT8, BASE_HEX, VALS( pdt_vals ), 0x3F, NULL, HFILL } },
3443     { &hf_cemi_me, { "Max Elements", "cemi.me", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL } },
3444     { &hf_cemi_ra, { "Read Access", "cemi.ra", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL } },
3445     { &hf_cemi_wa, { "Write Access", "cemi.wa", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL } },
3446     { &hf_cemi_ext_oi, { "Object Instance", "cemi.oi", FT_UINT16, BASE_DEC, NULL, 0xFFF0, NULL, HFILL } },
3447     { &hf_cemi_ext_pid, { "Property ID", "cemi.pid", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL } },
3448     { &hf_cemi_ext_ne, { "Count", "cemi.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3449     { &hf_cemi_ext_sx, { "Index", "cemi.x", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
3450     { &hf_cemi_ext_dt, { "Description Type", "cemi.dt", FT_UINT8, BASE_DEC, NULL, 0xF0, NULL, HFILL } },
3451     { &hf_cemi_ext_px, { "Property Index", "cemi.px", FT_UINT16, BASE_DEC, NULL, 0x0FFF, NULL, HFILL } },
3452     { &hf_cemi_ext_memory_length, { "Memory Length", "cemi.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3453     { &hf_cemi_ext_memory_address, { "Memory Address", "cemi.x", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
3454     { &hf_cemi_memory_length, { "Memory Length", "cemi.n", FT_UINT8, BASE_DEC, NULL, 0x0F, NULL, HFILL } },
3455     { &hf_cemi_memory_address, { "Memory Address", "cemi.x", FT_UINT16, BASE_HEX, NULL, 0, NULL, HFILL } },
3456     { &hf_cemi_memory_address_ext, { "Memory Address Extension", "cemi.xx", FT_UINT8, BASE_HEX, NULL, 0xF0, NULL, HFILL } },
3457     { &hf_cemi_level, { "Level", "cemi.level", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3458     { &hf_cemi_snp_pid, { "Property ID", "cemi.pid", FT_UINT16, BASE_DEC, NULL, 0xFFF0, NULL, HFILL } },
3459     { &hf_cemi_snp_reserved, { "Reserved", "cemi.reserved", FT_UINT16, BASE_DEC, NULL, 0x0F, NULL, HFILL } },
3460     { &hf_cemi_dpt_major, { "Data Point Type Major", "cemi.pdt.major", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
3461     { &hf_cemi_dpt_minor, { "Data Point Type Minor", "cemi.pdt.minor", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL } },
3462     { &hf_cemi_scf, { "Security Control Field", "cemi.scf", FT_UINT8, BASE_HEX, VALS( scf_vals ), 0, NULL, HFILL } },
3463     { &hf_cemi_scf_t, { "Tool Access", "cemi.scf.t", FT_UINT8, BASE_DEC, NULL, 0x80, NULL, HFILL } },
3464     { &hf_cemi_scf_sai, { "Security Algorithm Identifier", "cemi.scf.sai", FT_UINT8, BASE_HEX, VALS( scf_sai_vals ), 0x70, NULL, HFILL } },
3465     { &hf_cemi_scf_sbc, { "System Broadcast", "cemi.scf.sbc", FT_UINT8, BASE_DEC, NULL, 0x08, NULL, HFILL } },
3466     { &hf_cemi_scf_svc, { "Service", "cemi.scf.sai", FT_UINT8, BASE_HEX, VALS( scf_svc_vals ), 0x07, NULL, HFILL } },
3467     { &hf_cemi_adc_count, { "Count", "cemi.adc.n", FT_UINT8, BASE_DEC, NULL, 0, NULL, HFILL } },
3468   };
3469 
3470   /* Subtrees */
3471   static gint *ett[] = {
3472     &ett_cemi,
3473     &ett_cemi_ai,
3474     &ett_cemi_aie,
3475     &ett_cemi_ctrl1,
3476     &ett_cemi_ctrl2,
3477     &ett_cemi_tpci,
3478     &ett_cemi_apci,
3479     &ett_cemi_range,
3480     &ett_cemi_pd,
3481     &ett_cemi_dpt,
3482     &ett_cemi_scf,
3483     &ett_cemi_decrypted
3484   };
3485 
3486   proto_cemi = proto_register_protocol( "Common External Message Interface", "cEMI", "cemi" );
3487 
3488   proto_register_field_array( proto_cemi, hf, array_length( hf ) );
3489   proto_register_subtree_array( ett, array_length( ett ) );
3490 
3491   register_dissector( "cemi", dissect_cemi, proto_cemi );
3492 }
3493 
proto_reg_handoff_cemi(void)3494 void proto_reg_handoff_cemi( void )
3495 {
3496 }
3497 
3498 /*
3499  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
3500  *
3501  * Local variables:
3502  * c-basic-offset: 2
3503  * tab-width: 8
3504  * indent-tabs-mode: nil
3505  * End:
3506  *
3507  * vi: set shiftwidth=2 tabstop=8 expandtab:
3508  * :indentSize=2:tabSize=8:noTabs=true:
3509  */
3510