Expanding Bluetooth Mesh Profile dissector

Added support for Bluetooth Mesh beacons
Added support for Bluetooth Mesh Provisioning protocol
Added support for Bluetooth Mesh Proxy protocol
Added support for Bluetooth Mesh PB-ADV provisioning bearer
Added support for Bluetooth Mesh PB-GATT provisioning bearer

Link to Bluetooth Mesh Profile specification
https://www.bluetooth.org/docman/handlers/downloaddoc.ashx?doc_id=457092

Bug: 15523
Change-Id: I408726c0bc7e1d81077539d451c2047f540dd865
Reviewed-on: https://code.wireshark.org/review/32076
Petri-Dish: Anders Broman <a.broman58@gmail.com>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Piotr Winiarczyk 2018-12-04 19:33:52 +01:00 committed by Anders Broman
parent 8b0e851d4e
commit a8df0c0e13
9 changed files with 2331 additions and 13 deletions

View File

@ -758,6 +758,10 @@ set(DISSECTOR_SRC
${CMAKE_CURRENT_SOURCE_DIR}/packet-btle.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btle_rf.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btmesh.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btmesh-pbadv.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btmesh-provisioning.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btmesh-beacon.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btmesh-proxy.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btmcap.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btrfcomm.c
${CMAKE_CURRENT_SOURCE_DIR}/packet-btsap.c

View File

@ -30,6 +30,7 @@
#include "packet-btsdp.h"
#include "packet-http.h"
#include "packet-usb-hid.h"
#include "packet-btmesh.h"
#define HANDLE_TVB -1
@ -2133,6 +2134,7 @@ static dissector_handle_t http_handle;
static dissector_handle_t usb_hid_boot_keyboard_input_report_handle;
static dissector_handle_t usb_hid_boot_keyboard_output_report_handle;
static dissector_handle_t usb_hid_boot_mouse_input_report_handle;
static dissector_handle_t btmesh_proxy_handle;
static dissector_table_t att_handle_dissector_table;
@ -4582,13 +4584,13 @@ static int
btatt_call_dissector_by_dissector_name_with_data(const char *dissector_name,
tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
dissector_handle_t handle;
dissector_handle_t handle;
handle = find_dissector(dissector_name);
if (handle != NULL)
return call_dissector_with_data(handle, tvb, pinfo, tree, data);
else
REPORT_DISSECTOR_BUG("Dissector %s not registered", dissector_name);
handle = find_dissector(dissector_name);
if (handle != NULL)
return call_dissector_with_data(handle, tvb, pinfo, tree, data);
else
REPORT_DISSECTOR_BUG("Dissector %s not registered", dissector_name);
}
static gint
@ -10350,11 +10352,48 @@ dissect_attribute_value(proto_tree *tree, proto_item *patron_item, packet_info *
}
break;
case 0x2A62: /* Pulse Oximetry Control Point */ /* APPROVED: NO */
case 0x2ADB: /* Mesh Provisioning Data In */
case 0x2ADC: /* Mesh Provisioning Data Out */
case 0x2ADD: /* Mesh Proxy Data In */
case 0x2ADE: /* Mesh Proxy Data Out */
if (btmesh_proxy_handle) {
btle_mesh_proxy_ctx_t *proxy_ctx;
proxy_ctx = wmem_new0(wmem_packet_scope(), btle_mesh_proxy_ctx_t);
proxy_ctx->interface_id = bluetooth_data->interface_id;
proxy_ctx->adapter_id = bluetooth_data->adapter_id;
proxy_ctx->chandle = 0; //TODO
proxy_ctx->bt_uuid = uuid.bt_uuid;
proxy_ctx->access_address = 0; //TODO
switch (att_data->opcode) {
case ATT_OPCODE_WRITE_COMMAND:
proxy_ctx->proxy_side = E_BTMESH_PROXY_SIDE_CLIENT;
break;
case ATT_OPCODE_HANDLE_VALUE_NOTIFICATION:
proxy_ctx->proxy_side = E_BTMESH_PROXY_SIDE_SERVER;
break;
default:
proxy_ctx->proxy_side = E_BTMESH_PROXY_SIDE_UNKNOWN;
break;
}
call_dissector_with_data(btmesh_proxy_handle, tvb_new_subset_length(tvb, offset, length),
pinfo, proto_tree_get_root(tree), proxy_ctx);
offset += length;
} else {
if (bluetooth_gatt_has_no_parameter(att_data->opcode))
break;
proto_tree_add_item(tree, hf_btatt_value, tvb, offset, -1, ENC_NA);
offset = tvb_captured_length(tvb);
}
break;
case 0x2A62: /* Pulse Oximetry Control Point */ /* APPROVED: NO */
case 0x2AE0: /* Average Current */
case 0x2AE1: /* Average Voltage */
case 0x2AE2: /* Boolean */
@ -17290,6 +17329,7 @@ proto_reg_handoff_btatt(void)
usb_hid_boot_keyboard_input_report_handle = find_dissector_add_dependency("usbhid.boot_report.keyboard.input", proto_btatt);
usb_hid_boot_keyboard_output_report_handle = find_dissector_add_dependency("usbhid.boot_report.keyboard.output", proto_btatt);
usb_hid_boot_mouse_input_report_handle = find_dissector_add_dependency("usbhid.boot_report.mouse.input", proto_btatt);
btmesh_proxy_handle = find_dissector_add_dependency("btmesh.proxy", proto_btatt);
dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_ATT, btatt_handle);
dissector_add_uint("btl2cap.cid", BTL2CAP_FIXED_CID_ATT, btatt_handle);

View File

@ -834,7 +834,8 @@ static gint hf_btcommon_le_channel_map_37 = -1;
static gint hf_btcommon_le_channel_map_38 = -1;
static gint hf_btcommon_le_channel_map_39 = -1;
static gint hf_btcommon_eir_ad_mesh_msg = -1;
static gint hf_btcommon_eir_ad_mesh_pbadv = -1;
static gint hf_btcommon_eir_ad_mesh_beacon = -1;
static const int *hfx_btcommon_eir_ad_ips_flags[] = {
&hf_btcommon_eir_ad_ips_flags_reserved,
@ -926,6 +927,8 @@ static dissector_handle_t btcommon_ad_handle;
static dissector_handle_t btcommon_le_channel_map_handle;
static dissector_handle_t bthci_cmd_handle;
static dissector_handle_t btmesh_handle;
static dissector_handle_t btmesh_pbadv_handle;
static dissector_handle_t btmesh_beacon_handle;
static dissector_table_t bluetooth_eir_ad_manufacturer_company_id;
static dissector_table_t bluetooth_eir_ad_tds_organization_id;
@ -7894,13 +7897,32 @@ dissect_eir_ad_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, bluetoo
offset += 1;
break;
case 0x2a:
case 0x29: /* PB-ADV */
if (btmesh_pbadv_handle) {
call_dissector(btmesh_pbadv_handle, tvb_new_subset_length(tvb, offset, length), pinfo, proto_tree_get_root(tree));
} else {
proto_tree_add_item(entry_tree, hf_btcommon_eir_ad_mesh_pbadv, tvb, offset, length, ENC_NA);
}
offset += length;
break;
case 0x2a: /* Mesh Message */
if (btmesh_handle) {
call_dissector(btmesh_handle, tvb_new_subset_length(tvb, offset, length), pinfo, proto_tree_get_root(tree));
} else {
proto_tree_add_item(entry_tree, hf_btcommon_eir_ad_mesh_msg, tvb, offset, length, ENC_NA);
}
offset += length;
break;
case 0x2b: /* Mesh Beacon */
if (btmesh_beacon_handle) {
call_dissector(btmesh_beacon_handle, tvb_new_subset_length(tvb, offset, length), pinfo, proto_tree_get_root(tree));
} else {
proto_tree_add_item(entry_tree, hf_btcommon_eir_ad_mesh_beacon, tvb, offset, length, ENC_NA);
}
offset += length;
break;
case 0xFF: /* Manufacturer Specific */ {
guint16 company_id;
@ -9104,7 +9126,16 @@ proto_register_btcommon(void)
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btcommon_eir_ad_mesh_pbadv,
{ "Mesh PB-ADV message content", "btcommon.eir_ad.entry.mesh_pbadv",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btcommon_eir_ad_mesh_beacon,
{ "Mesh Beacon message content", "btcommon.eir_ad.entry.mesh_beacon",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
};
static gint *ett[] = {
@ -9155,6 +9186,8 @@ void
proto_reg_handoff_btcommon(void)
{
btmesh_handle = find_dissector("btmesh.msg");
btmesh_pbadv_handle = find_dissector("btmesh.pbadv");
btmesh_beacon_handle = find_dissector("btmesh.beacon");
}
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html

View File

@ -0,0 +1,365 @@
/* packet-btmesh-beacon.c
* Routines for Bluetooth mesh PB-ADV dissection
*
* Copyright 2019, Piotr Winiarczyk <wino45@gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Ref: Mesh Profile v1.0
* https://www.bluetooth.com/specifications/mesh-specifications
*/
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include "packet-btmesh.h"
#define BEACON_UNPROVISION 0x00
#define BEACON_SECURE 0x01
void proto_register_btmesh_beacon(void);
static int proto_btmesh_beacon = -1;
static int hf_btmesh_beacon_type = -1;
static int hf_btmesh_beacon_uuid = -1;
static int hf_btmesh_beacon_oob = -1;
static int hf_btmesh_beacon_oob_other = -1;
static int hf_btmesh_beacon_oob_electronic = -1;
static int hf_btmesh_beacon_oob_2d_code = -1;
static int hf_btmesh_beacon_oob_bar_code = -1;
static int hf_btmesh_beacon_oob_nfc= -1;
static int hf_btmesh_beacon_oob_number = -1;
static int hf_btmesh_beacon_oob_string = -1;
static int hf_btmesh_beacon_oob_rfu = -1;
static int hf_btmesh_beacon_oob_on_box = -1;
static int hf_btmesh_beacon_oob_inside_box = -1;
static int hf_btmesh_beacon_oob_on_paper = -1;
static int hf_btmesh_beacon_oob_inside_manual = -1;
static int hf_btmesh_beacon_oob_on_device = -1;
static int hf_btmesh_beacon_uri_hash = -1;
static int hf_btmesh_beacon_flags = -1;
static int hf_btmesh_beacon_flags_key_refresh = -1;
static int hf_btmesh_beacon_flags_iv_update = -1;
static int hf_btmesh_beacon_flags_rfu = -1;
static int hf_btmesh_beacon_network_id = -1;
static int hf_btmesh_beacon_ivindex = -1;
//TODO: check authentication value
static int hf_btmesh_beacon_authentication_value = -1;
static int hf_btmesh_beacon_unknown_data = -1;
static int ett_btmesh_beacon = -1;
static int ett_btmesh_beacon_oob = -1;
static int ett_btmesh_beacon_flags = -1;
static expert_field ei_btmesh_beacon_unknown_beacon_type = EI_INIT;
static expert_field ei_btmesh_beacon_unknown_payload = EI_INIT;
static expert_field ei_btmesh_beacon_rfu_not_zero = EI_INIT;
static const value_string btmesh_beacon_type[] = {
{ 0, "Unprovisioned Device Beacon" },
{ 1, "Secure Network Beacon" },
{ 0, NULL }
};
static const true_false_string available = {
"available",
"not available"
};
static const true_false_string flags_key_refresh = {
"Key Refresh in progress",
"Key Refresh not in progress"
};
static const true_false_string flags_iv_update = {
"IV Update active",
"Normal operation"
};
static gint
dissect_btmesh_beacon_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
proto_item *item, *oob_item, *flags_item;
proto_tree *sub_tree, *oob_tree, *flags_tree;
guint offset = 0;
guint data_size = 0;
btle_mesh_transport_ctx_t *tr_ctx;
btle_mesh_transport_ctx_t dummy_ctx = {E_BTMESH_TR_UNKNOWN, FALSE, 0};
guint16 rfu_bits16;
guint8 rfu_bits8;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "BT Mesh Beacon");
if (data == NULL) {
tr_ctx = &dummy_ctx;
} else {
tr_ctx = (btle_mesh_transport_ctx_t *) data;
}
item = proto_tree_add_item(tree, proto_btmesh_beacon, tvb, offset, -1, ENC_NA);
sub_tree = proto_item_add_subtree(item, ett_btmesh_beacon);
guint8 beacon_type = tvb_get_guint8(tvb, offset);
proto_tree_add_item(sub_tree, hf_btmesh_beacon_type, tvb, offset, 1, ENC_NA);
offset += 1;
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(beacon_type, btmesh_beacon_type, "Unknown Beacon Type"));
if (tr_ctx->fragmented) {
switch (tr_ctx->transport) {
case E_BTMESH_TR_PROXY:
col_append_str(pinfo->cinfo, COL_INFO," (Last Segment)");
break;
default:
//No default is needed since this is an additional information only
break;
}
}
switch(beacon_type) {
case BEACON_UNPROVISION:
proto_tree_add_item(sub_tree, hf_btmesh_beacon_uuid, tvb, offset, 16, ENC_NA);
offset += 16;
oob_item = proto_tree_add_item(sub_tree, hf_btmesh_beacon_oob, tvb, offset, 2, ENC_BIG_ENDIAN);
oob_tree = proto_item_add_subtree(oob_item, ett_btmesh_beacon_oob);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_other, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_electronic, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_2d_code, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_bar_code, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_nfc, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_number, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_string, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_rfu, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_on_box, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_inside_box, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_on_paper, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_inside_manual, tvb, offset, 2, ENC_NA);
proto_tree_add_item(oob_tree, hf_btmesh_beacon_oob_on_device, tvb, offset, 2, ENC_NA);
rfu_bits16 = (tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN) & 0x0780) >> 7;
if (rfu_bits16 != 0) {
//RFU bits should be 0
proto_tree_add_expert(oob_tree, pinfo, &ei_btmesh_beacon_rfu_not_zero, tvb, offset, -1);
}
offset += 2;
data_size = tvb_reported_length(tvb);
if (data_size == offset + 4 ) {
proto_tree_add_item(sub_tree, hf_btmesh_beacon_uri_hash, tvb, offset, 4, ENC_NA);
offset += 4;
}
//Wrong size handled outside switch/case
break;
case BEACON_SECURE:
flags_item = proto_tree_add_item(sub_tree, hf_btmesh_beacon_flags, tvb, offset, 1, ENC_NA);
flags_tree = proto_item_add_subtree(flags_item, ett_btmesh_beacon_flags);
proto_tree_add_item(flags_tree, hf_btmesh_beacon_flags_key_refresh, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_btmesh_beacon_flags_iv_update, tvb, offset, 1, ENC_NA);
proto_tree_add_item(flags_tree, hf_btmesh_beacon_flags_rfu, tvb, offset, 1, ENC_NA);
rfu_bits8 = tvb_get_guint8(tvb, offset) >> 2;
if (rfu_bits8 != 0) {
//RFU bits should be 0
proto_tree_add_expert(flags_tree, pinfo, &ei_btmesh_beacon_rfu_not_zero, tvb, offset, -1);
}
offset += 1;
proto_tree_add_item(sub_tree, hf_btmesh_beacon_network_id, tvb, offset, 8, ENC_NA);
offset += 8;
proto_tree_add_item(sub_tree, hf_btmesh_beacon_ivindex, tvb, offset, 4, ENC_NA);
offset += 4;
proto_tree_add_item(sub_tree, hf_btmesh_beacon_authentication_value, tvb, offset, 8, ENC_NA);
offset += 8;
break;
default:
//Unknown mesh beacon type, display data and flag it
proto_tree_add_item(sub_tree, hf_btmesh_beacon_unknown_data, tvb, offset, -1, ENC_NA);
proto_tree_add_expert(sub_tree, pinfo, &ei_btmesh_beacon_unknown_beacon_type, tvb, offset, -1);
offset += tvb_captured_length_remaining(tvb, offset);
break;
}
//There is still some data but all data should be already disssected
if (tvb_captured_length_remaining(tvb, offset) != 0) {
proto_tree_add_expert(sub_tree, pinfo, &ei_btmesh_beacon_unknown_payload, tvb, offset, -1);
}
return tvb_reported_length(tvb);
}
void
proto_register_btmesh_beacon(void)
{
static hf_register_info hf[] = {
{ &hf_btmesh_beacon_type,
{ "Type", "beacon.type",
FT_UINT8, BASE_DEC, VALS(btmesh_beacon_type), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_beacon_uuid,
{ "Device UUID", "beacon.uuid",
FT_GUID, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob,
{ "OOB Information", "beacon.oob",
FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_other,
{ "Other", "beacon.oob.other",
FT_BOOLEAN, 16, TFS(&available), 0x0001,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_electronic,
{ "Electronic / URI", "beacon.oob.electronic",
FT_BOOLEAN, 16, TFS(&available), 0x0002,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_2d_code,
{ "2D machine-readable code", "beacon.oob.2d_code",
FT_BOOLEAN, 16, TFS(&available), 0x0004,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_bar_code,
{ "Bar code", "beacon.oob.bar_code",
FT_BOOLEAN, 16, TFS(&available), 0x0008,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_nfc,
{ "Near Field Communication (NFC)", "beacon.oob.nfc",
FT_BOOLEAN, 16, TFS(&available), 0x0010,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_number,
{ "Number", "beacon.oob.number",
FT_BOOLEAN, 16, TFS(&available), 0x0020,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_string,
{ "String", "beacon.oob.string",
FT_BOOLEAN, 16, TFS(&available), 0x0040,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_rfu,
{ "Reserved for Future Use", "beacon.oob.rfu",
FT_UINT16, BASE_DEC, NULL, 0x0780,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_on_box,
{ "On box", "beacon.oob.on_box",
FT_BOOLEAN, 16, TFS(&available), 0x0800,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_inside_box,
{ "Inside box", "beacon.oob.inside_box",
FT_BOOLEAN, 16, TFS(&available), 0x1000,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_on_paper,
{ "On piece of paper", "beacon.oob.on_paper",
FT_BOOLEAN, 16, TFS(&available), 0x2000,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_inside_manual,
{ "Inside manual", "beacon.oob.inside_manual",
FT_BOOLEAN, 16, TFS(&available), 0x4000,
NULL, HFILL }
},
{ &hf_btmesh_beacon_oob_on_device,
{ "On device", "beacon.oob.on_device",
FT_BOOLEAN, 16, TFS(&available), 0x8000,
NULL, HFILL }
},
{ &hf_btmesh_beacon_uri_hash,
{ "URI Hash", "beacon.uri_hash",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_beacon_flags,
{ "Flags", "beacon.flags",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_beacon_flags_key_refresh,
{ "Key Refresh Flag", "beacon.flags.key_refresh",
FT_BOOLEAN, 8, TFS(&flags_key_refresh), 0x01,
NULL, HFILL }
},
{ &hf_btmesh_beacon_flags_iv_update,
{ "IV Update Flag", "beacon.flags.iv_update",
FT_BOOLEAN, 8, TFS(&flags_iv_update), 0x02,
NULL, HFILL }
},
{ &hf_btmesh_beacon_flags_rfu,
{ "Reserved for Future Use", "beacon.flags.rfu",
FT_UINT8, BASE_DEC, NULL, 0xFC,
NULL, HFILL }
},
{ &hf_btmesh_beacon_network_id,
{ "Network ID", "beacon.network_id",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_beacon_ivindex,
{ "IV Index", "beacon.ivindex",
FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_beacon_authentication_value,
{ "Authentication Value", "beacon.authentication_value",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_beacon_unknown_data,
{ "Unknown Data", "beacon.unknown_data",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
};
static gint *ett[] = {
&ett_btmesh_beacon,
&ett_btmesh_beacon_oob,
&ett_btmesh_beacon_flags,
};
static ei_register_info ei[] = {
{ &ei_btmesh_beacon_unknown_beacon_type,{ "beacon.unknown_beacon_type", PI_PROTOCOL, PI_ERROR, "Unknown Beacon Type", EXPFILL } },
{ &ei_btmesh_beacon_unknown_payload,{ "beacon.unknown_payload", PI_PROTOCOL, PI_ERROR, "Unknown Payload", EXPFILL } },
{ &ei_btmesh_beacon_rfu_not_zero,{ "beacon.rfu_not_zero", PI_PROTOCOL, PI_WARN, "Reserved for Future Use value not equal to 0", EXPFILL } },
};
expert_module_t* expert_btmesh_beacon;
proto_btmesh_beacon = proto_register_protocol("Bluetooth Mesh Beacon", "BT Mesh beacon", "beacon");
proto_register_field_array(proto_btmesh_beacon, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_btmesh_beacon = expert_register_protocol(proto_btmesh_beacon);
expert_register_field_array(expert_btmesh_beacon, ei, array_length(ei));
prefs_register_protocol_subtree("Bluetooth", proto_btmesh_beacon, NULL);
register_dissector("btmesh.beacon", dissect_btmesh_beacon_msg, proto_btmesh_beacon);
}
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -0,0 +1,549 @@
/* packet-btmesh-pbadv.c
* Routines for Bluetooth mesh PB-ADV dissection
*
* Copyright 2019, Piotr Winiarczyk <wino45@gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Ref: Mesh Profile v1.0
* https://www.bluetooth.com/specifications/mesh-specifications
*/
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/reassemble.h>
#include <epan/expert.h>
#include "packet-btmesh.h"
#define BTMESH_PB_ADV_NOT_USED 0
#define TRANSACTION_START 0x00
#define TRANSACTION_ACKNOWLEDGMENT 0x01
#define TRANSACTION_CONTINUATION 0x02
#define PROVISIONING_BEARER_CONTROL 0x03
#define LINK_OPEN 0x00
#define LINK_ACK 0x01
#define LINK_CLOSE 0x02
void proto_register_btmesh_pbadv(void);
static int proto_btmesh_pbadv = -1;
static dissector_handle_t btmesh_provisioning_handle;
static int hf_btmesh_pbadv_linkid = -1;
static int hf_btmesh_pbadv_trnumber = -1;
static int hf_btmesh_generic_provisioning_control_format = -1;
static int hf_btmesh_gpcf_segn = -1;
static int hf_btmesh_gpcf_total_length = -1;
//TODO - check FCS
static int hf_btmesh_gpcf_fcs = -1;
static int hf_btmesh_gpcf_padding = -1;
static int hf_btmesh_gpcf_segment_index = -1;
static int hf_btmesh_gpcf_bearer_opcode = -1;
static int hf_btmesh_gpcf_bearer_opcode_device_UUID = -1;
static int hf_btmesh_gpcf_bearer_opcode_reason = -1;
static int hf_btmesh_gpcf_bearer_unknown_data = -1;
static int hf_btmesh_gpp_payload = -1;
static int hf_btmesh_gpp_payload_fragment = -1;
static int hf_btmesh_gpp_fragments = -1;
static int hf_btmesh_gpp_fragment = -1;
static int hf_btmesh_gpp_fragment_overlap = -1;
static int hf_btmesh_gpp_fragment_overlap_conflict = -1;
static int hf_btmesh_gpp_fragment_multiple_tails = -1;
static int hf_btmesh_gpp_fragment_too_long_fragment = -1;
static int hf_btmesh_gpp_fragment_error = -1;
static int hf_btmesh_gpp_fragment_count = -1;
static int hf_btmesh_gpp_reassembled_length = -1;
static int ett_btmesh_pbadv = -1;
static int ett_btmesh_generic_provisioning = -1;
static int ett_btmesh_gpp_fragments = -1;
static int ett_btmesh_gpp_fragment = -1;
static expert_field ei_btmesh_gpcf_unknown_opcode = EI_INIT;
static expert_field ei_btmesh_gpcf_unknown_payload = EI_INIT;
static const fragment_items btmesh_gpp_frag_items = {
&ett_btmesh_gpp_fragments,
&ett_btmesh_gpp_fragment,
&hf_btmesh_gpp_fragments,
&hf_btmesh_gpp_fragment,
&hf_btmesh_gpp_fragment_overlap,
&hf_btmesh_gpp_fragment_overlap_conflict,
&hf_btmesh_gpp_fragment_multiple_tails,
&hf_btmesh_gpp_fragment_too_long_fragment,
&hf_btmesh_gpp_fragment_error,
&hf_btmesh_gpp_fragment_count,
NULL,
&hf_btmesh_gpp_reassembled_length,
/* Reassembled data field */
NULL,
"fragments"
};
static const value_string btmesh_generic_provisioning_control_format[] = {
{ 0, "Transaction Start" },
{ 1, "Transaction Acknowledgment" },
{ 2, "Transaction Continuation" },
{ 3, "Provisioning Bearer Control" },
{ 0, NULL }
};
static const value_string btmesh_gpcf_bearer_opcode_format[] = {
{ 0, "Link Open" },
{ 1, "Link ACK" },
{ 2, "Link Close" },
{ 0, NULL }
};
static const value_string btmesh_gpcf_bearer_opcode_reason_format[] = {
{ 0, "Success" },
{ 1, "Timeout" },
{ 2, "Fail" },
{ 0, NULL }
};
/* needed for packet reassembly */
static reassembly_table pbadv_reassembly_table;
typedef struct _pbadv_fragment_key {
guint32 link_id;
guint8 transaction_number;
} pbadv_fragment_key;
static guint
pbadv_fragment_hash(gconstpointer k)
{
const pbadv_fragment_key* key = (const pbadv_fragment_key*) k;
guint hash_val;
hash_val = 0;
hash_val += key->link_id;
hash_val += key->transaction_number;
return hash_val;
}
static gint
pbadv_fragment_equal(gconstpointer k1, gconstpointer k2)
{
const pbadv_fragment_key* key1 = (const pbadv_fragment_key*) k1;
const pbadv_fragment_key* key2 = (const pbadv_fragment_key*) k2;
return ((key1->link_id == key2->link_id) && (key1->transaction_number == key2->transaction_number)
? TRUE : FALSE);
}
static void *
pbadv_fragment_temporary_key(const packet_info *pinfo _U_, const guint32 id _U_,
const void *data)
{
pbadv_fragment_key *key = g_slice_new(pbadv_fragment_key);
const pbadv_fragment_key *pbadv = (const pbadv_fragment_key *)data;
key->link_id = pbadv->link_id;
key->transaction_number = pbadv->transaction_number;
return key;
}
static void
pbadv_fragment_free_temporary_key(gpointer ptr)
{
pbadv_fragment_key *key = (pbadv_fragment_key *)ptr;
g_slice_free(pbadv_fragment_key, key);
}
static void *
pbadv_fragment_persistent_key(const packet_info *pinfo _U_, const guint32 id _U_,
const void *data)
{
pbadv_fragment_key *key = g_slice_new(pbadv_fragment_key);
const pbadv_fragment_key *pbadv = (const pbadv_fragment_key *)data;
key->link_id = pbadv->link_id;
key->transaction_number = pbadv->transaction_number;
return key;
}
static void
pbadv_fragment_free_persistent_key(gpointer ptr)
{
pbadv_fragment_key *key = (pbadv_fragment_key *)ptr;
if (key) {
g_slice_free(pbadv_fragment_key, key);
}
}
static const reassembly_table_functions pbadv_reassembly_table_functions = {
pbadv_fragment_hash,
pbadv_fragment_equal,
pbadv_fragment_temporary_key,
pbadv_fragment_persistent_key,
pbadv_fragment_free_temporary_key,
pbadv_fragment_free_persistent_key
};
static gint
dissect_btmesh_pbadv_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
{
proto_item *item;
proto_tree *sub_tree, *sub_tree_generic_provisioning;
proto_item *ti;
gboolean defragment = FALSE;
int offset = 0;
btle_mesh_transport_ctx_t tr_ctx;
guint8 segn, length;
guint32 total_length;
guint8 gpcf_bearer_opcode;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "BT Mesh PB-ADV");
item = proto_tree_add_item(tree, proto_btmesh_pbadv, tvb, offset, -1, ENC_NA);
sub_tree = proto_item_add_subtree(item, ett_btmesh_pbadv);
guint32 pbadv_link_id = tvb_get_guint32(tvb, offset, ENC_BIG_ENDIAN);
proto_tree_add_item(sub_tree, hf_btmesh_pbadv_linkid, tvb, offset, 4, ENC_NA);
offset += 4;
guint8 pbadv_trnumber = tvb_get_guint8(tvb, offset);
proto_tree_add_item(sub_tree, hf_btmesh_pbadv_trnumber, tvb, offset, 1, ENC_NA);
offset += 1;
pbadv_fragment_key frg_key;
frg_key.link_id = pbadv_link_id;
frg_key.transaction_number = pbadv_trnumber;
sub_tree_generic_provisioning = proto_tree_add_subtree(sub_tree, tvb, offset, -1, ett_btmesh_generic_provisioning, &ti, "Generic Provisioning PDU");
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_generic_provisioning_control_format, tvb, offset, 1, ENC_NA);
guint8 gpcf = tvb_get_guint8(tvb, offset) & 0x03;
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(gpcf, btmesh_generic_provisioning_control_format, "Unknown PDU"));
fragment_head *fd_head = NULL;
gint segment_index = -1;
switch (gpcf) {
//Transaction Start
case TRANSACTION_START:
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_segn, tvb, offset, 1, ENC_NA);
segn = (tvb_get_guint8(tvb, offset) & 0xFC) >> 2;
offset += 1;
total_length = (guint32)tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN);
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_total_length, tvb, offset, 2, ENC_NA);
offset += 2;
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_fcs, tvb, offset, 1, ENC_NA);
offset += 1;
segment_index = 0;
defragment = TRUE;
if (segn == 0) {
if (btmesh_provisioning_handle) {
length = tvb_reported_length(tvb);
tr_ctx.transport = E_BTMESH_TR_ADV;
tr_ctx.fragmented = FALSE;
tr_ctx.segment_index = 0;
call_dissector_with_data(btmesh_provisioning_handle, tvb_new_subset_length(tvb, offset, length),
pinfo, proto_tree_get_root(sub_tree_generic_provisioning), &tr_ctx);
} else {
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpp_payload, tvb, offset, -1, ENC_NA);
}
} else {
//Segmentation
if (!pinfo->fd->visited) {
//First fragment can be delivered out of order, and can be the last one.
fd_head = fragment_get(&pbadv_reassembly_table, pinfo, BTMESH_PB_ADV_NOT_USED, &frg_key);
if (fd_head) {
fragment_set_tot_len(&pbadv_reassembly_table, pinfo, BTMESH_PB_ADV_NOT_USED, &frg_key, total_length);
}
fd_head = fragment_add(&pbadv_reassembly_table,
tvb, offset, pinfo,
BTMESH_PB_ADV_NOT_USED, &frg_key,
0,
tvb_captured_length_remaining(tvb, offset),
TRUE);
if (!fd_head) {
//Set the length only when not reassembled
fragment_set_tot_len(&pbadv_reassembly_table, pinfo, BTMESH_PB_ADV_NOT_USED, &frg_key, total_length);
}
} else {
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpp_payload_fragment, tvb, offset, -1, ENC_NA);
}
}
break;
//Transaction Acknowledgment
case TRANSACTION_ACKNOWLEDGMENT:
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_padding, tvb, offset, 1, ENC_NA);
break;
//Transaction Continuation
case TRANSACTION_CONTINUATION:
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_segment_index, tvb, offset, 1, ENC_NA);
segment_index = (tvb_get_guint8(tvb, offset) & 0xFC) >> 2;
defragment = TRUE;
offset += 1;
//Segmentation
if (!pinfo->fd->visited) {
fd_head = fragment_add(&pbadv_reassembly_table,
tvb, offset, pinfo,
BTMESH_PB_ADV_NOT_USED, &frg_key,
20 + (segment_index - 1) * 23,
tvb_captured_length_remaining(tvb, offset),
TRUE);
} else {
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpp_payload_fragment, tvb, offset, -1, ENC_NA);
}
break;
//Provisioning Bearer Control
case PROVISIONING_BEARER_CONTROL:
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_bearer_opcode, tvb, offset, 1, ENC_NA);
gpcf_bearer_opcode = (tvb_get_guint8(tvb, offset) & 0xFC) >> 2;
offset += 1;
switch(gpcf_bearer_opcode) {
case LINK_OPEN:
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_bearer_opcode_device_UUID, tvb, offset, 16, ENC_NA);
offset += 16;
break;
case LINK_ACK:
//No data in this PDU
break;
case LINK_CLOSE:
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_bearer_opcode_reason, tvb, offset, 1, ENC_NA);
offset += 1;
break;
default:
//Unknown data
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpcf_bearer_unknown_data, tvb, offset, -1, ENC_NA);
offset += tvb_captured_length_remaining(tvb, offset);
proto_tree_add_expert(sub_tree, pinfo, &ei_btmesh_gpcf_unknown_opcode, tvb, offset, -1);
break;
}
//There is still some data but all data should be already disssected
if (tvb_captured_length_remaining(tvb, offset) != 0) {
proto_tree_add_expert(sub_tree, pinfo, &ei_btmesh_gpcf_unknown_payload, tvb, offset, -1);
}
break;
}
//Second pass
if (pinfo->fd->visited && defragment ) {
fd_head = fragment_get(&pbadv_reassembly_table, pinfo, BTMESH_PB_ADV_NOT_USED, &frg_key);
if (fd_head && (fd_head->flags&FD_DEFRAGMENTED)) {
tvbuff_t *next_tvb;
next_tvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled Provisioning PDU", fd_head, &btmesh_gpp_frag_items, NULL, sub_tree_generic_provisioning);
if (next_tvb) {
col_append_str(pinfo->cinfo, COL_INFO, " (Message Reassembled)");
if (btmesh_provisioning_handle) {
tr_ctx.transport = E_BTMESH_TR_ADV;
tr_ctx.fragmented = TRUE;
tr_ctx.segment_index = segment_index;
call_dissector_with_data(btmesh_provisioning_handle, next_tvb, pinfo,
proto_tree_get_root(sub_tree_generic_provisioning), &tr_ctx);
} else {
proto_tree_add_item(sub_tree_generic_provisioning, hf_btmesh_gpp_payload, next_tvb, 0, -1, ENC_NA);
}
} else {
col_append_fstr(pinfo->cinfo, COL_INFO," (Message fragment %u)", segment_index);
}
}
}
return tvb_reported_length(tvb);
}
static void
pbadv_init_routine(void)
{
reassembly_table_register(&pbadv_reassembly_table, &pbadv_reassembly_table_functions);
}
void
proto_register_btmesh_pbadv(void)
{
static hf_register_info hf[] = {
//PB-ADV
{ &hf_btmesh_pbadv_linkid,
{ "Link ID", "pbadv.linkid",
FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_pbadv_trnumber,
{ "Transaction Number", "pbadv.trnumber",
FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
//Generic Provisioning Control
{ &hf_btmesh_generic_provisioning_control_format,
{ "Generic Provisioning Control Format", "pbadv.gen_prov.gpcf",
FT_UINT8, BASE_DEC, VALS(btmesh_generic_provisioning_control_format), 0x03,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_segn,
{ "The last segment number", "pbadv.gen_prov.gpcf.segn",
FT_UINT8, BASE_DEC, NULL, 0xFC,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_total_length,
{ "Total Length", "pbadv.gen_prov.gpcf.total_length",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_fcs,
{ "Frame Check Sequence", "pbadv.gen_prov.gpcf.fcs",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_padding,
{ "Padding", "pbadv.gen_prov.gpcf.padding",
FT_UINT8, BASE_DEC, NULL, 0xFC,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_segment_index,
{ "Segment number of the transaction", "pbadv.gen_prov.gpcf.segment_index",
FT_UINT8, BASE_DEC, NULL, 0xFC,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_bearer_opcode,
{ "Bearer Opcode", "pbadv.gen_prov.gpcf.bearer_opcode",
FT_UINT8, BASE_DEC, VALS(btmesh_gpcf_bearer_opcode_format), 0xFC,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_bearer_opcode_device_UUID,
{ "Device UUID", "pbadv.gen_prov.gpcf.bearer_opcode.device_uuid",
FT_GUID, BASE_NONE, NULL, 0x00,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_bearer_opcode_reason,
{ "Reason", "pbadv.gen_prov.gpcf.bearer_opcode.reason",
FT_UINT8, BASE_DEC, VALS(btmesh_gpcf_bearer_opcode_reason_format), 0x00,
NULL, HFILL }
},
{ &hf_btmesh_gpcf_bearer_unknown_data,
{ "Unknown Data", "pbadv.gen_prov.gpcf.unknown_data",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
//Generic Provisioning Payload
{ &hf_btmesh_gpp_payload,
{ "Generic Provisioning Payload", "pbadv.gen_prov.gpp.payload",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_gpp_payload_fragment,
{ "Generic Provisioning Payload Fragment", "pbadv.gen_prov.gpp.payload.fragment",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
//Generic Provisioning Payload Reassembly
{ &hf_btmesh_gpp_fragments,
{ "Reassembled Generic Provisioning Payload Fragments", "pbadv.gen_prov.gpp.fragments",
FT_NONE, BASE_NONE, NULL, 0x0,
"Generic Provisioning Payload Fragments", HFILL }
},
{ &hf_btmesh_gpp_fragment,
{ "Generic Provisioning Payload Fragment", "pbadv.gen_prov.gpp.fragment",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_gpp_fragment_overlap,
{ "Fragment overlap", "pbadv.gen_prov.gpp.fragment.overlap",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Fragment overlaps with other fragments", HFILL }
},
{ &hf_btmesh_gpp_fragment_overlap_conflict,
{ "Conflicting data in fragment overlap", "pbadv.gen_prov.gpp.fragment.overlap.conflict",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Overlapping fragments contained conflicting data", HFILL }
},
{ &hf_btmesh_gpp_fragment_multiple_tails,
{ "Multiple tail fragments found", "pbadv.gen_prov.gpp.fragment.multipletails",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Several tails were found when defragmenting the packet", HFILL }
},
{ &hf_btmesh_gpp_fragment_too_long_fragment,
{ "Fragment too long", "pbadv.gen_prov.gpp.fragment.toolongfragment",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Fragment contained data past end of packet", HFILL }
},
{ &hf_btmesh_gpp_fragment_error,
{ "Defragmentation error", "pbadv.gen_prov.gpp.fragment.error",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"Defragmentation error due to illegal fragments", HFILL }
},
{ &hf_btmesh_gpp_fragment_count,
{ "Fragment count", "pbadv.gen_prov.gpp.fragment.count",
FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_gpp_reassembled_length,
{ "Reassembled Generic Provisioning Payload length", "pbadv.gen_prov.gpp.reassembled.length",
FT_UINT32, BASE_DEC, NULL, 0x0,
"The total length of the reassembled payload", HFILL }
},
};
static gint *ett[] = {
&ett_btmesh_pbadv,
&ett_btmesh_generic_provisioning,
&ett_btmesh_gpp_fragments,
&ett_btmesh_gpp_fragment,
};
static ei_register_info ei[] = {
{ &ei_btmesh_gpcf_unknown_opcode,{ "pbadv.gpcf.unknown_opcode", PI_PROTOCOL, PI_WARN, "Unknown Opcode", EXPFILL } },
{ &ei_btmesh_gpcf_unknown_payload,{ "pbadv.gpcf.unknown_payload", PI_PROTOCOL, PI_ERROR, "Unknown Payload", EXPFILL } },
};
expert_module_t* expert_btmesh_pbadv;
proto_btmesh_pbadv = proto_register_protocol("Bluetooth Mesh PB-ADV", "BT Mesh PB-ADV", "pbadv");
proto_register_field_array(proto_btmesh_pbadv, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_btmesh_pbadv = expert_register_protocol(proto_btmesh_pbadv);
expert_register_field_array(expert_btmesh_pbadv, ei, array_length(ei));
/*module_t *btmesh_pbadv_module;
btmesh_pbadv_module = */prefs_register_protocol_subtree("Bluetooth", proto_btmesh_pbadv, NULL);
register_dissector("btmesh.pbadv", dissect_btmesh_pbadv_msg, proto_btmesh_pbadv);
register_init_routine(&pbadv_init_routine);
}
void
proto_reg_handoff_btmesh_pbadv(void)
{
btmesh_provisioning_handle = find_dissector("btmesh.provisioning");
}
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -0,0 +1,866 @@
/* packet-btmesh-provisioning.c
* Routines for Bluetooth mesh Provisioning PDU dissection
*
* Copyright 2019, Piotr Winiarczyk <wino45@gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Ref: Mesh Profile v1.0
* https://www.bluetooth.com/specifications/mesh-specifications
*/
#include "tfs.h"
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include "packet-btmesh.h"
#define PROVISIONING_INVITE_PDU 0x00
#define PROVISIONING_CAPABILITIES_PDU 0x01
#define PROVISIONING_START_PDU 0x02
#define PROVISIONING_PUBLIC_KEY_PDU 0x03
#define PROVISIONING_INPUT_COMPLETE_PDU 0x04
#define PROVISIONING_CONFIRMATION_PDU 0x05
#define PROVISIONING_RANDOM_PDU 0x06
#define PROVISIONING_DATA_PDU 0x07
#define PROVISIONING_COMPLETE_PDU 0x08
#define PROVISIONING_FAILED_PDU 0x09
#define NO_OOB_AUTHENTICATION_IS_USED 0x00
#define STATIC_OOB_AUTHENTICATION_IS_USED 0x01
#define OUTPUT_OOB_AUTHENTICATION_IS_USED 0x02
#define INPUT_OOB_AUTHENTICATION_IS_USED 0x03
void proto_register_btmesh_provisioning(void);
static int proto_btmesh_provisioning = -1;
static int hf_btmesh_provisioning_pdu_type = -1;
static int hf_btmesh_provisioning_pdu_padding = -1;
static int hf_btmesh_provisioning_attention_duration = -1;
static int hf_btmesh_provisioning_number_of_elements = -1;
static int hf_btmesh_provisioning_algorithms = -1;
static int hf_btmesh_provisioning_algorithms_p256 = -1;
static int hf_btmesh_provisioning_algorithms_rfu = -1;
static int hf_btmesh_provisioning_public_key_type = -1;
static int hf_btmesh_provisioning_public_key_type_oob = -1;
static int hf_btmesh_provisioning_public_key_type_rfu = -1;
static int hf_btmesh_provisioning_static_oob_type = -1;
static int hf_btmesh_provisioning_static_oob_type_static_oob_available = -1;
static int hf_btmesh_provisioning_static_oob_type_rfu = -1;
static int hf_btmesh_provisioning_output_oob_size = -1;
static int hf_btmesh_provisioning_output_oob_action = -1;
static int hf_btmesh_provisioning_output_oob_action_blink = -1;
static int hf_btmesh_provisioning_output_oob_action_beep = -1;
static int hf_btmesh_provisioning_output_oob_action_vibrate = -1;
static int hf_btmesh_provisioning_output_oob_action_output_numeric = -1;
static int hf_btmesh_provisioning_output_oob_action_output_alphanumeric = -1;
static int hf_btmesh_provisioning_output_oob_action_output_rfu = -1;
static int hf_btmesh_provisioning_input_oob_size = -1;
static int hf_btmesh_provisioning_input_oob_action = -1;
static int hf_btmesh_provisioning_input_oob_action_push = -1;
static int hf_btmesh_provisioning_input_oob_action_twist = -1;
static int hf_btmesh_provisioning_input_oob_action_input_numeric = -1;
static int hf_btmesh_provisioning_input_oob_action_input_alphanumeric = -1;
static int hf_btmesh_provisioning_input_oob_action_rfu = -1;
static int hf_btmesh_provisioning_algorithm = -1;
static int hf_btmesh_provisioning_public_key = -1;
static int hf_btmesh_provisioning_authentication_method = -1;
static int hf_btmesh_provisioning_authentication_action_no_oob_action = -1;
static int hf_btmesh_provisioning_authentication_action_static_oob_action = -1;
static int hf_btmesh_provisioning_authentication_action_output_oob_action = -1;
static int hf_btmesh_provisioning_authentication_action_input_oob_action = -1;
static int hf_btmesh_provisioning_authentication_size_no_oob_action = -1;
static int hf_btmesh_provisioning_authentication_size_static_oob_action = -1;
static int hf_btmesh_provisioning_authentication_size_output_oob_action = -1;
static int hf_btmesh_provisioning_authentication_size_input_oob_action = -1;
static int hf_btmesh_provisioning_public_key_x = -1;
static int hf_btmesh_provisioning_public_key_y = -1;
static int hf_btmesh_provisioning_confirmation = -1;
static int hf_btmesh_provisioning_random = -1;
static int hf_btmesh_provisioning_encrypted_provisioning_data = -1;
static int hf_btmesh_provisioning_decrypted_provisioning_data_mic = -1;
static int hf_btmesh_provisioning_error_code = -1;
static int hf_btmesh_provisioning_unknown_data = -1;
static int ett_btmesh_provisioning = -1;
static int ett_btmesh_provisioning_algorithms = -1;
static int ett_btmesh_provisioning_public_key_type = -1;
static int ett_btmesh_provisioning_static_oob_type = -1;
static int ett_btmesh_provisioning_output_oob_action = -1;
static int ett_btmesh_provisioning_output_oob_size = -1;
static int ett_btmesh_provisioning_input_oob_action = -1;
static int ett_btmesh_provisioning_input_oob_size = -1;
static int ett_btmesh_provisioning_algorithm = -1;
static int ett_btmesh_provisioning_public_key = -1;
static int ett_btmesh_provisioning_authentication_method = -1;
static int ett_btmesh_provisioning_authentication_action = -1;
static int ett_btmesh_provisioning_authentication_size = -1;
static int ett_btmesh_provisioning_error_code = -1;
static expert_field ei_btmesh_provisioning_unknown_opcode = EI_INIT;
static expert_field ei_btmesh_provisioning_unknown_payload = EI_INIT;
static expert_field ei_btmesh_provisioning_unknown_authentication_method = EI_INIT;
static expert_field ei_btmesh_provisioning_rfu_not_zero = EI_INIT;
static expert_field ei_btmesh_provisioning_in_rfu_range = EI_INIT;
static expert_field ei_btmesh_provisioning_prohibited = EI_INIT;
static expert_field ei_btmesh_provisioning_zero_elements = EI_INIT;
static const value_string btmesh_provisioning_pdu_type_format[] = {
{ 0, "Provisioning Invite PDU" },
{ 1, "Provisioning Capabilities PDU" },
{ 2, "Provisioning Start PDU" },
{ 3, "Provisioning Public Key PDU" },
{ 4, "Provisioning Input Complete PDU" },
{ 5, "Provisioning Confirmation PDU" },
{ 6, "Provisioning Random PDU" },
{ 7, "Provisioning Data PDU" },
{ 8, "Provisioning Complete PDU" },
{ 9, "Provisioning Failed PDU" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_error_code_format[] = {
{ 0, "Prohibited" },
{ 1, "Invalid PDU" },
{ 2, "Invalid Format" },
{ 3, "Unexpected PDU" },
{ 4, "Confirmation Failed" },
{ 5, "Out of Resources" },
{ 6, "Decryption Failed" },
{ 7, "Unexpected Error" },
{ 8, "Cannot Assign Addresses" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_algorithm_format[] = {
{ 0, "FIPS P-256 Elliptic Curve" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_public_key_format[] = {
{ 0, "No OOB Public Key is used" },
{ 1, "OOB Public Key is used" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_method_format[] = {
{ 0, "No OOB authentication is used" },
{ 1, "Static OOB authentication is used" },
{ 2, "Output OOB authentication is used" },
{ 3, "Input OOB authentication is used" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_action_no_oob_action_format[] = {
{ 0, "None" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_action_static_oob_action_format[] = {
{ 0, "None" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_action_output_oob_action_format[] = {
{ 0, "Blink" },
{ 1, "Beep" },
{ 2, "Vibrate" },
{ 3, "Output Numeric" },
{ 4, "Output Alphanumeric" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_action_input_oob_action_format[] = {
{ 0, "Push" },
{ 1, "Twist" },
{ 2, "Input Numeric" },
{ 3, "Input Alphanumeric" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_size_no_oob_action_format[] = {
{ 0, "None" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_size_static_oob_action_format[] = {
{ 0, "None" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_size_output_oob_action_format[] = {
{ 0, "Prohibited" },
{ 1, "The Output OOB size in characters to be used" },
{ 2, "The Output OOB size in characters to be used" },
{ 3, "The Output OOB size in characters to be used" },
{ 4, "The Output OOB size in characters to be used" },
{ 5, "The Output OOB size in characters to be used" },
{ 6, "The Output OOB size in characters to be used" },
{ 7, "The Output OOB size in characters to be used" },
{ 8, "The Output OOB size in characters to be used" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_authentication_size_input_oob_action_format[] = {
{ 0, "Prohibited" },
{ 1, "The Input OOB size in characters to be used" },
{ 2, "The Input OOB size in characters to be used" },
{ 3, "The Input OOB size in characters to be used" },
{ 4, "The Input OOB size in characters to be used" },
{ 5, "The Input OOB size in characters to be used" },
{ 6, "The Input OOB size in characters to be used" },
{ 7, "The Input OOB size in characters to be used" },
{ 8, "The Input OOB size in characters to be used" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_output_oob_size_format[] = {
{ 0, "The device does not support output OOB" },
{ 1, "Maximum size in octets supported by the device" },
{ 2, "Maximum size in octets supported by the device" },
{ 3, "Maximum size in octets supported by the device" },
{ 4, "Maximum size in octets supported by the device" },
{ 5, "Maximum size in octets supported by the device" },
{ 6, "Maximum size in octets supported by the device" },
{ 7, "Maximum size in octets supported by the device" },
{ 8, "Maximum size in octets supported by the device" },
{ 0, NULL }
};
static const value_string btmesh_provisioning_input_oob_size_format[] = {
{ 0, "The device does not support input OOB" },
{ 1, "Maximum size in octets supported by the device" },
{ 2, "Maximum size in octets supported by the device" },
{ 3, "Maximum size in octets supported by the device" },
{ 4, "Maximum size in octets supported by the device" },
{ 5, "Maximum size in octets supported by the device" },
{ 6, "Maximum size in octets supported by the device" },
{ 7, "Maximum size in octets supported by the device" },
{ 8, "Maximum size in octets supported by the device" },
{ 0, NULL }
};
static gint
dissect_btmesh_provisioning_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
proto_item *item, *algorithms_item, *public_key_type_item;
proto_item *static_oob_type_item, *output_oob_action_item, *input_oob_action_item;
proto_tree *sub_tree, *algorithms_tree, *public_key_type_tree;
proto_tree *static_oob_type_tree, *output_oob_action_tree, *input_oob_action_tree;
proto_item *expert_item;
proto_tree *expert_tree;
int offset = 0;
btle_mesh_transport_ctx_t *tr_ctx;
btle_mesh_transport_ctx_t dummy_ctx = {E_BTMESH_TR_UNKNOWN, FALSE, 0};
guint8 authentication_method, authentication_action, authentication_size;
guint8 provisioning_algorithm;
guint8 prohibited_value, output_oob_size, input_oob_size;
guint16 rfu_uint16;
guint8 no_of_elements;
guint8 error_code;
guint8 provisioning_public_key;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "BT Mesh Provisioning PDU");
if (data == NULL) {
tr_ctx = &dummy_ctx;
} else {
tr_ctx = (btle_mesh_transport_ctx_t *) data;
}
item = proto_tree_add_item(tree, proto_btmesh_provisioning, tvb, offset, -1, ENC_NA);
sub_tree = proto_item_add_subtree(item, ett_btmesh_provisioning);
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_pdu_type, tvb, offset, 1, ENC_NA);
guint8 pdu_type = tvb_get_guint8(tvb, offset) & 0x3F;
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_pdu_padding, tvb, offset, 1, ENC_NA);
guint8 pdu_padding = (tvb_get_guint8(tvb, offset) & 0xC0) >> 6;
if (pdu_padding != 0) {
//Padding should be 0
proto_tree_add_expert(sub_tree, pinfo, &ei_btmesh_provisioning_rfu_not_zero, tvb, offset, -1);
}
offset += 1;
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(pdu_type, btmesh_provisioning_pdu_type_format, "Unknown Provisioning PDU"));
if (tr_ctx->fragmented) {
switch (tr_ctx->transport) {
case E_BTMESH_TR_ADV:
col_append_fstr(pinfo->cinfo, COL_INFO," (Message fragment %u)", tr_ctx->segment_index);
break;
case E_BTMESH_TR_PROXY:
col_append_str(pinfo->cinfo, COL_INFO," (Last Segment)");
break;
default:
//No default is needed since this is an additional information only
break;
}
}
switch(pdu_type) {
case PROVISIONING_INVITE_PDU:
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_attention_duration, tvb, offset, 1, ENC_NA);
offset += 1;
break;
case PROVISIONING_CAPABILITIES_PDU:
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_number_of_elements, tvb, offset, 1, ENC_NA);
no_of_elements = tvb_get_guint8(tvb, offset);
if (no_of_elements == 0) {
proto_tree_add_expert(sub_tree, pinfo, &ei_btmesh_provisioning_zero_elements, tvb, offset, -1);
}
offset += 1;
algorithms_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_algorithms, tvb, offset, 2, ENC_NA);
algorithms_tree = proto_item_add_subtree(algorithms_item, ett_btmesh_provisioning_algorithms);
proto_tree_add_item(algorithms_tree, hf_btmesh_provisioning_algorithms_p256, tvb, offset, 2, ENC_NA);
proto_tree_add_item(algorithms_tree, hf_btmesh_provisioning_algorithms_rfu, tvb, offset, 2, ENC_NA);
rfu_uint16 = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN) >> 1;
if (rfu_uint16 != 0) {
proto_tree_add_expert(algorithms_tree, pinfo, &ei_btmesh_provisioning_rfu_not_zero, tvb, offset, -1);
}
offset += 2;
public_key_type_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_public_key_type, tvb, offset, 1, ENC_NA);
public_key_type_tree = proto_item_add_subtree(public_key_type_item, ett_btmesh_provisioning_public_key_type);
proto_tree_add_item(public_key_type_tree, hf_btmesh_provisioning_public_key_type_oob, tvb, offset, 1, ENC_NA);
proto_tree_add_item(public_key_type_tree, hf_btmesh_provisioning_public_key_type_rfu, tvb, offset, 1, ENC_NA);
prohibited_value = tvb_get_guint8(tvb, offset) >> 1;
if (prohibited_value != 0) {
proto_tree_add_expert(public_key_type_tree, pinfo, &ei_btmesh_provisioning_prohibited, tvb, offset, -1);
}
offset += 1;
static_oob_type_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_static_oob_type, tvb, offset, 1, ENC_NA);
static_oob_type_tree = proto_item_add_subtree(static_oob_type_item, ett_btmesh_provisioning_static_oob_type);
proto_tree_add_item(static_oob_type_tree, hf_btmesh_provisioning_static_oob_type_static_oob_available, tvb, offset, 1, ENC_NA);
proto_tree_add_item(static_oob_type_tree, hf_btmesh_provisioning_static_oob_type_rfu, tvb, offset, 1, ENC_NA);
prohibited_value = tvb_get_guint8(tvb, offset) >> 1;
if (prohibited_value != 0) {
proto_tree_add_expert(static_oob_type_tree, pinfo, &ei_btmesh_provisioning_prohibited, tvb, offset, -1);
}
offset += 1;
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_output_oob_size, tvb, offset, 1, ENC_NA);
output_oob_size = tvb_get_guint8(tvb, offset);
if (output_oob_size >= 9) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_output_oob_size);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
output_oob_action_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_output_oob_action, tvb, offset, 2, ENC_NA);
output_oob_action_tree = proto_item_add_subtree(output_oob_action_item, ett_btmesh_provisioning_output_oob_action);
proto_tree_add_item(output_oob_action_tree, hf_btmesh_provisioning_output_oob_action_blink, tvb, offset, 2, ENC_NA);
proto_tree_add_item(output_oob_action_tree, hf_btmesh_provisioning_output_oob_action_beep, tvb, offset, 2, ENC_NA);
proto_tree_add_item(output_oob_action_tree, hf_btmesh_provisioning_output_oob_action_vibrate, tvb, offset, 2, ENC_NA);
proto_tree_add_item(output_oob_action_tree, hf_btmesh_provisioning_output_oob_action_output_numeric, tvb, offset, 2, ENC_NA);
proto_tree_add_item(output_oob_action_tree, hf_btmesh_provisioning_output_oob_action_output_alphanumeric, tvb, offset, 2, ENC_NA);
proto_tree_add_item(output_oob_action_tree, hf_btmesh_provisioning_output_oob_action_output_rfu, tvb, offset, 2, ENC_NA);
rfu_uint16 = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN) >> 5;
if (rfu_uint16 != 0) {
proto_tree_add_expert(output_oob_action_tree, pinfo, &ei_btmesh_provisioning_rfu_not_zero, tvb, offset, -1);
}
offset += 2;
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_input_oob_size, tvb, offset, 1, ENC_NA);
input_oob_size = tvb_get_guint8(tvb, offset);
if (input_oob_size >= 9) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_input_oob_size);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
input_oob_action_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_input_oob_action, tvb, offset, 2, ENC_NA);
input_oob_action_tree = proto_item_add_subtree(input_oob_action_item, ett_btmesh_provisioning_input_oob_action);
proto_tree_add_item(input_oob_action_tree, hf_btmesh_provisioning_input_oob_action_push, tvb, offset, 2, ENC_NA);
proto_tree_add_item(input_oob_action_tree, hf_btmesh_provisioning_input_oob_action_twist, tvb, offset, 2, ENC_NA);
proto_tree_add_item(input_oob_action_tree, hf_btmesh_provisioning_input_oob_action_input_numeric, tvb, offset, 2, ENC_NA);
proto_tree_add_item(input_oob_action_tree, hf_btmesh_provisioning_input_oob_action_input_alphanumeric, tvb, offset, 2, ENC_NA);
proto_tree_add_item(input_oob_action_tree, hf_btmesh_provisioning_input_oob_action_rfu, tvb, offset, 2, ENC_NA);
rfu_uint16 = tvb_get_guint16(tvb, offset, ENC_BIG_ENDIAN) >> 4;
if (rfu_uint16 != 0) {
proto_tree_add_expert(input_oob_action_tree, pinfo, &ei_btmesh_provisioning_rfu_not_zero, tvb, offset, -1);
}
offset += 2;
break;
case PROVISIONING_START_PDU:
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_algorithm, tvb, offset, 1, ENC_NA);
provisioning_algorithm = tvb_get_guint8(tvb, offset);
if (provisioning_algorithm >= 1) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_algorithm);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_public_key, tvb, offset, 1, ENC_NA);
provisioning_public_key = tvb_get_guint8(tvb, offset);
if (provisioning_public_key >= 2) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_public_key);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_method, tvb, offset, 1, ENC_NA);
authentication_method = tvb_get_guint8(tvb, offset);
authentication_action = 0;
authentication_size = 0;
offset += 1;
switch(authentication_method){
case NO_OOB_AUTHENTICATION_IS_USED:
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_action_no_oob_action, tvb, offset, 1, ENC_NA);
authentication_action = tvb_get_guint8(tvb, offset);
if (authentication_action != 0) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_action);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_size_no_oob_action, tvb, offset, 1, ENC_NA);
authentication_size = tvb_get_guint8(tvb, offset);
if (authentication_size != 0) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_size);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
break;
case STATIC_OOB_AUTHENTICATION_IS_USED:
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_action_static_oob_action, tvb, offset, 1, ENC_NA);
authentication_action = tvb_get_guint8(tvb, offset);
if (authentication_action != 0) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_action);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_size_static_oob_action, tvb, offset, 1, ENC_NA);
authentication_size = tvb_get_guint8(tvb, offset);
if (authentication_size != 0) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_size);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
break;
case OUTPUT_OOB_AUTHENTICATION_IS_USED:
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_action_output_oob_action, tvb, offset, 1, ENC_NA);
authentication_action = tvb_get_guint8(tvb, offset);
if (authentication_action >= 5) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_action);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_size_output_oob_action, tvb, offset, 1, ENC_NA);
authentication_size = tvb_get_guint8(tvb, offset);
if (authentication_size >= 9) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_size);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
} else {
if (authentication_size == 0) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_size);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_prohibited, tvb, offset, -1);
}
}
offset += 1;
break;
case INPUT_OOB_AUTHENTICATION_IS_USED:
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_action_input_oob_action, tvb, offset, 1, ENC_NA);
authentication_action = tvb_get_guint8(tvb, offset);
if (authentication_action >= 4) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_action);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_authentication_size_input_oob_action, tvb, offset, 1, ENC_NA);
authentication_size = tvb_get_guint8(tvb, offset);
if (authentication_size >= 9) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_size);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
} else {
if (authentication_size == 0) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_size);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_prohibited, tvb, offset, -1);
}
}
offset += 1;
break;
default:
//RFU authentication method, display parameters and flag it
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_authentication_method);
proto_tree_add_item(expert_tree, hf_btmesh_provisioning_unknown_data, tvb, offset, -1, ENC_NA);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_unknown_authentication_method, tvb, offset, -1);
offset += tvb_captured_length_remaining(tvb, offset);
break;
}
break;
case PROVISIONING_PUBLIC_KEY_PDU:
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_public_key_x, tvb, offset, 32, ENC_NA);
offset += 32;
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_public_key_y, tvb, offset, 32, ENC_NA);
offset += 32;
break;
case PROVISIONING_INPUT_COMPLETE_PDU:
break;
case PROVISIONING_CONFIRMATION_PDU:
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_confirmation, tvb, offset, 16, ENC_NA);
offset += 16;
break;
case PROVISIONING_RANDOM_PDU:
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_random, tvb, offset, 16, ENC_NA);
offset += 16;
break;
case PROVISIONING_DATA_PDU:
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_encrypted_provisioning_data, tvb, offset, 25, ENC_NA);
offset += 25;
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_decrypted_provisioning_data_mic, tvb, offset, 8, ENC_NA);
offset += 8;
break;
case PROVISIONING_COMPLETE_PDU:
//No parameters for this PDU
break;
case PROVISIONING_FAILED_PDU:
expert_item = proto_tree_add_item(sub_tree, hf_btmesh_provisioning_error_code, tvb, offset, 1, ENC_NA);
error_code = tvb_get_guint8(tvb, offset);
if (error_code >= 9) {
expert_tree = proto_item_add_subtree(expert_item, ett_btmesh_provisioning_error_code);
proto_tree_add_expert(expert_tree, pinfo, &ei_btmesh_provisioning_in_rfu_range, tvb, offset, -1);
}
offset += 1;
break;
default:
//Unknown PDU Type, display data and flag it
proto_tree_add_item(sub_tree, hf_btmesh_provisioning_unknown_data, tvb, offset, -1, ENC_NA);
proto_tree_add_expert(sub_tree, pinfo, &ei_btmesh_provisioning_unknown_opcode, tvb, offset, -1);
offset += tvb_captured_length_remaining(tvb, offset);
break;
}
//There is still some data but all data should be already disssected
if (tvb_captured_length_remaining(tvb, offset) != 0) {
proto_tree_add_expert(sub_tree, pinfo, &ei_btmesh_provisioning_unknown_payload, tvb, offset, -1);
}
return tvb_reported_length(tvb);
}
void
proto_register_btmesh_provisioning(void)
{
static hf_register_info hf[] = {
{ &hf_btmesh_provisioning_pdu_type,
{ "Provisioning PDU Type", "provisioning.pdu_type",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_pdu_type_format), 0x3F,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_pdu_padding,
{ "Provisioning PDU Padding", "provisioning.pdu_padding",
FT_UINT8, BASE_DEC, NULL, 0xC0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_attention_duration,
{ "Attention Duration", "provisioning.attention_duration",
FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_number_of_elements,
{ "Number of Elements", "provisioning.number_of_elements",
FT_UINT8, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_algorithms,
{ "Algorithms", "provisioning.algorithms",
FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_algorithms_p256,
{ "FIPS P-256 Elliptic Curve", "provisioning.algorithms.p256",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0001,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_algorithms_rfu,
{ "RFU", "provisioning.algorithms.rfu",
FT_UINT16, BASE_DEC, NULL, 0xFFFE,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_public_key_type,
{ "Public Key Type", "provisioning.public_key_type",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_public_key_type_oob,
{ "Public Key Type OOB", "provisioning.public_key_type.oob",
FT_BOOLEAN, 8, TFS(&tfs_available_not_available), 0x01,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_public_key_type_rfu,
{ "RFU", "provisioning.public_key_type.rfu",
FT_UINT8, BASE_DEC, NULL, 0xFE,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_static_oob_type,
{ "Static OOB Type", "provisioning.static_oob_type",
FT_UINT8, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_static_oob_type_static_oob_available,
{ "Static OOB Information", "provisioning.static_oob_type.static_oob_available",
FT_BOOLEAN, 8, TFS(&tfs_available_not_available), 0x01,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_static_oob_type_rfu,
{ "RFU", "provisioning.static_oob_type.rfu",
FT_UINT8, BASE_DEC, NULL, 0xFE,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_output_oob_size,
{ "Output OOB Size", "provisioning.output_oob_size",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_output_oob_size_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_output_oob_action,
{ "Static OOB Action", "provisioning.output_oob_action",
FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_output_oob_action_blink,
{ "Blink", "provisioning.output_oob_action.blink",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0001,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_output_oob_action_beep,
{ "Beep", "provisioning.output_oob_action.beep",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0002,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_output_oob_action_vibrate,
{ "Vibrate", "provisioning.output_oob_action.vibrate",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0004,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_output_oob_action_output_numeric,
{ "Output Numeric", "provisioning.output_oob_action.output_numeric",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0008,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_output_oob_action_output_alphanumeric,
{ "Output Alphanumeric", "provisioning.output_oob_action.output_alphanumeric",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0010,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_output_oob_action_output_rfu,
{ "RFU", "provisioning.output_oob_action.rfu",
FT_UINT16, BASE_DEC, NULL, 0xFFE0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_input_oob_size,
{ "Input OOB Size", "provisioning.input_oob_size",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_input_oob_size_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_input_oob_action,
{ "Input OOB Action", "provisioning.input_oob_action",
FT_UINT16, BASE_HEX, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_input_oob_action_push,
{ "Push", "provisioning.input_oob_action.push",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0001,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_input_oob_action_twist,
{ "Twist", "provisioning.input_oob_action.twist",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0002,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_input_oob_action_input_numeric,
{ "Input Numeric", "provisioning.input_oob_action.input_numeric",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0004,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_input_oob_action_input_alphanumeric,
{ "Input Alphanumeric", "provisioning.input_oob_action.input_alphanumeric",
FT_BOOLEAN, 16, TFS(&tfs_available_not_available), 0x0008,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_input_oob_action_rfu,
{ "RFU", "provisioning.input_oob_action.rfc",
FT_UINT16, BASE_DEC, NULL, 0xFFF0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_algorithm,
{ "Algorithm", "provisioning.algorithm",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_algorithm_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_public_key,
{ "Public Key", "provisioning.public_key",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_public_key_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_method,
{ "Authentication Method", "provisioning.authentication_method",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_method_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_action_no_oob_action,
{ "No OOB Authentication Action", "provisioning.authentication_action.no_oob_action",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_action_no_oob_action_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_action_static_oob_action,
{ "Static OOB Authentication Action", "provisioning.authentication_action.static_oob_action",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_action_static_oob_action_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_action_output_oob_action,
{ "Output OOB Authentication Action", "provisioning.authentication_action.output_oob_action",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_action_output_oob_action_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_action_input_oob_action,
{ "Input OOB Authentication Action", "provisioning.authentication_action.input_oob_action",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_action_input_oob_action_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_size_no_oob_action,
{ "No OOB Authentication Size", "provisioning.authentication_size.no_oob_action",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_size_no_oob_action_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_size_static_oob_action,
{ "Static OOB Authentication Size", "provisioning.authentication_size.static_oob_action",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_size_static_oob_action_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_size_output_oob_action,
{ "Output OOB Authentication Size", "provisioning.authentication_size.output_oob_action",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_size_output_oob_action_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_authentication_size_input_oob_action,
{ "Input OOB Authentication Size", "provisioning.authentication_size.input_oob_action",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_authentication_size_input_oob_action_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_public_key_x,
{ "Public Key X", "provisioning.public_key_x",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_public_key_y,
{ "Public Key Y", "provisioning.public_key_y",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_confirmation,
{ "Confirmation", "provisioning.confirmation",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_random,
{ "Random", "provisioning.random",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_encrypted_provisioning_data,
{ "Encrypted Provisioning Data", "provisioning.encrypted_provisioning_data",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_decrypted_provisioning_data_mic,
{ "Decrypted Provisioning Data MIC", "provisioning.decrypted_provisioning_data_mic",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_error_code,
{ "Error Code", "provisioning.error_code",
FT_UINT8, BASE_DEC, VALS(btmesh_provisioning_error_code_format), 0x0,
NULL, HFILL }
},
{ &hf_btmesh_provisioning_unknown_data,
{ "Unknown Data", "provisioning.unknown_data",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
};
static gint *ett[] = {
&ett_btmesh_provisioning,
&ett_btmesh_provisioning_algorithms,
&ett_btmesh_provisioning_public_key_type,
&ett_btmesh_provisioning_static_oob_type,
&ett_btmesh_provisioning_output_oob_action,
&ett_btmesh_provisioning_output_oob_size,
&ett_btmesh_provisioning_input_oob_action,
&ett_btmesh_provisioning_input_oob_size,
&ett_btmesh_provisioning_algorithm,
&ett_btmesh_provisioning_public_key,
&ett_btmesh_provisioning_authentication_method,
&ett_btmesh_provisioning_authentication_action,
&ett_btmesh_provisioning_authentication_size,
&ett_btmesh_provisioning_error_code,
};
static ei_register_info ei[] = {
{ &ei_btmesh_provisioning_unknown_opcode,{ "provisioning.unknown_opcode", PI_PROTOCOL, PI_ERROR, "Unknown Opcode", EXPFILL } },
{ &ei_btmesh_provisioning_unknown_payload,{ "provisioning.unknown_payload", PI_PROTOCOL, PI_ERROR, "Unknown Payload", EXPFILL } },
{ &ei_btmesh_provisioning_unknown_authentication_method,{ "provisioning.unknown_authentication_method", PI_PROTOCOL, PI_ERROR, "Unknown Authentication Method", EXPFILL } },
{ &ei_btmesh_provisioning_rfu_not_zero,{ "provisioning.rfu_not_zero", PI_PROTOCOL, PI_WARN, "RFU value not equal to 0", EXPFILL } },
{ &ei_btmesh_provisioning_in_rfu_range,{ "provisioning.in_rfu_range", PI_PROTOCOL, PI_WARN, "Value in RFU range", EXPFILL } },
{ &ei_btmesh_provisioning_prohibited,{ "provisioning.prohibited", PI_PROTOCOL, PI_ERROR, "Prohibited value", EXPFILL } },
{ &ei_btmesh_provisioning_zero_elements,{ "provisioning.zero_elements", PI_PROTOCOL, PI_ERROR, "Number of Elements equal to 0 is Prohibited", EXPFILL } },
};
expert_module_t* expert_btmesh_provisioning;
proto_btmesh_provisioning = proto_register_protocol("Bluetooth Mesh Provisioning PDU", "BT Mesh Provisioning", "provisioning");
proto_register_field_array(proto_btmesh_provisioning, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_btmesh_provisioning = expert_register_protocol(proto_btmesh_provisioning);
expert_register_field_array(expert_btmesh_provisioning, ei, array_length(ei));
/*module_t *btmesh_provisioning_module;
btmesh_provisioning_module = */prefs_register_protocol_subtree("Bluetooth", proto_btmesh_provisioning, NULL);
register_dissector("btmesh.provisioning", dissect_btmesh_provisioning_msg, proto_btmesh_provisioning);
}
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* ex: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -0,0 +1,402 @@
/* packet-btmesh-proxy.c
* Routines for Bluetooth mesh Proxy PDU dissection
*
* Copyright 2019, Piotr Winiarczyk <wino45@gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*
* Ref: Mesh Profile v1.0
* https://www.bluetooth.com/specifications/mesh-specifications
*/
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/reassemble.h>
#include <epan/tvbuff-int.h>
#include "packet-btmesh.h"
#define PROXY_COMPLETE_MESSAGE 0x00
#define PROXY_FIRST_SEGMENT 0x01
#define PROXY_CONTINUATION_SEGMENT 0x02
#define PROXY_LAST_SEGMENT 0x03
#define PROXY_PDU_NETWORK 0x00
#define PROXY_PDU_MESH_BEACON 0x01
#define PROXY_PDU_CONFIGURATION 0x02
#define PROXY_PDU_PROVISIONING 0x03
void proto_register_btmesh_proxy(void);
static int proto_btmesh_proxy = -1;
static int hf_btmesh_proxy_type = -1;
static int hf_btmesh_proxy_sar = -1;
static int hf_btmesh_proxy_data = -1;
static int hf_btmesh_proxy_fragments = -1;
static int hf_btmesh_proxy_fragment = -1;
static int hf_btmesh_proxy_fragment_overlap = -1;
static int hf_btmesh_proxy_fragment_overlap_conflict = -1;
static int hf_btmesh_proxy_fragment_multiple_tails = -1;
static int hf_btmesh_proxy_fragment_too_long_fragment = -1;
static int hf_btmesh_proxy_fragment_error = -1;
static int hf_btmesh_proxy_fragment_count = -1;
static int hf_btmesh_proxy_reassembled_length = -1;
static int ett_btmesh_proxy = -1;
static int ett_btmesh_proxy_fragments = -1;
static int ett_btmesh_proxy_fragment = -1;
static dissector_handle_t btmesh_handle;
static dissector_handle_t btmesh_provisioning_handle;
static dissector_handle_t btmesh_beacon_handle;
static wmem_tree_t *connection_info_tree;
static wmem_allocator_t *pool;
static const value_string btmesh_proxy_type[] = {
{ 0, "Network PDU" },
{ 1, "Mesh Beacon" },
{ 2, "Proxy Configuration" },
{ 3, "Provisioning PDU" },
{ 0, NULL }
};
static const value_string btmesh_proxy_sar[] = {
{ 0, "Data field contains a complete message" },
{ 1, "Data field contains the first segment of a message" },
{ 2, "Data field contains a continuation segment of a message" },
{ 3, "Data field contains the last segment of a message" },
{ 0, NULL }
};
static const fragment_items btmesh_proxy_frag_items = {
&ett_btmesh_proxy_fragments,
&ett_btmesh_proxy_fragment,
&hf_btmesh_proxy_fragments,
&hf_btmesh_proxy_fragment,
&hf_btmesh_proxy_fragment_overlap,
&hf_btmesh_proxy_fragment_overlap_conflict,
&hf_btmesh_proxy_fragment_multiple_tails,
&hf_btmesh_proxy_fragment_too_long_fragment,
&hf_btmesh_proxy_fragment_error,
&hf_btmesh_proxy_fragment_count,
NULL,
&hf_btmesh_proxy_reassembled_length,
/* Reassembled data field */
NULL,
"fragments"
};
static reassembly_table proxy_reassembly_table;
static guint32 sequence_counter[E_BTMESH_PROXY_SIDE_LAST];
static guint32 fragment_counter[E_BTMESH_PROXY_SIDE_LAST];
static gboolean first_pass;
static gint
dissect_btmesh_proxy_msg(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *proxy_data)
{
proto_item *item;
proto_tree *sub_tree;
tvbuff_t *next_tvb = NULL;
fragment_head *fd_head = NULL;
guint32 *sequence_counter_ptr;
void *storage;
btle_mesh_transport_ctx_t tr_ctx;
guint offset = 0;
btle_mesh_proxy_ctx_t *proxy_ctx = NULL;
DISSECTOR_ASSERT(proxy_data);
proxy_ctx = (btle_mesh_proxy_ctx_t *)proxy_data;
DISSECTOR_ASSERT(proxy_ctx->proxy_side < E_BTMESH_PROXY_SIDE_LAST);
if (pinfo->fd->visited && first_pass) {
first_pass=FALSE;
sequence_counter[proxy_ctx->proxy_side] = 0;
fragment_counter[proxy_ctx->proxy_side] = 0;
}
col_set_str(pinfo->cinfo, COL_PROTOCOL, "BT Mesh Proxy");
item = proto_tree_add_item(tree, proto_btmesh_proxy, tvb, offset, -1, ENC_NA);
sub_tree = proto_item_add_subtree(item, ett_btmesh_proxy);
proto_tree_add_item(sub_tree, hf_btmesh_proxy_type, tvb, offset, 1, ENC_NA);
proto_tree_add_item(sub_tree, hf_btmesh_proxy_sar, tvb, offset, 1, ENC_NA);
guint8 proxy_sar = (tvb_get_guint8(tvb, offset) & 0xC0 ) >> 6;
guint8 proxy_type = tvb_get_guint8(tvb, offset) & 0x3F;
offset += 1;
guint32 length = tvb_reported_length(tvb) - offset;
gboolean packetReassembledOrComplete = FALSE;
gboolean packetComplete = FALSE;
col_set_str(pinfo->cinfo, COL_INFO, val_to_str_const(proxy_type, btmesh_proxy_type, "Unknown Proxy PDU"));
switch (proxy_sar){
case PROXY_COMPLETE_MESSAGE:
packetReassembledOrComplete = TRUE;
packetComplete = TRUE;
next_tvb = tvb_new_subset_length_caplen(tvb, offset, -1, tvb_captured_length(tvb) - offset);
col_append_str(pinfo->cinfo, COL_INFO," (Complete)");
break;
case PROXY_FIRST_SEGMENT:
proto_tree_add_item(sub_tree, hf_btmesh_proxy_data, tvb, offset, length, ENC_NA);
if (!pinfo->fd->visited) {
sequence_counter[proxy_ctx->proxy_side]++;
fragment_counter[proxy_ctx->proxy_side]=0;
fd_head = fragment_add_seq(&proxy_reassembly_table,
tvb, offset, pinfo,
sequence_counter[proxy_ctx->proxy_side], NULL,
fragment_counter[proxy_ctx->proxy_side],
tvb_captured_length_remaining(tvb, offset),
TRUE, 0);
fragment_counter[proxy_ctx->proxy_side]++;
} else {
sequence_counter[proxy_ctx->proxy_side]++;
}
col_append_str(pinfo->cinfo, COL_INFO," (First Segment)");
break;
case PROXY_CONTINUATION_SEGMENT:
proto_tree_add_item(sub_tree, hf_btmesh_proxy_data, tvb, offset, length, ENC_NA);
if (!pinfo->fd->visited) {
fd_head = fragment_add_seq(&proxy_reassembly_table,
tvb, offset, pinfo,
sequence_counter[proxy_ctx->proxy_side], NULL,
fragment_counter[proxy_ctx->proxy_side],
tvb_captured_length_remaining(tvb, offset),
TRUE, 0);
fragment_counter[proxy_ctx->proxy_side]++;
}
col_append_str(pinfo->cinfo, COL_INFO," (Continuation Segment)");
break;
case PROXY_LAST_SEGMENT:
proto_tree_add_item(sub_tree, hf_btmesh_proxy_data, tvb, offset, length, ENC_NA);
if (!pinfo->fd->visited) {
fd_head = fragment_add_seq(&proxy_reassembly_table,
tvb, offset, pinfo,
sequence_counter[proxy_ctx->proxy_side], NULL,
fragment_counter[proxy_ctx->proxy_side],
tvb_captured_length_remaining(tvb, offset),
FALSE, 0);
fragment_counter[proxy_ctx->proxy_side]++;
//add mapping "pinfo->num" -> "sequence_counter"
storage = wmem_alloc0(pool, sizeof(sequence_counter[proxy_ctx->proxy_side]));
*((guint32 *)storage) = sequence_counter[proxy_ctx->proxy_side];
wmem_tree_insert32(connection_info_tree, pinfo->num, storage);
fd_head = fragment_get(&proxy_reassembly_table, pinfo, sequence_counter[proxy_ctx->proxy_side], NULL);
}
packetReassembledOrComplete = TRUE;
col_append_str(pinfo->cinfo, COL_INFO," (Last Segment)");
break;
//No default since this is 2 bit value
}
if (packetReassembledOrComplete && pinfo->fd->visited) {
if (next_tvb == NULL) {
sequence_counter_ptr = (guint32 *)wmem_tree_lookup32(connection_info_tree, pinfo->num);
if (sequence_counter_ptr != NULL) {
fd_head = fragment_get(&proxy_reassembly_table, pinfo, *sequence_counter_ptr, NULL);
}
if (fd_head) {
next_tvb = process_reassembled_data(tvb, offset, pinfo,
"Reassembled Message", fd_head, &btmesh_proxy_frag_items,
NULL, sub_tree);
col_append_str(pinfo->cinfo, COL_INFO, " (Message Reassembled)");
}
}
if (next_tvb){
offset = 0;
tr_ctx.transport = E_BTMESH_TR_PROXY;
if (packetComplete) {
tr_ctx.fragmented = FALSE;
} else {
tr_ctx.fragmented = TRUE;
}
tr_ctx.segment_index = 0;
switch(proxy_type) {
case PROXY_PDU_NETWORK:
if (btmesh_handle) {
call_dissector(btmesh_handle, next_tvb, pinfo, proto_tree_get_root(tree));
} else {
proto_tree_add_item(sub_tree, hf_btmesh_proxy_data, next_tvb, offset, length, ENC_NA);
}
break;
case PROXY_PDU_MESH_BEACON:
if (btmesh_beacon_handle) {
call_dissector_with_data(btmesh_beacon_handle, next_tvb, pinfo, proto_tree_get_root(tree), &tr_ctx);
} else {
proto_tree_add_item(sub_tree, hf_btmesh_proxy_data, next_tvb, offset, length, ENC_NA);
}
break;
case PROXY_PDU_CONFIGURATION:
//TODO decrypt, dissect and display
proto_tree_add_item(sub_tree, hf_btmesh_proxy_data, next_tvb, offset, length, ENC_NA);
break;
case PROXY_PDU_PROVISIONING:
if (btmesh_provisioning_handle) {
call_dissector_with_data(btmesh_provisioning_handle, next_tvb, pinfo, proto_tree_get_root(tree), &tr_ctx);
} else {
proto_tree_add_item(sub_tree, hf_btmesh_proxy_data, next_tvb, offset, length, ENC_NA);
}
break;
//Default is not needed
}
}
}
return tvb_reported_length(tvb);
}
static void
proxy_init_routine(void)
{
reassembly_table_register(&proxy_reassembly_table, &addresses_reassembly_table_functions);
for (int i=0; i< E_BTMESH_PROXY_SIDE_LAST; i++ ){
sequence_counter[i] = 0;
fragment_counter[i] = 0;
}
first_pass = TRUE;
pool = wmem_allocator_new(WMEM_ALLOCATOR_SIMPLE);
}
static void
proxy_cleanup_dissector(void)
{
wmem_destroy_allocator(pool);
pool = NULL;
}
void
proto_register_btmesh_proxy(void)
{
static hf_register_info hf[] = {
{ &hf_btmesh_proxy_type,
{ "Type", "btmproxy.type",
FT_UINT8, BASE_DEC, VALS(btmesh_proxy_type), 0x3F,
NULL, HFILL }
},
{ &hf_btmesh_proxy_sar,
{ "SAR", "btmproxy.sar",
FT_UINT8, BASE_DEC, VALS(btmesh_proxy_sar), 0xC0,
NULL, HFILL }
},
{ &hf_btmesh_proxy_data,
{ "Data", "btmproxy.data",
FT_BYTES, BASE_NONE, NULL, 0x00,
NULL, HFILL }
},
//Proxy Payload Reassembly
{ &hf_btmesh_proxy_fragments,
{ "Reassembled Proxy Payload Fragments", "btmproxy.fragments",
FT_NONE, BASE_NONE, NULL, 0x0,
"Proxy Payload Fragments", HFILL }
},
{ &hf_btmesh_proxy_fragment,
{ "Proxy Payload Fragment", "btmproxy.fragment",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_proxy_fragment_overlap,
{ "Fragment overlap", "btmproxy.fragment.overlap",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Fragment overlaps with other fragments", HFILL }
},
{ &hf_btmesh_proxy_fragment_overlap_conflict,
{ "Conflicting data in fragment overlap", "btmproxy.fragment.overlap.conflict",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Overlapping fragments contained conflicting data", HFILL }
},
{ &hf_btmesh_proxy_fragment_multiple_tails,
{ "Multiple tail fragments found", "btmproxy.fragment.multipletails",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Several tails were found when defragmenting the packet", HFILL }
},
{ &hf_btmesh_proxy_fragment_too_long_fragment,
{ "Fragment too long", "btmproxy.fragment.toolongfragment",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"Fragment contained data past end of packet", HFILL }
},
{ &hf_btmesh_proxy_fragment_error,
{ "Defragmentation error", "btmproxy.fragment.error",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"Defragmentation error due to illegal fragments", HFILL }
},
{ &hf_btmesh_proxy_fragment_count,
{ "Fragment count", "btmproxy.fragment.count",
FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btmesh_proxy_reassembled_length,
{ "Reassembled Proxy Payload length", "btmproxy.reassembled.length",
FT_UINT32, BASE_DEC, NULL, 0x0,
"The total length of the reassembled payload", HFILL }
},
};
static gint *ett[] = {
&ett_btmesh_proxy,
&ett_btmesh_proxy_fragments,
&ett_btmesh_proxy_fragment,
};
proto_btmesh_proxy = proto_register_protocol("Bluetooth Mesh Proxy", "BT Mesh proxy", "btmproxy");
proto_register_field_array(proto_btmesh_proxy, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
prefs_register_protocol_subtree("Bluetooth", proto_btmesh_proxy, NULL);
register_dissector("btmesh.proxy", dissect_btmesh_proxy_msg, proto_btmesh_proxy);
connection_info_tree = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
register_init_routine(proxy_init_routine);
register_cleanup_routine(proxy_cleanup_dissector);
}
void
proto_reg_handoff_btmesh_proxy(void)
{
btmesh_handle = find_dissector("btmesh.msg");
btmesh_provisioning_handle = find_dissector("btmesh.provisioning");
btmesh_beacon_handle = find_dissector("btmesh.beacon");
}
/*
* Editor modelines
*
* Local Variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/

View File

@ -27,8 +27,6 @@ gint net_mic_size_chosen = 1;
static int proto_btmesh = -1;
/*-------------------------------------
* UAT for BT Mesh
*-------------------------------------
@ -1250,7 +1248,7 @@ proto_register_btmesh(void)
NULL, HFILL }
},
{ &hf_btmesh_segment,
{ "Sewgment", "btmesh.segment",
{ "Segment", "btmesh.segment",
FT_BYTES, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
@ -1290,6 +1288,10 @@ proto_register_btmesh(void)
btmesh_module = prefs_register_protocol_subtree("Bluetooth", proto_btmesh, NULL);
prefs_register_static_text_preference(btmesh_module, "version",
"Bluetooth Mesh Profile v1.0",
"Version of protocol supported by this dissector.");
btmesh_uat = uat_new("BTMesh Network keys",
sizeof(uat_btmesh_record_t), /* record size */
"btmesh_nw_keys", /* filename */

View File

@ -0,0 +1,57 @@
/* packet-btmesh.h
* Structures for determining the dissection context for Bluetooth mesh.
*
* Copyright 2019, Piotr Winiarczyk <wino45@gmail.com>
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __PACKET_BTMESH_H__
#define __PACKET_BTMESH_H__
typedef enum {
E_BTMESH_TR_UNKNOWN = 0,
E_BTMESH_TR_ADV,
E_BTMESH_TR_PB_ADV,
E_BTMESH_TR_PROXY
} btle_mesh_tr_t;
typedef enum {
E_BTMESH_PROXY_SIDE_UNKNOWN = 0,
E_BTMESH_PROXY_SIDE_SERVER,
E_BTMESH_PROXY_SIDE_CLIENT,
E_BTMESH_PROXY_SIDE_LAST
} btle_mesh_proxy_side_t;
typedef struct {
btle_mesh_tr_t transport;
gboolean fragmented;
guint segment_index;
} btle_mesh_transport_ctx_t;
typedef struct {
guint32 interface_id;
guint32 adapter_id;
guint16 chandle;
guint16 bt_uuid;
guint32 access_address;
btle_mesh_proxy_side_t proxy_side;
} btle_mesh_proxy_ctx_t;
#endif /* __PACKET_BTMESH_H__ */
/*
* Editor modelines - http://www.wireshark.org/tools/modelines.html
*
* Local variables:
* c-basic-offset: 4
* tab-width: 8
* indent-tabs-mode: nil
* End:
*
* vi: set shiftwidth=4 tabstop=8 expandtab:
* :indentSize=4:tabSize=8:noTabs=true:
*/