Bluetooth: Fix wrong recognized RFCOMM services

It seems that RFCOMM service can be dynamically changed while
connection is still alive. In other words: host can connect to
remote device and set one RFCOMM service (remote service), but later
remote device can change service to one of host service without
any disconnection. This patch add support for this case.
Also improve searching for useful UUID service through SDP.

Change-Id: I9e03b9b965d6b0d9761b4a451cdeb4a1a33ca017
Reviewed-on: https://code.wireshark.org/review/808
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Michal Labedzki 2014-03-24 11:54:00 +01:00 committed by Anders Broman
parent ab42220b65
commit a99a0360c4
2 changed files with 105 additions and 32 deletions

View File

@ -128,6 +128,13 @@ static dissector_handle_t btgnss_handle;
static dissector_table_t rfcomm_service_dissector_table;
static dissector_table_t rfcomm_channel_dissector_table;
static wmem_tree_t *service_directions = NULL;
typedef struct {
guint32 direction;
guint32 end_in;
} service_direction_t;
typedef struct {
guint channel;
gchar* payload_proto_name;
@ -204,6 +211,8 @@ static const value_string vs_frame_type_short[] = {
{0, NULL}
};
#define FRAME_TYPE_SABM 0x2F
#define FRAME_TYPE_UIH 0xEF
static const value_string vs_ctl[] = {
/* masked 0xfc */
@ -616,22 +625,73 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data
/* payload length */
offset = dissect_btrfcomm_payload_length(tvb, offset, rfcomm_tree, &frame_len);
if (dlci && (frame_len || (frame_type == 0xef) || (frame_type == 0x2f))) {
wmem_tree_key_t key[10];
guint32 k_interface_id;
guint32 k_adapter_id;
guint32 k_sdp_psm;
guint32 k_direction;
guint32 k_bd_addr_oui;
guint32 k_bd_addr_id;
guint32 k_service_type;
guint32 k_service_channel;
guint32 k_frame_number;
if (dlci && (frame_len || (frame_type == FRAME_TYPE_UIH) || (frame_type == FRAME_TYPE_SABM))) {
wmem_tree_key_t key[10];
guint32 k_interface_id;
guint32 k_adapter_id;
guint32 k_psm;
guint32 k_direction;
guint32 k_bd_addr_oui;
guint32 k_bd_addr_id;
guint32 k_service_type;
guint32 k_frame_number;
guint32 k_chandle;
guint32 k_channel;
service_direction_t *service_direction;
wmem_tree_t *subtree;
k_interface_id = l2cap_data->interface_id;
k_adapter_id = l2cap_data->adapter_id;
k_sdp_psm = SDP_PSM_DEFAULT;
k_direction = (l2cap_data->is_local_psm) ? P2P_DIR_SENT : P2P_DIR_RECV;
k_chandle = l2cap_data->chandle;
k_psm = l2cap_data->psm;
k_channel = dlci >> 1;
k_frame_number = pinfo->fd->num;
key[0].length = 1;
key[0].key = &k_interface_id;
key[1].length = 1;
key[1].key = &k_adapter_id;
key[2].length = 1;
key[2].key = &k_chandle;
key[3].length = 1;
key[3].key = &k_psm;
key[4].length = 1;
key[4].key = &k_channel;
if (!pinfo->fd->flags.visited && frame_type == FRAME_TYPE_SABM) {
key[5].length = 0;
key[5].key = NULL;
subtree = (wmem_tree_t *) wmem_tree_lookup32_array(service_directions, key);
service_direction = (subtree) ? (service_direction_t *) wmem_tree_lookup32_le(subtree, k_frame_number) : NULL;
if (service_direction && service_direction->end_in == G_MAXUINT32) {
service_direction->end_in = k_frame_number;
}
key[5].length = 1;
key[5].key = &k_frame_number;
key[6].length = 0;
key[6].key = NULL;
service_direction = wmem_new(wmem_file_scope(), service_direction_t);
service_direction->direction = (pinfo->p2p_dir == P2P_DIR_RECV) ? P2P_DIR_SENT : P2P_DIR_RECV;
service_direction->end_in = G_MAXUINT32;
wmem_tree_insert32_array(service_directions, key, service_direction);
}
key[5].length = 0;
key[5].key = NULL;
subtree = (wmem_tree_t *) wmem_tree_lookup32_array(service_directions, key);
service_direction = (subtree) ? (service_direction_t *) wmem_tree_lookup32_le(subtree, k_frame_number) : NULL;
if (service_direction && service_direction->end_in > k_frame_number) {
k_direction = service_direction->direction;
} else {
k_direction = (l2cap_data->is_local_psm) ? P2P_DIR_SENT : P2P_DIR_RECV;
}
k_psm = SDP_PSM_DEFAULT;
if (k_direction == P2P_DIR_RECV) {
k_bd_addr_oui = l2cap_data->remote_bd_addr_oui;
k_bd_addr_id = l2cap_data->remote_bd_addr_id;
@ -640,15 +700,9 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data
k_bd_addr_id = 0;
}
k_service_type = BTSDP_RFCOMM_PROTOCOL_UUID;
k_service_channel = dlci >> 1;
k_frame_number = pinfo->fd->num;
key[0].length = 1;
key[0].key = &k_interface_id;
key[1].length = 1;
key[1].key = &k_adapter_id;
key[2].length = 1;
key[2].key = &k_sdp_psm;
key[2].key = &k_psm;
key[3].length = 1;
key[3].key = &k_direction;
key[4].length = 1;
@ -658,7 +712,7 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data
key[6].length = 1;
key[6].key = &k_service_type;
key[7].length = 1;
key[7].key = &k_service_channel;
key[7].key = &k_channel;
key[8].length = 1;
key[8].key = &k_frame_number;
key[9].length = 0;
@ -685,12 +739,12 @@ dissect_btrfcomm(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data
col_append_fstr(pinfo->cinfo, COL_INFO, "%s Channel=%u ",
val_to_str_const(frame_type, vs_frame_type_short, "Unknown"), dlci >> 1);
if (dlci && (frame_type == 0x2f))
if (dlci && (frame_type == FRAME_TYPE_SABM))
col_append_fstr(pinfo->cinfo, COL_INFO, "(%s) ",
val_to_str_ext_const(service_info->uuid.bt_uuid, &bt_sig_uuid_vals_ext, "Unknown"));
/* UID frame */
if ((frame_type == 0xef) && dlci && pf_flag) {
if ((frame_type == FRAME_TYPE_UIH) && dlci && pf_flag) {
col_append_str(pinfo->cinfo, COL_INFO, "UID ");
/* add credit based flow control byte */
@ -1072,6 +1126,8 @@ proto_register_btrfcomm(void)
expert_btrfcomm = expert_register_protocol(proto_btrfcomm);
expert_register_field_array(expert_btrfcomm, ei, array_length(ei));
service_directions = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());
rfcomm_service_dissector_table = register_dissector_table("btrfcomm.service", "BT RFCOMM Service", FT_UINT16, BASE_HEX);
rfcomm_channel_dissector_table = register_dissector_table("btrfcomm.channel", "BT RFCOMM Channel", FT_UINT16, BASE_DEC);

View File

@ -347,6 +347,9 @@ static expert_field ei_data_element_value_large = EI_INIT;
static dissector_handle_t btsdp_handle;
static dissector_table_t btrfcomm_service_table;
static dissector_table_t btl2cap_service_table;
static wmem_tree_t *tid_requests = NULL;
static wmem_tree_t *continuation_states = NULL;
static wmem_tree_t *record_handle_services = NULL;
@ -858,16 +861,27 @@ service_info_t* btsdp_get_service_info(wmem_tree_key_t* key)
}
static uuid_t
get_most_specified_uuid(wmem_array_t *uuid_array)
get_specified_uuid(wmem_array_t *uuid_array)
{
uuid_t uuid;
/* TODO: For now try to use first (most specified) UUID, this may sometimes fail */
/* Try to find UUID that is already use in RFCOMM or L2CAP, otherwise try to
return last one (most generic).
NOTE: UUIDs in array are from (most specified) to (most generic) */
if (uuid_array) {
guint32 i_uuid;
guint32 size;
uuid_t *p_uuid = NULL;
if (wmem_array_get_count(uuid_array) > 0)
p_uuid = (uuid_t *) wmem_array_index(uuid_array, 0);
size = wmem_array_get_count(uuid_array);
for (i_uuid = 0; i_uuid < size; i_uuid += 1) {
p_uuid = (uuid_t *) wmem_array_index(uuid_array, i_uuid);
if (dissector_get_uint_handle(btrfcomm_service_table, p_uuid->bt_uuid))
break;
if (dissector_get_uint_handle(btl2cap_service_table, p_uuid->bt_uuid))
break;
}
if (p_uuid) return *p_uuid;
}
@ -3617,7 +3631,7 @@ dissect_sdp_service_attribute_list(proto_tree *tree, tvbuff_t *tvb, gint offset,
number_of_attributes += 1;
}
uuid = get_most_specified_uuid(uuid_array);
uuid = get_specified_uuid(uuid_array);
if (uuid.size == 0 && service_uuid)
uuid = *service_uuid;
@ -3950,7 +3964,7 @@ dissect_sdp_service_attribute_request(proto_tree *tree, tvbuff_t *tvb,
offset += 2;
uuid_array = get_uuids(pinfo, record_handle, l2cap_data);
uuid = get_most_specified_uuid(uuid_array);
uuid = get_specified_uuid(uuid_array);
offset += dissect_attribute_id_list(tree, tvb, offset, pinfo, &uuid);
@ -3988,7 +4002,7 @@ dissect_sdp_service_attribute_response(proto_tree *tree, tvbuff_t *tvb,
wmem_array_t *uuid_array;
uuid_array = get_uuids(pinfo, record_handle, l2cap_data);
uuid = get_most_specified_uuid(uuid_array);
uuid = get_specified_uuid(uuid_array);
} else {
memset(&uuid, 0, sizeof(uuid_t));
}
@ -4081,7 +4095,7 @@ dissect_sdp_service_search_attribute_request(proto_tree *tree, tvbuff_t *tvb,
proto_tree_add_item(tree, hf_maximum_attribute_byte_count, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
uuid = get_most_specified_uuid(uuid_array);
uuid = get_specified_uuid(uuid_array);
offset += dissect_attribute_id_list(tree, tvb, offset, pinfo, &uuid);
@ -4115,7 +4129,7 @@ dissect_sdp_service_search_attribute_response(proto_tree *tree, tvbuff_t *tvb,
PDU_TYPE_SERVICE_SEARCH_ATTRIBUTE, &new_tvb, &is_first,
&is_continued, &uuid_array, NULL, l2cap_data);
uuid = get_most_specified_uuid(uuid_array);
uuid = get_specified_uuid(uuid_array);
if (is_first && !is_continued) {
dissect_sdp_service_attribute_list_array(tree, tvb, offset, pinfo,
@ -5668,6 +5682,9 @@ proto_reg_handoff_btsdp(void)
{
dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_SDP, btsdp_handle);
dissector_add_handle("btl2cap.cid", btsdp_handle);
btrfcomm_service_table = find_dissector_table("btrfcomm.service");
btl2cap_service_table = find_dissector_table("btl2cap.service");
}
/*