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