wireshark/epan/dissectors/packet-btavctp.c

493 lines
17 KiB
C

/* packet-btavctp.c
* Routines for Bluetooth AVCTP dissection
*
* Copyright 2012, Michal Labedzki for Tieto Corporation
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "config.h"
#include <epan/packet.h>
#include <epan/prefs.h>
#include <epan/expert.h>
#include <epan/decode_as.h>
#include <epan/proto_data.h>
#include "packet-bluetooth.h"
#include "packet-btl2cap.h"
#include "packet-btsdp.h"
#include "packet-btavctp.h"
#define PACKET_TYPE_SINGLE 0x00
#define PACKET_TYPE_START 0x01
#define PACKET_TYPE_CONTINUE 0x02
#define PACKET_TYPE_END 0x03
int proto_btavctp = -1;
static int hf_btavctp_transaction = -1;
static int hf_btavctp_packet_type = -1;
static int hf_btavctp_cr = -1;
static int hf_btavctp_ipid = -1;
static int hf_btavctp_rfa = -1;
static int hf_btavctp_pid = -1;
static int hf_btavctp_number_of_packets = -1;
static gint ett_btavctp = -1;
static expert_field ei_btavctp_unexpected_frame = EI_INIT;
static expert_field ei_btavctp_invalid_profile = EI_INIT;
static dissector_handle_t btavctp_handle;
typedef struct _fragment_t {
guint length;
guint8 *data;
} fragment_t;
typedef struct _fragments_t {
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
guint32 psm;
guint32 count;
guint32 number_of_packets;
guint32 pid;
wmem_tree_t *fragment;
} fragments_t;
static wmem_tree_t *reassembling = NULL;
static fragments_t *fragments = NULL;
static const value_string packet_type_vals[] = {
{ PACKET_TYPE_SINGLE, "Single" },
{ PACKET_TYPE_START, "Start" },
{ PACKET_TYPE_CONTINUE, "Continue" },
{ PACKET_TYPE_END, "End" },
{ 0, NULL }
};
static const value_string cr_vals[] = {
{ 0x00, "Command" },
{ 0x01, "Response" },
{ 0, NULL }
};
static const value_string ipid_vals[] = {
{ 0x00, "Profile OK" },
{ 0x01, "Invalid profile" },
{ 0, NULL }
};
void proto_register_btavctp(void);
void proto_reg_handoff_btavctp(void);
static gint
dissect_btavctp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
proto_item *ti;
proto_tree *btavctp_tree;
proto_item *pitem;
proto_item *ipid_item = NULL;
btavctp_data_t *avctp_data;
tvbuff_t *next_tvb;
gint offset = 0;
guint packet_type;
guint cr;
guint pid = 0;
guint transaction;
guint number_of_packets = 0;
guint length;
guint i_frame;
gboolean ipid = FALSE;
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
guint32 psm;
gint previous_proto;
previous_proto = (GPOINTER_TO_INT(wmem_list_frame_data(wmem_list_frame_prev(wmem_list_tail(pinfo->layers)))));
if (previous_proto == proto_btl2cap) {
btl2cap_data_t *l2cap_data;
l2cap_data = (btl2cap_data_t *) data;
interface_id = l2cap_data->interface_id;
adapter_id = l2cap_data->adapter_id;
chandle = l2cap_data->chandle;
psm = l2cap_data->psm;
} else {
interface_id = HCI_INTERFACE_DEFAULT;
adapter_id = HCI_ADAPTER_DEFAULT;
chandle = 0;
psm = 0;
}
ti = proto_tree_add_item(tree, proto_btavctp, tvb, offset, tvb_captured_length_remaining(tvb, offset), ENC_NA);
btavctp_tree = proto_item_add_subtree(ti, ett_btavctp);
col_set_str(pinfo->cinfo, COL_PROTOCOL, "AVCTP");
col_clear(pinfo->cinfo, COL_INFO);
switch (pinfo->p2p_dir) {
case P2P_DIR_SENT:
col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
break;
case P2P_DIR_RECV:
col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
break;
default:
col_set_str(pinfo->cinfo, COL_INFO, "UnknownDirection ");
break;
}
proto_tree_add_item(btavctp_tree, hf_btavctp_transaction, tvb, offset, 1, ENC_BIG_ENDIAN);
pitem = proto_tree_add_item(btavctp_tree, hf_btavctp_packet_type, tvb, offset, 1, ENC_BIG_ENDIAN);
proto_tree_add_item(btavctp_tree, hf_btavctp_cr, tvb, offset, 1, ENC_BIG_ENDIAN);
transaction = tvb_get_guint8(tvb, offset) >> 4;
packet_type = (tvb_get_guint8(tvb, offset) & 0x0C) >> 2;
cr = (tvb_get_guint8(tvb, offset) & 0x02) >> 1 ;
if (packet_type == PACKET_TYPE_SINGLE || packet_type == PACKET_TYPE_START) {
ipid_item = proto_tree_add_item(btavctp_tree, hf_btavctp_ipid, tvb, offset, 1, ENC_BIG_ENDIAN);
ipid = tvb_get_guint8(tvb, offset) & 0x01;
} else {
proto_tree_add_item(btavctp_tree, hf_btavctp_rfa, tvb, offset, 1, ENC_BIG_ENDIAN);
}
offset++;
if (packet_type == PACKET_TYPE_START) {
proto_tree_add_item(btavctp_tree, hf_btavctp_number_of_packets, tvb, offset, 1, ENC_BIG_ENDIAN);
number_of_packets = tvb_get_guint8(tvb, offset);
offset++;
}
if (packet_type == PACKET_TYPE_SINGLE || packet_type == PACKET_TYPE_START) {
proto_tree_add_item(btavctp_tree, hf_btavctp_pid, tvb, offset, 2, ENC_BIG_ENDIAN);
pid = tvb_get_ntohs(tvb, offset);
if (p_get_proto_data(pinfo->pool, pinfo, proto_bluetooth, PROTO_DATA_BLUETOOTH_SERVICE_UUID ) == NULL) {
guint8 *value_data;
bluetooth_uuid_t uuid;
uuid.size = 2;
uuid.bt_uuid = pid;
uuid.data[0] = pid >> 8;
uuid.data[1] = pid & 0xFF;
value_data = wmem_strdup(wmem_file_scope(), print_numeric_bluetooth_uuid(pinfo->pool, &uuid));
p_add_proto_data(pinfo->pool, pinfo, proto_bluetooth, PROTO_DATA_BLUETOOTH_SERVICE_UUID, value_data);
}
offset +=2;
}
col_append_fstr(pinfo->cinfo, COL_INFO, "%s - Transaction: %u, PacketType: %s",
val_to_str_const(cr, cr_vals, "unknown CR"), transaction,
val_to_str_const(packet_type, packet_type_vals, "unknown packet type"));
if (ipid) {
expert_add_info(pinfo, ipid_item, &ei_btavctp_invalid_profile);
col_append_sep_str(pinfo->cinfo, COL_INFO, NULL, "Invalid profile");
if (tvb_captured_length_remaining(tvb, offset) == 0)
return offset;
}
avctp_data = wmem_new(pinfo->pool, btavctp_data_t);
avctp_data->cr = cr;
avctp_data->interface_id = interface_id;
avctp_data->adapter_id = adapter_id;
avctp_data->chandle = chandle;
avctp_data->psm = psm;
length = tvb_reported_length_remaining(tvb, offset);
/* reassembling */
next_tvb = tvb_new_subset_length(tvb, offset, length);
if (packet_type == PACKET_TYPE_SINGLE) {
bluetooth_uuid_t uuid;
uuid.size = 2;
uuid.bt_uuid = pid;
uuid.data[0] = pid >> 8;
uuid.data[1] = pid & 0xFF;
if (!dissector_try_string(bluetooth_uuid_table, print_numeric_bluetooth_uuid(pinfo->pool, &uuid), next_tvb, pinfo, tree, avctp_data)) {
call_data_dissector(next_tvb, pinfo, tree);
}
} else {
fragment_t *fragment;
wmem_tree_key_t key[6];
guint32 frame_number;
frame_number = pinfo->num;
key[0].length = 1;
key[0].key = &interface_id;
key[1].length = 1;
key[1].key = &adapter_id;
key[2].length = 1;
key[2].key = &chandle;
key[3].length = 1;
key[3].key = &psm;
key[4].length = 1;
key[4].key = &frame_number;
key[5].length = 0;
key[5].key = NULL;
if (packet_type == PACKET_TYPE_START) {
if (!pinfo->fd->visited) {
fragment = wmem_new(wmem_file_scope(), fragment_t);
fragment->length = length;
fragment->data = (guint8 *) wmem_alloc(wmem_file_scope(), fragment->length);
tvb_memcpy(tvb, fragment->data, offset, fragment->length);
fragments = wmem_new(wmem_file_scope(), fragments_t);
fragments->number_of_packets = number_of_packets;
fragments->pid = pid;
fragments->count = 1;
fragments->fragment = wmem_tree_new(wmem_file_scope());
wmem_tree_insert32(fragments->fragment, fragments->count, fragment);
fragments->interface_id = interface_id;
fragments->adapter_id = adapter_id;
fragments->chandle = chandle;
fragments->psm = psm;
wmem_tree_insert32_array(reassembling, key, fragments);
} else {
fragments = (fragments_t *)wmem_tree_lookup32_array_le(reassembling, key);
if (!(fragments && fragments->interface_id == interface_id &&
fragments->adapter_id == adapter_id &&
fragments->chandle == chandle &&
fragments->psm == psm))
fragments = NULL;
}
call_data_dissector(next_tvb, pinfo, tree);
} else if (packet_type == PACKET_TYPE_CONTINUE) {
fragments = (fragments_t *)wmem_tree_lookup32_array_le(reassembling, key);
if (!(fragments && fragments->interface_id == interface_id &&
fragments->adapter_id == adapter_id &&
fragments->chandle == chandle &&
fragments->psm == psm))
fragments = NULL;
if (!pinfo->fd->visited && fragments != NULL) {
fragment = wmem_new(wmem_file_scope(), fragment_t);
fragment->length = length;
fragment->data = (guint8 *) wmem_alloc(wmem_file_scope(), fragment->length);
tvb_memcpy(tvb, fragment->data, offset, fragment->length);
fragments->count++;
wmem_tree_insert32(fragments->fragment, fragments->count, fragment);
fragments->interface_id = interface_id;
fragments->adapter_id = adapter_id;
fragments->chandle = chandle;
fragments->psm = psm;
frame_number = pinfo->num;
key[0].length = 1;
key[0].key = &interface_id;
key[1].length = 1;
key[1].key = &adapter_id;
key[2].length = 1;
key[2].key = &chandle;
key[3].length = 1;
key[3].key = &psm;
key[4].length = 1;
key[4].key = &frame_number;
key[5].length = 0;
key[5].key = NULL;
wmem_tree_insert32_array(reassembling, key, fragments);
}
call_data_dissector(next_tvb, pinfo, tree);
} else if (packet_type == PACKET_TYPE_END) {
fragments = (fragments_t *)wmem_tree_lookup32_array_le(reassembling, key);
if (!(fragments && fragments->interface_id == interface_id &&
fragments->adapter_id == adapter_id &&
fragments->chandle == chandle &&
fragments->psm == psm))
fragments = NULL;
if (!pinfo->fd->visited && fragments != NULL) {
fragment = wmem_new(wmem_file_scope(), fragment_t);
fragment->length = length;
fragment->data = (guint8 *) wmem_alloc(wmem_file_scope(), fragment->length);
tvb_memcpy(tvb, fragment->data, offset, fragment->length);
fragments->count++;
wmem_tree_insert32(fragments->fragment, fragments->count, fragment);
fragments->interface_id = interface_id;
fragments->adapter_id = adapter_id;
fragments->chandle = chandle;
fragments->psm = psm;
frame_number = pinfo->num;
key[0].length = 1;
key[0].key = &interface_id;
key[1].length = 1;
key[1].key = &adapter_id;
key[2].length = 1;
key[2].key = &chandle;
key[3].length = 1;
key[3].key = &psm;
key[4].length = 1;
key[4].key = &frame_number;
key[5].length = 0;
key[5].key = NULL;
wmem_tree_insert32_array(reassembling, key, fragments);
}
length = 0;
if (!fragments || fragments->count != fragments->number_of_packets) {
expert_add_info(pinfo, pitem, &ei_btavctp_unexpected_frame);
call_data_dissector(next_tvb, pinfo, tree);
} else {
guint8 *reassembled = NULL;
bluetooth_uuid_t uuid;
for (i_frame = 1; i_frame <= fragments->count; ++i_frame) {
fragment = (fragment_t *)wmem_tree_lookup32_le(fragments->fragment, i_frame);
if (fragment) {
reassembled = (guint8*)wmem_realloc(pinfo->pool, reassembled, length + fragment->length);
memcpy(reassembled + length, fragment->data, fragment->length);
length += fragment->length;
}
}
next_tvb = tvb_new_child_real_data(tvb, reassembled, length, length);
add_new_data_source(pinfo, next_tvb, "Reassembled AVCTP");
uuid.size = 2;
uuid.bt_uuid = fragments->pid;
uuid.data[0] = fragments->pid >> 8;
uuid.data[1] = fragments->pid & 0xFF;
if (!dissector_try_string(bluetooth_uuid_table, print_numeric_bluetooth_uuid(pinfo->pool, &uuid), next_tvb, pinfo, tree, avctp_data)) {
call_data_dissector(next_tvb, pinfo, tree);
}
}
fragments = NULL;
} else {
call_data_dissector(next_tvb, pinfo, tree);
}
}
return offset;
}
void
proto_register_btavctp(void)
{
module_t *module;
expert_module_t* expert_btavctp;
static hf_register_info hf[] = {
{ &hf_btavctp_transaction,
{ "Transaction", "btavctp.transaction",
FT_UINT8, BASE_HEX, NULL, 0xF0,
NULL, HFILL }
},
{ &hf_btavctp_packet_type,
{ "Packet Type", "btavctp.packet_type",
FT_UINT8, BASE_HEX, VALS(packet_type_vals), 0x0C,
NULL, HFILL }
},
{ &hf_btavctp_cr,
{ "C/R", "btavctp.cr",
FT_UINT8, BASE_HEX, VALS(cr_vals), 0x02,
NULL, HFILL }
},
{ &hf_btavctp_ipid,
{ "IPID", "btavctp.ipid",
FT_UINT8, BASE_HEX, VALS(ipid_vals), 0x01,
NULL, HFILL }
},
{ &hf_btavctp_rfa,
{ "RFA", "btavctp.rfa",
FT_UINT8, BASE_HEX, NULL, 0x01,
NULL, HFILL }
},
{ &hf_btavctp_pid,
{ "Profile Identifier", "btavctp.pid",
FT_UINT16, BASE_HEX|BASE_EXT_STRING, &bluetooth_uuid_vals_ext, 0x00,
NULL, HFILL }
},
{ &hf_btavctp_number_of_packets,
{ "Number of packets", "btavctp.nop",
FT_UINT8, BASE_DEC, NULL, 0x00,
NULL, HFILL }
}
};
static gint *ett[] = {
&ett_btavctp
};
static ei_register_info ei[] = {
{ &ei_btavctp_unexpected_frame, { "btavctp.unexpected_frame", PI_PROTOCOL, PI_WARN, "Unexpected frame", EXPFILL }},
{ &ei_btavctp_invalid_profile, { "btavctp.invalid_profile", PI_PROTOCOL, PI_NOTE, "Invalid Profile", EXPFILL }},
};
/* Decode As handling */
reassembling = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
proto_btavctp = proto_register_protocol("Bluetooth AVCTP Protocol", "BT AVCTP", "btavctp");
btavctp_handle = register_dissector("btavctp", dissect_btavctp, proto_btavctp);
proto_register_field_array(proto_btavctp, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_btavctp = expert_register_protocol(proto_btavctp);
expert_register_field_array(expert_btavctp, ei, array_length(ei));
module = prefs_register_protocol_subtree("Bluetooth", proto_btavctp, NULL);
prefs_register_static_text_preference(module, "avctp.version",
"Bluetooth Protocol AVCTP version: 1.4",
"Version of protocol supported by this dissector.");
}
void
proto_reg_handoff_btavctp(void)
{
dissector_add_string("bluetooth.uuid", "17", btavctp_handle);
dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_AVCTP_CTRL, btavctp_handle);
dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_AVCTP_BRWS, btavctp_handle);
dissector_add_for_decode_as("btl2cap.cid", btavctp_handle);
}
/*
* Editor modelines - https://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:
*/