diff --git a/epan/dissectors/CMakeLists.txt b/epan/dissectors/CMakeLists.txt index 472d53a1c5..3d66455a19 100644 --- a/epan/dissectors/CMakeLists.txt +++ b/epan/dissectors/CMakeLists.txt @@ -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 diff --git a/epan/dissectors/packet-btatt.c b/epan/dissectors/packet-btatt.c index 2b24f54924..bd1d456dd8 100644 --- a/epan/dissectors/packet-btatt.c +++ b/epan/dissectors/packet-btatt.c @@ -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); diff --git a/epan/dissectors/packet-bthci_cmd.c b/epan/dissectors/packet-bthci_cmd.c index fb0b59ca14..d678afaf5d 100644 --- a/epan/dissectors/packet-bthci_cmd.c +++ b/epan/dissectors/packet-bthci_cmd.c @@ -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 diff --git a/epan/dissectors/packet-btmesh-beacon.c b/epan/dissectors/packet-btmesh-beacon.c new file mode 100644 index 0000000000..4d6deacf0d --- /dev/null +++ b/epan/dissectors/packet-btmesh-beacon.c @@ -0,0 +1,365 @@ +/* packet-btmesh-beacon.c + * Routines for Bluetooth mesh PB-ADV dissection + * + * Copyright 2019, Piotr Winiarczyk + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 +#include +#include + +#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: + */ diff --git a/epan/dissectors/packet-btmesh-pbadv.c b/epan/dissectors/packet-btmesh-pbadv.c new file mode 100644 index 0000000000..b614f7a0d2 --- /dev/null +++ b/epan/dissectors/packet-btmesh-pbadv.c @@ -0,0 +1,549 @@ +/* packet-btmesh-pbadv.c + * Routines for Bluetooth mesh PB-ADV dissection + * + * Copyright 2019, Piotr Winiarczyk + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 +#include +#include +#include + +#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: + */ diff --git a/epan/dissectors/packet-btmesh-provisioning.c b/epan/dissectors/packet-btmesh-provisioning.c new file mode 100644 index 0000000000..6a0dd4397a --- /dev/null +++ b/epan/dissectors/packet-btmesh-provisioning.c @@ -0,0 +1,866 @@ +/* packet-btmesh-provisioning.c + * Routines for Bluetooth mesh Provisioning PDU dissection + * + * Copyright 2019, Piotr Winiarczyk + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 +#include +#include + +#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: + */ diff --git a/epan/dissectors/packet-btmesh-proxy.c b/epan/dissectors/packet-btmesh-proxy.c new file mode 100644 index 0000000000..086a6d8a0f --- /dev/null +++ b/epan/dissectors/packet-btmesh-proxy.c @@ -0,0 +1,402 @@ +/* packet-btmesh-proxy.c + * Routines for Bluetooth mesh Proxy PDU dissection + * + * Copyright 2019, Piotr Winiarczyk + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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 +#include +#include +#include + +#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: + */ diff --git a/epan/dissectors/packet-btmesh.c b/epan/dissectors/packet-btmesh.c index 5d9d735766..caa376f45c 100644 --- a/epan/dissectors/packet-btmesh.c +++ b/epan/dissectors/packet-btmesh.c @@ -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 */ diff --git a/epan/dissectors/packet-btmesh.h b/epan/dissectors/packet-btmesh.h new file mode 100644 index 0000000000..3ab19da1aa --- /dev/null +++ b/epan/dissectors/packet-btmesh.h @@ -0,0 +1,57 @@ +/* packet-btmesh.h + * Structures for determining the dissection context for Bluetooth mesh. + * + * Copyright 2019, Piotr Winiarczyk + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * 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: + */