1 /* packet-btmesh-pbadv.c
2 * Routines for Bluetooth mesh PB-ADV dissection
4 * Copyright 2019, Piotr Winiarczyk <wino45@gmail.com>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * SPDX-License-Identifier: GPL-2.0-or-later
12 * Ref: Mesh Profile v1.0
13 * https://www.bluetooth.com/specifications/mesh-specifications
18 #include <epan/packet.h>
19 #include <epan/prefs.h>
20 #include <epan/reassemble.h>
21 #include <epan/expert.h>
23 #include "packet-btmesh.h"
25 #define BTMESH_PB_ADV_NOT_USED 0
27 #define TRANSACTION_START 0x00
28 #define TRANSACTION_ACKNOWLEDGMENT 0x01
29 #define TRANSACTION_CONTINUATION 0x02
30 #define PROVISIONING_BEARER_CONTROL 0x03
32 #define LINK_OPEN 0x00
34 #define LINK_CLOSE 0x02
36 void proto_register_btmesh_pbadv(void);
37 void proto_reg_handoff_btmesh_pbadv(void);
39 static int proto_btmesh_pbadv
;
41 static dissector_handle_t btmesh_provisioning_handle
;
43 static int hf_btmesh_pbadv_linkid
;
44 static int hf_btmesh_pbadv_trnumber
;
46 static int hf_btmesh_generic_provisioning_control_format
;
47 static int hf_btmesh_gpcf_segn
;
48 static int hf_btmesh_gpcf_total_length
;
50 static int hf_btmesh_gpcf_fcs
;
51 static int hf_btmesh_gpcf_padding
;
52 static int hf_btmesh_gpcf_segment_index
;
53 static int hf_btmesh_gpcf_bearer_opcode
;
54 static int hf_btmesh_gpcf_bearer_opcode_device_UUID
;
55 static int hf_btmesh_gpcf_bearer_opcode_reason
;
56 static int hf_btmesh_gpcf_bearer_unknown_data
;
58 static int hf_btmesh_gpp_payload
;
59 static int hf_btmesh_gpp_payload_fragment
;
60 static int hf_btmesh_gpp_fragments
;
61 static int hf_btmesh_gpp_fragment
;
62 static int hf_btmesh_gpp_fragment_overlap
;
63 static int hf_btmesh_gpp_fragment_overlap_conflict
;
64 static int hf_btmesh_gpp_fragment_multiple_tails
;
65 static int hf_btmesh_gpp_fragment_too_long_fragment
;
66 static int hf_btmesh_gpp_fragment_error
;
67 static int hf_btmesh_gpp_fragment_count
;
68 static int hf_btmesh_gpp_reassembled_length
;
70 static int ett_btmesh_pbadv
;
71 static int ett_btmesh_generic_provisioning
;
72 static int ett_btmesh_gpp_fragments
;
73 static int ett_btmesh_gpp_fragment
;
75 static expert_field ei_btmesh_gpcf_unknown_opcode
;
76 static expert_field ei_btmesh_gpcf_unknown_payload
;
78 static const fragment_items btmesh_gpp_frag_items
= {
79 &ett_btmesh_gpp_fragments
,
80 &ett_btmesh_gpp_fragment
,
82 &hf_btmesh_gpp_fragments
,
83 &hf_btmesh_gpp_fragment
,
84 &hf_btmesh_gpp_fragment_overlap
,
85 &hf_btmesh_gpp_fragment_overlap_conflict
,
86 &hf_btmesh_gpp_fragment_multiple_tails
,
87 &hf_btmesh_gpp_fragment_too_long_fragment
,
88 &hf_btmesh_gpp_fragment_error
,
89 &hf_btmesh_gpp_fragment_count
,
91 &hf_btmesh_gpp_reassembled_length
,
92 /* Reassembled data field */
97 static const value_string btmesh_generic_provisioning_control_format
[] = {
98 { 0, "Transaction Start" },
99 { 1, "Transaction Acknowledgment" },
100 { 2, "Transaction Continuation" },
101 { 3, "Provisioning Bearer Control" },
105 static const value_string btmesh_gpcf_bearer_opcode_format
[] = {
112 static const value_string btmesh_gpcf_bearer_opcode_reason_format
[] = {
119 /* needed for packet reassembly */
120 static reassembly_table pbadv_reassembly_table
;
122 typedef struct _pbadv_fragment_key
{
124 uint8_t transaction_number
;
125 } pbadv_fragment_key
;
128 pbadv_fragment_hash(const void *k
)
130 const pbadv_fragment_key
* key
= (const pbadv_fragment_key
*) k
;
135 hash_val
+= key
->link_id
;
136 hash_val
+= key
->transaction_number
;
141 pbadv_fragment_equal(const void *k1
, const void *k2
)
143 const pbadv_fragment_key
* key1
= (const pbadv_fragment_key
*) k1
;
144 const pbadv_fragment_key
* key2
= (const pbadv_fragment_key
*) k2
;
146 return ((key1
->link_id
== key2
->link_id
) && (key1
->transaction_number
== key2
->transaction_number
)
151 pbadv_fragment_temporary_key(const packet_info
*pinfo _U_
, const uint32_t id _U_
,
154 pbadv_fragment_key
*key
= g_slice_new(pbadv_fragment_key
);
155 const pbadv_fragment_key
*pbadv
= (const pbadv_fragment_key
*)data
;
157 key
->link_id
= pbadv
->link_id
;
158 key
->transaction_number
= pbadv
->transaction_number
;
164 pbadv_fragment_free_temporary_key(void *ptr
)
166 pbadv_fragment_key
*key
= (pbadv_fragment_key
*)ptr
;
168 g_slice_free(pbadv_fragment_key
, key
);
172 pbadv_fragment_persistent_key(const packet_info
*pinfo _U_
, const uint32_t id _U_
,
175 pbadv_fragment_key
*key
= g_slice_new(pbadv_fragment_key
);
176 const pbadv_fragment_key
*pbadv
= (const pbadv_fragment_key
*)data
;
178 key
->link_id
= pbadv
->link_id
;
179 key
->transaction_number
= pbadv
->transaction_number
;
185 pbadv_fragment_free_persistent_key(void *ptr
)
187 pbadv_fragment_key
*key
= (pbadv_fragment_key
*)ptr
;
189 g_slice_free(pbadv_fragment_key
, key
);
193 static const reassembly_table_functions pbadv_reassembly_table_functions
= {
195 pbadv_fragment_equal
,
196 pbadv_fragment_temporary_key
,
197 pbadv_fragment_persistent_key
,
198 pbadv_fragment_free_temporary_key
,
199 pbadv_fragment_free_persistent_key
203 dissect_btmesh_pbadv_msg(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
207 proto_tree
*sub_tree
, *sub_tree_generic_provisioning
;
209 bool defragment
= false;
211 btle_mesh_transport_ctx_t tr_ctx
;
212 uint8_t segn
, length
;
213 uint32_t total_length
;
214 uint8_t gpcf_bearer_opcode
;
216 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "BT Mesh PB-ADV");
218 item
= proto_tree_add_item(tree
, proto_btmesh_pbadv
, tvb
, offset
, -1, ENC_NA
);
219 sub_tree
= proto_item_add_subtree(item
, ett_btmesh_pbadv
);
221 uint32_t pbadv_link_id
= tvb_get_uint32(tvb
, offset
, ENC_BIG_ENDIAN
);
222 proto_tree_add_item(sub_tree
, hf_btmesh_pbadv_linkid
, tvb
, offset
, 4, ENC_NA
);
225 uint8_t pbadv_trnumber
= tvb_get_uint8(tvb
, offset
);
226 proto_tree_add_item(sub_tree
, hf_btmesh_pbadv_trnumber
, tvb
, offset
, 1, ENC_NA
);
229 pbadv_fragment_key frg_key
;
230 frg_key
.link_id
= pbadv_link_id
;
231 frg_key
.transaction_number
= pbadv_trnumber
;
233 sub_tree_generic_provisioning
= proto_tree_add_subtree(sub_tree
, tvb
, offset
, -1, ett_btmesh_generic_provisioning
, &ti
, "Generic Provisioning PDU");
235 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_generic_provisioning_control_format
, tvb
, offset
, 1, ENC_NA
);
236 uint8_t gpcf
= tvb_get_uint8(tvb
, offset
) & 0x03;
238 col_set_str(pinfo
->cinfo
, COL_INFO
, val_to_str_const(gpcf
, btmesh_generic_provisioning_control_format
, "Unknown PDU"));
240 fragment_head
*fd_head
= NULL
;
241 int segment_index
= -1;
245 case TRANSACTION_START
:
246 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_segn
, tvb
, offset
, 1, ENC_NA
);
247 segn
= (tvb_get_uint8(tvb
, offset
) & 0xFC) >> 2;
249 total_length
= (uint32_t)tvb_get_uint16(tvb
, offset
, ENC_BIG_ENDIAN
);
250 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_total_length
, tvb
, offset
, 2, ENC_NA
);
252 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_fcs
, tvb
, offset
, 1, ENC_NA
);
257 if (btmesh_provisioning_handle
) {
258 length
= tvb_reported_length(tvb
);
259 tr_ctx
.transport
= E_BTMESH_TR_ADV
;
260 tr_ctx
.fragmented
= false;
261 tr_ctx
.segment_index
= 0;
262 call_dissector_with_data(btmesh_provisioning_handle
, tvb_new_subset_length(tvb
, offset
, length
),
263 pinfo
, proto_tree_get_root(sub_tree_generic_provisioning
), &tr_ctx
);
265 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpp_payload
, tvb
, offset
, -1, ENC_NA
);
269 if (!pinfo
->fd
->visited
) {
270 //First fragment can be delivered out of order, and can be the last one.
271 fd_head
= fragment_get(&pbadv_reassembly_table
, pinfo
, BTMESH_PB_ADV_NOT_USED
, &frg_key
);
273 fragment_set_tot_len(&pbadv_reassembly_table
, pinfo
, BTMESH_PB_ADV_NOT_USED
, &frg_key
, total_length
);
275 fd_head
= fragment_add(&pbadv_reassembly_table
,
277 BTMESH_PB_ADV_NOT_USED
, &frg_key
,
279 tvb_captured_length_remaining(tvb
, offset
),
282 //Set the length only when not reassembled
283 fragment_set_tot_len(&pbadv_reassembly_table
, pinfo
, BTMESH_PB_ADV_NOT_USED
, &frg_key
, total_length
);
286 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpp_payload_fragment
, tvb
, offset
, -1, ENC_NA
);
291 //Transaction Acknowledgment
292 case TRANSACTION_ACKNOWLEDGMENT
:
293 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_padding
, tvb
, offset
, 1, ENC_NA
);
296 //Transaction Continuation
297 case TRANSACTION_CONTINUATION
:
298 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_segment_index
, tvb
, offset
, 1, ENC_NA
);
299 segment_index
= (tvb_get_uint8(tvb
, offset
) & 0xFC) >> 2;
303 if (!pinfo
->fd
->visited
) {
304 fragment_add(&pbadv_reassembly_table
,
306 BTMESH_PB_ADV_NOT_USED
, &frg_key
,
307 20 + (segment_index
- 1) * 23,
308 tvb_captured_length_remaining(tvb
, offset
),
311 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpp_payload_fragment
, tvb
, offset
, -1, ENC_NA
);
315 //Provisioning Bearer Control
316 case PROVISIONING_BEARER_CONTROL
:
317 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_bearer_opcode
, tvb
, offset
, 1, ENC_NA
);
318 gpcf_bearer_opcode
= (tvb_get_uint8(tvb
, offset
) & 0xFC) >> 2;
320 switch(gpcf_bearer_opcode
) {
322 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_bearer_opcode_device_UUID
, tvb
, offset
, 16, ENC_NA
);
327 //No data in this PDU
331 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_bearer_opcode_reason
, tvb
, offset
, 1, ENC_NA
);
337 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpcf_bearer_unknown_data
, tvb
, offset
, -1, ENC_NA
);
338 offset
+= tvb_captured_length_remaining(tvb
, offset
);
339 proto_tree_add_expert(sub_tree
, pinfo
, &ei_btmesh_gpcf_unknown_opcode
, tvb
, offset
, -1);
342 //There is still some data but all data should be already disssected
343 if (tvb_captured_length_remaining(tvb
, offset
) != 0) {
344 proto_tree_add_expert(sub_tree
, pinfo
, &ei_btmesh_gpcf_unknown_payload
, tvb
, offset
, -1);
350 if (pinfo
->fd
->visited
&& defragment
) {
351 fd_head
= fragment_get(&pbadv_reassembly_table
, pinfo
, BTMESH_PB_ADV_NOT_USED
, &frg_key
);
352 if (fd_head
&& (fd_head
->flags
&FD_DEFRAGMENTED
)) {
354 next_tvb
= process_reassembled_data(tvb
, offset
, pinfo
, "Reassembled Provisioning PDU", fd_head
, &btmesh_gpp_frag_items
, NULL
, sub_tree_generic_provisioning
);
356 col_append_str(pinfo
->cinfo
, COL_INFO
, " (Message Reassembled)");
357 if (btmesh_provisioning_handle
) {
358 tr_ctx
.transport
= E_BTMESH_TR_ADV
;
359 tr_ctx
.fragmented
= true;
360 tr_ctx
.segment_index
= segment_index
;
361 call_dissector_with_data(btmesh_provisioning_handle
, next_tvb
, pinfo
,
362 proto_tree_get_root(sub_tree_generic_provisioning
), &tr_ctx
);
364 proto_tree_add_item(sub_tree_generic_provisioning
, hf_btmesh_gpp_payload
, next_tvb
, 0, -1, ENC_NA
);
367 col_append_fstr(pinfo
->cinfo
, COL_INFO
," (Message fragment %u)", segment_index
);
372 return tvb_reported_length(tvb
);
376 proto_register_btmesh_pbadv(void)
378 static hf_register_info hf
[] = {
380 { &hf_btmesh_pbadv_linkid
,
381 { "Link ID", "pbadv.linkid",
382 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
385 { &hf_btmesh_pbadv_trnumber
,
386 { "Transaction Number", "pbadv.trnumber",
387 FT_UINT8
, BASE_DEC
, NULL
, 0x0,
390 //Generic Provisioning Control
391 { &hf_btmesh_generic_provisioning_control_format
,
392 { "Generic Provisioning Control Format", "pbadv.gen_prov.gpcf",
393 FT_UINT8
, BASE_DEC
, VALS(btmesh_generic_provisioning_control_format
), 0x03,
396 { &hf_btmesh_gpcf_segn
,
397 { "The last segment number", "pbadv.gen_prov.gpcf.segn",
398 FT_UINT8
, BASE_DEC
, NULL
, 0xFC,
401 { &hf_btmesh_gpcf_total_length
,
402 { "Total Length", "pbadv.gen_prov.gpcf.total_length",
403 FT_UINT16
, BASE_DEC
, NULL
, 0x0,
406 { &hf_btmesh_gpcf_fcs
,
407 { "Frame Check Sequence", "pbadv.gen_prov.gpcf.fcs",
408 FT_UINT8
, BASE_HEX
, NULL
, 0x0,
411 { &hf_btmesh_gpcf_padding
,
412 { "Padding", "pbadv.gen_prov.gpcf.padding",
413 FT_UINT8
, BASE_DEC
, NULL
, 0xFC,
416 { &hf_btmesh_gpcf_segment_index
,
417 { "Segment number of the transaction", "pbadv.gen_prov.gpcf.segment_index",
418 FT_UINT8
, BASE_DEC
, NULL
, 0xFC,
421 { &hf_btmesh_gpcf_bearer_opcode
,
422 { "Bearer Opcode", "pbadv.gen_prov.gpcf.bearer_opcode",
423 FT_UINT8
, BASE_DEC
, VALS(btmesh_gpcf_bearer_opcode_format
), 0xFC,
426 { &hf_btmesh_gpcf_bearer_opcode_device_UUID
,
427 { "Device UUID", "pbadv.gen_prov.gpcf.bearer_opcode.device_uuid",
428 FT_GUID
, BASE_NONE
, NULL
, 0x00,
431 { &hf_btmesh_gpcf_bearer_opcode_reason
,
432 { "Reason", "pbadv.gen_prov.gpcf.bearer_opcode.reason",
433 FT_UINT8
, BASE_DEC
, VALS(btmesh_gpcf_bearer_opcode_reason_format
), 0x00,
436 { &hf_btmesh_gpcf_bearer_unknown_data
,
437 { "Unknown Data", "pbadv.gen_prov.gpcf.unknown_data",
438 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
441 //Generic Provisioning Payload
442 { &hf_btmesh_gpp_payload
,
443 { "Generic Provisioning Payload", "pbadv.gen_prov.gpp.payload",
444 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
447 { &hf_btmesh_gpp_payload_fragment
,
448 { "Generic Provisioning Payload Fragment", "pbadv.gen_prov.gpp.payload.fragment",
449 FT_BYTES
, BASE_NONE
, NULL
, 0x0,
452 //Generic Provisioning Payload Reassembly
453 { &hf_btmesh_gpp_fragments
,
454 { "Reassembled Generic Provisioning Payload Fragments", "pbadv.gen_prov.gpp.fragments",
455 FT_NONE
, BASE_NONE
, NULL
, 0x0,
458 { &hf_btmesh_gpp_fragment
,
459 { "Generic Provisioning Payload Fragment", "pbadv.gen_prov.gpp.fragment",
460 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
463 { &hf_btmesh_gpp_fragment_overlap
,
464 { "Fragment overlap", "pbadv.gen_prov.gpp.fragment.overlap",
465 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
466 "Fragment overlaps with other fragments", HFILL
}
468 { &hf_btmesh_gpp_fragment_overlap_conflict
,
469 { "Conflicting data in fragment overlap", "pbadv.gen_prov.gpp.fragment.overlap.conflict",
470 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
471 "Overlapping fragments contained conflicting data", HFILL
}
473 { &hf_btmesh_gpp_fragment_multiple_tails
,
474 { "Multiple tail fragments found", "pbadv.gen_prov.gpp.fragment.multipletails",
475 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
476 "Several tails were found when defragmenting the packet", HFILL
}
478 { &hf_btmesh_gpp_fragment_too_long_fragment
,
479 { "Fragment too long", "pbadv.gen_prov.gpp.fragment.toolongfragment",
480 FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
481 "Fragment contained data past end of packet", HFILL
}
483 { &hf_btmesh_gpp_fragment_error
,
484 { "Defragmentation error", "pbadv.gen_prov.gpp.fragment.error",
485 FT_FRAMENUM
, BASE_NONE
, NULL
, 0x0,
486 "Defragmentation error due to illegal fragments", HFILL
}
488 { &hf_btmesh_gpp_fragment_count
,
489 { "Fragment count", "pbadv.gen_prov.gpp.fragment.count",
490 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
493 { &hf_btmesh_gpp_reassembled_length
,
494 { "Reassembled Generic Provisioning Payload length", "pbadv.gen_prov.gpp.reassembled.length",
495 FT_UINT32
, BASE_DEC
, NULL
, 0x0,
496 "The total length of the reassembled payload", HFILL
}
500 static int *ett
[] = {
502 &ett_btmesh_generic_provisioning
,
503 &ett_btmesh_gpp_fragments
,
504 &ett_btmesh_gpp_fragment
,
507 static ei_register_info ei
[] = {
508 { &ei_btmesh_gpcf_unknown_opcode
,{ "pbadv.gpcf.unknown_opcode", PI_PROTOCOL
, PI_WARN
, "Unknown Opcode", EXPFILL
} },
509 { &ei_btmesh_gpcf_unknown_payload
,{ "pbadv.gpcf.unknown_payload", PI_PROTOCOL
, PI_ERROR
, "Unknown Payload", EXPFILL
} },
512 expert_module_t
* expert_btmesh_pbadv
;
514 proto_btmesh_pbadv
= proto_register_protocol("Bluetooth Mesh PB-ADV", "BT Mesh PB-ADV", "pbadv");
516 proto_register_field_array(proto_btmesh_pbadv
, hf
, array_length(hf
));
517 proto_register_subtree_array(ett
, array_length(ett
));
519 expert_btmesh_pbadv
= expert_register_protocol(proto_btmesh_pbadv
);
520 expert_register_field_array(expert_btmesh_pbadv
, ei
, array_length(ei
));
522 prefs_register_protocol_subtree("Bluetooth", proto_btmesh_pbadv
, NULL
);
523 register_dissector("btmesh.pbadv", dissect_btmesh_pbadv_msg
, proto_btmesh_pbadv
);
525 reassembly_table_register(&pbadv_reassembly_table
, &pbadv_reassembly_table_functions
);
529 proto_reg_handoff_btmesh_pbadv(void)
531 btmesh_provisioning_handle
= find_dissector("btmesh.provisioning");
540 * indent-tabs-mode: nil
543 * ex: set shiftwidth=4 tabstop=8 expandtab:
544 * :indentSize=4:tabSize=8:noTabs=true: