1 /* packet-btmesh-pbadv.c
2  * Routines for Bluetooth mesh PB-ADV dissection
3  *
4  * Copyright 2019, Piotr Winiarczyk <wino45@gmail.com>
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  * Ref: Mesh Profile v1.0
13  * https://www.bluetooth.com/specifications/mesh-specifications
14  */
15 
16 #include "config.h"
17 
18 #include <epan/packet.h>
19 #include <epan/prefs.h>
20 #include <epan/reassemble.h>
21 #include <epan/expert.h>
22 
23 #include "packet-btmesh.h"
24 
25 #define BTMESH_PB_ADV_NOT_USED 0
26 
27 #define TRANSACTION_START           0x00
28 #define TRANSACTION_ACKNOWLEDGMENT  0x01
29 #define TRANSACTION_CONTINUATION    0x02
30 #define PROVISIONING_BEARER_CONTROL 0x03
31 
32 #define LINK_OPEN  0x00
33 #define LINK_ACK   0x01
34 #define LINK_CLOSE 0x02
35 
36 void proto_register_btmesh_pbadv(void);
37 void proto_reg_handoff_btmesh_pbadv(void);
38 
39 static int proto_btmesh_pbadv = -1;
40 
41 static dissector_handle_t btmesh_provisioning_handle;
42 
43 static int hf_btmesh_pbadv_linkid = -1;
44 static int hf_btmesh_pbadv_trnumber = -1;
45 
46 static int hf_btmesh_generic_provisioning_control_format = -1;
47 static int hf_btmesh_gpcf_segn = -1;
48 static int hf_btmesh_gpcf_total_length = -1;
49 //TODO - check FCS
50 static int hf_btmesh_gpcf_fcs = -1;
51 static int hf_btmesh_gpcf_padding = -1;
52 static int hf_btmesh_gpcf_segment_index = -1;
53 static int hf_btmesh_gpcf_bearer_opcode = -1;
54 static int hf_btmesh_gpcf_bearer_opcode_device_UUID = -1;
55 static int hf_btmesh_gpcf_bearer_opcode_reason = -1;
56 static int hf_btmesh_gpcf_bearer_unknown_data = -1;
57 
58 static int hf_btmesh_gpp_payload = -1;
59 static int hf_btmesh_gpp_payload_fragment = -1;
60 static int hf_btmesh_gpp_fragments = -1;
61 static int hf_btmesh_gpp_fragment = -1;
62 static int hf_btmesh_gpp_fragment_overlap = -1;
63 static int hf_btmesh_gpp_fragment_overlap_conflict = -1;
64 static int hf_btmesh_gpp_fragment_multiple_tails = -1;
65 static int hf_btmesh_gpp_fragment_too_long_fragment = -1;
66 static int hf_btmesh_gpp_fragment_error = -1;
67 static int hf_btmesh_gpp_fragment_count = -1;
68 static int hf_btmesh_gpp_reassembled_length = -1;
69 
70 static int ett_btmesh_pbadv = -1;
71 static int ett_btmesh_generic_provisioning = -1;
72 static int ett_btmesh_gpp_fragments = -1;
73 static int ett_btmesh_gpp_fragment = -1;
74 
75 static expert_field ei_btmesh_gpcf_unknown_opcode = EI_INIT;
76 static expert_field ei_btmesh_gpcf_unknown_payload = EI_INIT;
77 
78 static const fragment_items btmesh_gpp_frag_items = {
79     &ett_btmesh_gpp_fragments,
80     &ett_btmesh_gpp_fragment,
81 
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,
90     NULL,
91     &hf_btmesh_gpp_reassembled_length,
92     /* Reassembled data field */
93     NULL,
94     "fragments"
95 };
96 
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" },
102     { 0, NULL }
103 };
104 
105 static const value_string btmesh_gpcf_bearer_opcode_format[] = {
106     { 0, "Link Open" },
107     { 1, "Link ACK" },
108     { 2, "Link Close" },
109     { 0, NULL }
110 };
111 
112 static const value_string btmesh_gpcf_bearer_opcode_reason_format[] = {
113     { 0, "Success" },
114     { 1, "Timeout" },
115     { 2, "Fail" },
116     { 0, NULL }
117 };
118 
119 /* needed for packet reassembly */
120 static reassembly_table pbadv_reassembly_table;
121 
122 typedef struct _pbadv_fragment_key {
123     guint32 link_id;
124     guint8 transaction_number;
125 } pbadv_fragment_key;
126 
127 static guint
pbadv_fragment_hash(gconstpointer k)128 pbadv_fragment_hash(gconstpointer k)
129 {
130     const pbadv_fragment_key* key = (const pbadv_fragment_key*) k;
131     guint hash_val;
132 
133     hash_val = 0;
134 
135     hash_val += key->link_id;
136     hash_val += key->transaction_number;
137     return hash_val;
138 }
139 
140 static gint
pbadv_fragment_equal(gconstpointer k1,gconstpointer k2)141 pbadv_fragment_equal(gconstpointer k1, gconstpointer k2)
142 {
143     const pbadv_fragment_key* key1 = (const pbadv_fragment_key*) k1;
144     const pbadv_fragment_key* key2 = (const pbadv_fragment_key*) k2;
145 
146     return ((key1->link_id == key2->link_id) && (key1->transaction_number == key2->transaction_number)
147             ? TRUE : FALSE);
148 }
149 
150 static void *
pbadv_fragment_temporary_key(const packet_info * pinfo _U_,const guint32 id _U_,const void * data)151 pbadv_fragment_temporary_key(const packet_info *pinfo _U_, const guint32 id _U_,
152                               const void *data)
153 {
154     pbadv_fragment_key *key = g_slice_new(pbadv_fragment_key);
155     const pbadv_fragment_key *pbadv = (const pbadv_fragment_key *)data;
156 
157     key->link_id = pbadv->link_id;
158     key->transaction_number = pbadv->transaction_number;
159 
160     return key;
161 }
162 
163 static void
pbadv_fragment_free_temporary_key(gpointer ptr)164 pbadv_fragment_free_temporary_key(gpointer ptr)
165 {
166     pbadv_fragment_key *key = (pbadv_fragment_key *)ptr;
167 
168     g_slice_free(pbadv_fragment_key, key);
169 }
170 
171 static void *
pbadv_fragment_persistent_key(const packet_info * pinfo _U_,const guint32 id _U_,const void * data)172 pbadv_fragment_persistent_key(const packet_info *pinfo _U_, const guint32 id _U_,
173                               const void *data)
174 {
175     pbadv_fragment_key *key = g_slice_new(pbadv_fragment_key);
176     const pbadv_fragment_key *pbadv = (const pbadv_fragment_key *)data;
177 
178     key->link_id = pbadv->link_id;
179     key->transaction_number = pbadv->transaction_number;
180 
181     return key;
182 }
183 
184 static void
pbadv_fragment_free_persistent_key(gpointer ptr)185 pbadv_fragment_free_persistent_key(gpointer ptr)
186 {
187     pbadv_fragment_key *key = (pbadv_fragment_key *)ptr;
188     if (key) {
189         g_slice_free(pbadv_fragment_key, key);
190     }
191 }
192 
193 static const reassembly_table_functions pbadv_reassembly_table_functions = {
194     pbadv_fragment_hash,
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
200 };
201 
202 static gint
dissect_btmesh_pbadv_msg(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)203 dissect_btmesh_pbadv_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
204 {
205 
206     proto_item *item;
207     proto_tree *sub_tree, *sub_tree_generic_provisioning;
208     proto_item *ti;
209     gboolean defragment = FALSE;
210     int offset = 0;
211     btle_mesh_transport_ctx_t tr_ctx;
212     guint8 segn, length;
213     guint32 total_length;
214     guint8 gpcf_bearer_opcode;
215 
216     col_set_str(pinfo->cinfo, COL_PROTOCOL, "BT Mesh PB-ADV");
217 
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);
220 
221     guint32 pbadv_link_id = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);
222     proto_tree_add_item(sub_tree, hf_btmesh_pbadv_linkid, tvb, offset, 4, ENC_NA);
223     offset += 4;
224 
225     guint8 pbadv_trnumber = tvb_get_guint8(tvb, offset);
226     proto_tree_add_item(sub_tree, hf_btmesh_pbadv_trnumber, tvb, offset, 1, ENC_NA);
227     offset += 1;
228 
229     pbadv_fragment_key frg_key;
230     frg_key.link_id = pbadv_link_id;
231     frg_key.transaction_number = pbadv_trnumber;
232 
233     sub_tree_generic_provisioning = proto_tree_add_subtree(sub_tree, tvb, offset, -1, ett_btmesh_generic_provisioning, &ti, "Generic Provisioning PDU");
234 
235     proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_generic_provisioning_control_format, tvb, offset, 1, ENC_NA);
236     guint8 gpcf = tvb_get_guint8(tvb, offset) & 0x03;
237 
238     col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(gpcf, btmesh_generic_provisioning_control_format, "Unknown PDU"));
239 
240     fragment_head *fd_head = NULL;
241     gint segment_index = -1;
242 
243     switch (gpcf) {
244         //Transaction Start
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_guint8(tvb, offset) & 0xFC) >> 2;
248             offset += 1;
249             total_length = (guint32)tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
250             proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_total_length, tvb, offset, 2, ENC_NA);
251             offset += 2;
252             proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_fcs, tvb, offset, 1, ENC_NA);
253             offset += 1;
254             segment_index = 0;
255             defragment = TRUE;
256             if (segn == 0) {
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);
264                 } else {
265                     proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpp_payload, tvb, offset, -1, ENC_NA);
266                 }
267             } else {
268                 //Segmentation
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);
272                     if (fd_head) {
273                         fragment_set_tot_len(&pbadv_reassembly_table, pinfo, BTMESH_PB_ADV_NOT_USED, &frg_key, total_length);
274                     }
275                     fd_head = fragment_add(&pbadv_reassembly_table,
276                         tvb, offset, pinfo,
277                         BTMESH_PB_ADV_NOT_USED, &frg_key,
278                         0,
279                         tvb_captured_length_remaining(tvb, offset),
280                         TRUE);
281                     if (!fd_head) {
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);
284                     }
285                 } else {
286                     proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpp_payload_fragment, tvb, offset, -1, ENC_NA);
287                 }
288             }
289 
290         break;
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);
294 
295         break;
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_guint8(tvb, offset) & 0xFC) >> 2;
300             defragment = TRUE;
301             offset += 1;
302             //Segmentation
303             if (!pinfo->fd->visited) {
304                 fragment_add(&pbadv_reassembly_table,
305                     tvb, offset, pinfo,
306                     BTMESH_PB_ADV_NOT_USED, &frg_key,
307                     20 + (segment_index - 1) * 23,
308                     tvb_captured_length_remaining(tvb, offset),
309                     TRUE);
310             } else {
311                 proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpp_payload_fragment, tvb, offset, -1, ENC_NA);
312             }
313 
314         break;
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_guint8(tvb, offset) & 0xFC) >> 2;
319             offset += 1;
320             switch(gpcf_bearer_opcode) {
321                 case LINK_OPEN:
322                     proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_bearer_opcode_device_UUID, tvb, offset, 16, ENC_NA);
323                     offset += 16;
324 
325                 break;
326                 case LINK_ACK:
327                     //No data in this PDU
328 
329                 break;
330                 case LINK_CLOSE:
331                     proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_bearer_opcode_reason, tvb, offset, 1, ENC_NA);
332                     offset += 1;
333 
334                 break;
335                 default:
336                     //Unknown data
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);
340                 break;
341             }
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);
345             }
346 
347         break;
348     }
349     //Second pass
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)) {
353             tvbuff_t *next_tvb;
354             next_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled Provisioning PDU", fd_head, &btmesh_gpp_frag_items, NULL, sub_tree_generic_provisioning);
355             if (next_tvb) {
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);
363                 } else {
364                     proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpp_payload, next_tvb, 0, -1, ENC_NA);
365                 }
366             } else {
367                 col_append_fstr(pinfo->cinfo, COL_INFO," (Message fragment %u)", segment_index);
368             }
369         }
370     }
371 
372     return tvb_reported_length(tvb);
373 }
374 
375 static void
pbadv_init_routine(void)376 pbadv_init_routine(void)
377 {
378     reassembly_table_register(&pbadv_reassembly_table, &pbadv_reassembly_table_functions);
379 }
380 
381 void
proto_register_btmesh_pbadv(void)382 proto_register_btmesh_pbadv(void)
383 {
384     static hf_register_info hf[] = {
385         //PB-ADV
386         { &hf_btmesh_pbadv_linkid,
387             { "Link ID", "pbadv.linkid",
388                 FT_UINT32, BASE_DEC, NULL, 0x0,
389                 NULL, HFILL }
390         },
391         { &hf_btmesh_pbadv_trnumber,
392             { "Transaction Number", "pbadv.trnumber",
393                 FT_UINT8, BASE_DEC, NULL, 0x0,
394                 NULL, HFILL }
395         },
396         //Generic Provisioning Control
397         { &hf_btmesh_generic_provisioning_control_format,
398             { "Generic Provisioning Control Format", "pbadv.gen_prov.gpcf",
399                 FT_UINT8, BASE_DEC, VALS(btmesh_generic_provisioning_control_format), 0x03,
400                 NULL, HFILL }
401         },
402         { &hf_btmesh_gpcf_segn,
403             { "The last segment number", "pbadv.gen_prov.gpcf.segn",
404                 FT_UINT8, BASE_DEC, NULL, 0xFC,
405                 NULL, HFILL }
406         },
407         { &hf_btmesh_gpcf_total_length,
408             { "Total Length", "pbadv.gen_prov.gpcf.total_length",
409                 FT_UINT16, BASE_DEC, NULL, 0x0,
410                 NULL, HFILL }
411         },
412         { &hf_btmesh_gpcf_fcs,
413             { "Frame Check Sequence", "pbadv.gen_prov.gpcf.fcs",
414                 FT_UINT8, BASE_HEX, NULL, 0x0,
415                 NULL, HFILL }
416         },
417         { &hf_btmesh_gpcf_padding,
418             { "Padding", "pbadv.gen_prov.gpcf.padding",
419                 FT_UINT8, BASE_DEC, NULL, 0xFC,
420                 NULL, HFILL }
421         },
422         { &hf_btmesh_gpcf_segment_index,
423             { "Segment number of the transaction", "pbadv.gen_prov.gpcf.segment_index",
424                 FT_UINT8, BASE_DEC, NULL, 0xFC,
425                 NULL, HFILL }
426         },
427         { &hf_btmesh_gpcf_bearer_opcode,
428             { "Bearer Opcode", "pbadv.gen_prov.gpcf.bearer_opcode",
429                 FT_UINT8, BASE_DEC, VALS(btmesh_gpcf_bearer_opcode_format), 0xFC,
430                 NULL, HFILL }
431         },
432         { &hf_btmesh_gpcf_bearer_opcode_device_UUID,
433             { "Device UUID", "pbadv.gen_prov.gpcf.bearer_opcode.device_uuid",
434                 FT_GUID, BASE_NONE, NULL, 0x00,
435                 NULL, HFILL }
436         },
437         { &hf_btmesh_gpcf_bearer_opcode_reason,
438             { "Reason", "pbadv.gen_prov.gpcf.bearer_opcode.reason",
439                 FT_UINT8, BASE_DEC, VALS(btmesh_gpcf_bearer_opcode_reason_format), 0x00,
440                 NULL, HFILL }
441         },
442         { &hf_btmesh_gpcf_bearer_unknown_data,
443             { "Unknown Data", "pbadv.gen_prov.gpcf.unknown_data",
444                 FT_BYTES, BASE_NONE, NULL, 0x0,
445                 NULL, HFILL }
446         },
447         //Generic Provisioning Payload
448         { &hf_btmesh_gpp_payload,
449             { "Generic Provisioning Payload", "pbadv.gen_prov.gpp.payload",
450                 FT_BYTES, BASE_NONE, NULL, 0x0,
451                 NULL, HFILL }
452         },
453         { &hf_btmesh_gpp_payload_fragment,
454             { "Generic Provisioning Payload Fragment", "pbadv.gen_prov.gpp.payload.fragment",
455                 FT_BYTES, BASE_NONE, NULL, 0x0,
456                 NULL, HFILL }
457         },
458         //Generic Provisioning Payload Reassembly
459         { &hf_btmesh_gpp_fragments,
460             { "Reassembled Generic Provisioning Payload Fragments", "pbadv.gen_prov.gpp.fragments",
461                 FT_NONE, BASE_NONE, NULL, 0x0,
462                 "Generic Provisioning Payload Fragments", HFILL }
463         },
464         { &hf_btmesh_gpp_fragment,
465             { "Generic Provisioning Payload Fragment", "pbadv.gen_prov.gpp.fragment",
466                 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
467                 NULL, HFILL }
468         },
469         { &hf_btmesh_gpp_fragment_overlap,
470             { "Fragment overlap", "pbadv.gen_prov.gpp.fragment.overlap",
471                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
472                 "Fragment overlaps with other fragments", HFILL }
473         },
474         { &hf_btmesh_gpp_fragment_overlap_conflict,
475             { "Conflicting data in fragment overlap", "pbadv.gen_prov.gpp.fragment.overlap.conflict",
476                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
477                 "Overlapping fragments contained conflicting data", HFILL }
478         },
479         { &hf_btmesh_gpp_fragment_multiple_tails,
480             { "Multiple tail fragments found", "pbadv.gen_prov.gpp.fragment.multipletails",
481                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
482                 "Several tails were found when defragmenting the packet", HFILL }
483         },
484         { &hf_btmesh_gpp_fragment_too_long_fragment,
485             { "Fragment too long", "pbadv.gen_prov.gpp.fragment.toolongfragment",
486                 FT_BOOLEAN, BASE_NONE, NULL, 0x0,
487                 "Fragment contained data past end of packet", HFILL }
488         },
489         { &hf_btmesh_gpp_fragment_error,
490             { "Defragmentation error", "pbadv.gen_prov.gpp.fragment.error",
491                 FT_FRAMENUM, BASE_NONE, NULL, 0x0,
492                 "Defragmentation error due to illegal fragments", HFILL }
493         },
494         { &hf_btmesh_gpp_fragment_count,
495             { "Fragment count", "pbadv.gen_prov.gpp.fragment.count",
496                 FT_UINT32, BASE_DEC, NULL, 0x0,
497                 NULL, HFILL }
498         },
499         { &hf_btmesh_gpp_reassembled_length,
500             { "Reassembled Generic Provisioning Payload length", "pbadv.gen_prov.gpp.reassembled.length",
501                 FT_UINT32, BASE_DEC, NULL, 0x0,
502                 "The total length of the reassembled payload", HFILL }
503         },
504     };
505 
506     static gint *ett[] = {
507         &ett_btmesh_pbadv,
508         &ett_btmesh_generic_provisioning,
509         &ett_btmesh_gpp_fragments,
510         &ett_btmesh_gpp_fragment,
511     };
512 
513     static ei_register_info ei[] = {
514         { &ei_btmesh_gpcf_unknown_opcode,{ "pbadv.gpcf.unknown_opcode", PI_PROTOCOL, PI_WARN, "Unknown Opcode", EXPFILL } },
515         { &ei_btmesh_gpcf_unknown_payload,{ "pbadv.gpcf.unknown_payload", PI_PROTOCOL, PI_ERROR, "Unknown Payload", EXPFILL } },
516     };
517 
518     expert_module_t* expert_btmesh_pbadv;
519 
520     proto_btmesh_pbadv = proto_register_protocol("Bluetooth Mesh PB-ADV", "BT Mesh PB-ADV", "pbadv");
521 
522     proto_register_field_array(proto_btmesh_pbadv, hf, array_length(hf));
523     proto_register_subtree_array(ett, array_length(ett));
524 
525     expert_btmesh_pbadv = expert_register_protocol(proto_btmesh_pbadv);
526     expert_register_field_array(expert_btmesh_pbadv, ei, array_length(ei));
527 
528     prefs_register_protocol_subtree("Bluetooth", proto_btmesh_pbadv, NULL);
529     register_dissector("btmesh.pbadv", dissect_btmesh_pbadv_msg, proto_btmesh_pbadv);
530 
531     register_init_routine(&pbadv_init_routine);
532 }
533 
534 void
proto_reg_handoff_btmesh_pbadv(void)535 proto_reg_handoff_btmesh_pbadv(void)
536 {
537     btmesh_provisioning_handle = find_dissector("btmesh.provisioning");
538 }
539 
540 /*
541  * Editor modelines
542  *
543  * Local Variables:
544  * c-basic-offset: 4
545  * tab-width: 8
546  * indent-tabs-mode: nil
547  * End:
548  *
549  * ex: set shiftwidth=4 tabstop=8 expandtab:
550  * :indentSize=4:tabSize=8:noTabs=true:
551  */
552