wireshark/epan/dissectors/packet-btl2cap.c

2457 lines
95 KiB
C

/* packet-btl2cap.c
* Routines for the Bluetooth L2CAP dissection
* Copyright 2002, Christoph Scholz <scholz@cs.uni-bonn.de>
* From: http://affix.sourceforge.net/archive/ethereal_affix-3.patch
*
* Refactored for wireshark checkin
* Ronnie Sahlberg 2006
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "config.h"
#include <epan/packet.h>
#include <epan/exceptions.h>
#include <epan/expert.h>
#include <epan/tap.h>
#include <epan/wmem/wmem.h>
#include <epan/decode_as.h>
#include "packet-bluetooth-hci.h"
#include "packet-bthci_acl.h"
#include "packet-btsdp.h"
#include "packet-btl2cap.h"
/* Initialize the protocol and registered fields */
static int proto_btl2cap = -1;
static int hf_btl2cap_length = -1;
static int hf_btl2cap_cid = -1;
static int hf_btl2cap_payload = -1;
static int hf_btl2cap_command = -1;
static int hf_btl2cap_cmd_code = -1;
static int hf_btl2cap_cmd_ident = -1;
static int hf_btl2cap_cmd_length = -1;
static int hf_btl2cap_cmd_data = -1;
static int hf_btl2cap_psm = -1;
static int hf_btl2cap_psm_dynamic = -1;
static int hf_btl2cap_scid = -1;
static int hf_btl2cap_dcid = -1;
static int hf_btl2cap_icid = -1;
static int hf_btl2cap_controller = -1;
static int hf_btl2cap_dcontroller = -1;
static int hf_btl2cap_result = -1;
static int hf_btl2cap_move_result = -1;
static int hf_btl2cap_move_confirmation_result = -1;
static int hf_btl2cap_status = -1;
static int hf_btl2cap_rej_reason = -1;
static int hf_btl2cap_sig_mtu = -1;
static int hf_btl2cap_info_mtu = -1;
static int hf_btl2cap_info_flowcontrol = -1;
static int hf_btl2cap_info_retransmission = -1;
static int hf_btl2cap_info_bidirqos = -1;
static int hf_btl2cap_info_enh_retransmission = -1;
static int hf_btl2cap_info_streaming = -1;
static int hf_btl2cap_info_fcs = -1;
static int hf_btl2cap_info_flow_spec = -1;
static int hf_btl2cap_info_fixedchan = -1;
static int hf_btl2cap_info_fixedchans = -1;
static int hf_btl2cap_info_fixedchans_null = -1;
static int hf_btl2cap_info_fixedchans_signal = -1;
static int hf_btl2cap_info_fixedchans_connless = -1;
static int hf_btl2cap_info_fixedchans_amp_man = -1;
static int hf_btl2cap_info_fixedchans_amp_test = -1;
static int hf_btl2cap_info_window = -1;
static int hf_btl2cap_info_unicast = -1;
static int hf_btl2cap_info_type = -1;
static int hf_btl2cap_info_result = -1;
static int hf_btl2cap_continuation_flag = -1;
static int hf_btl2cap_configuration_result = -1;
static int hf_btl2cap_info_extfeatures = -1;
static int hf_btl2cap_option = -1;
static int hf_btl2cap_option_type = -1;
static int hf_btl2cap_option_length = -1;
static int hf_btl2cap_option_mtu = -1;
static int hf_btl2cap_option_flushTO = -1;
static int hf_btl2cap_option_flush_to_us = -1;
static int hf_btl2cap_option_flags = -1;
static int hf_btl2cap_option_service_type = -1;
static int hf_btl2cap_option_tokenrate = -1;
static int hf_btl2cap_option_tokenbucketsize = -1;
static int hf_btl2cap_option_peakbandwidth = -1;
static int hf_btl2cap_option_latency = -1;
static int hf_btl2cap_option_delayvariation = -1;
static int hf_btl2cap_option_retransmissionmode = -1;
static int hf_btl2cap_option_txwindow = -1;
static int hf_btl2cap_option_maxtransmit = -1;
static int hf_btl2cap_option_retransmittimeout = -1;
static int hf_btl2cap_option_monitortimeout = -1;
static int hf_btl2cap_option_mps = -1;
static int hf_btl2cap_option_fcs = -1;
static int hf_btl2cap_option_window = -1;
static int hf_btl2cap_option_identifier = -1;
static int hf_btl2cap_option_sdu_size = -1;
static int hf_btl2cap_option_sdu_arrival_time = -1;
static int hf_btl2cap_option_access_latency = -1;
static int hf_btl2cap_control = -1;
static int hf_btl2cap_control_sar = -1;
static int hf_btl2cap_control_reqseq = -1;
static int hf_btl2cap_control_txseq = -1;
static int hf_btl2cap_control_retransmissiondisable = -1;
static int hf_btl2cap_control_supervisory = -1;
static int hf_btl2cap_control_type = -1;
static int hf_btl2cap_fcs = -1;
static int hf_btl2cap_sdulength = -1;
static int hf_btl2cap_continuation_to = -1;
static int hf_btl2cap_reassembled_in = -1;
static int hf_btl2cap_min_interval = -1;
static int hf_btl2cap_max_interval = -1;
static int hf_btl2cap_slave_latency = -1;
static int hf_btl2cap_timeout_multiplier = -1;
static int hf_btl2cap_conn_param_result = -1;
/* Initialize the subtree pointers */
static gint ett_btl2cap = -1;
static gint ett_btl2cap_cmd = -1;
static gint ett_btl2cap_option = -1;
static gint ett_btl2cap_extfeatures = -1;
static gint ett_btl2cap_fixedchans = -1;
static gint ett_btl2cap_control = -1;
static expert_field ei_btl2cap_parameter_mismatch = EI_INIT;
static expert_field ei_btl2cap_sdulength_bad = EI_INIT;
static expert_field ei_btl2cap_length_bad = EI_INIT;
/* Initialize dissector table */
static dissector_table_t l2cap_psm_dissector_table;
static dissector_table_t l2cap_cid_dissector_table;
static dissector_table_t l2cap_service_dissector_table;
/* This table maps cid values to psm values.
* The same table is used both for SCID and DCID.
* For received CIDs we 'or' the cid with 0x80000000 in this table
*/
static wmem_tree_t *cid_to_psm_table = NULL;
static wmem_tree_t *sdp_service_infos = NULL;
typedef struct _config_data_t {
guint8 mode;
guint8 txwindow;
wmem_tree_t *start_fragments; /* indexed by pinfo->fd->num */
} config_data_t;
typedef struct _sdu_reassembly_t
{
guint8 *reassembled;
guint8 seq;
guint32 first_frame;
guint32 last_frame;
guint16 tot_len;
int cur_off; /* counter used by reassembly */
} sdu_reassembly_t;
typedef struct _psm_data_t {
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
guint32 scid;
guint32 dcid;
guint32 first_scid_frame;
guint32 first_dcid_frame;
guint16 psm;
gboolean local_service;
guint32 disconnect_in_frame;
config_data_t in;
config_data_t out;
} psm_data_t;
static const value_string command_code_vals[] = {
{ 0x01, "Command Reject" },
{ 0x02, "Connection Request" },
{ 0x03, "Connection Response" },
{ 0x04, "Configure Request" },
{ 0x05, "Configure Response" },
{ 0x06, "Disconnect Request" },
{ 0x07, "Disconnect Response" },
{ 0x08, "Echo Request" },
{ 0x09, "Echo Response" },
{ 0x0A, "Information Request" },
{ 0x0B, "Information Response" },
{ 0x0C, "Create Channel Request" },
{ 0x0D, "Create Channel Response" },
{ 0x0E, "Move Channel Request" },
{ 0x0F, "Move Channel Response" },
{ 0x10, "Move Channel Confirmation" },
{ 0x11, "Move Channel Confirmation Response" },
{ 0x12, "Connection Parameter Update Request" },
{ 0x13, "Connection Parameter Update Response" },
{ 0, NULL }
};
static const value_string psm_vals[] = {
{ 0x0001, "SDP" },
{ 0x0003, "RFCOMM" },
{ 0x0005, "TCS-BIN" },
{ 0x0007, "TCS-BIN-CORDLESS" },
{ 0x000F, "BNEP" },
{ 0x0011, "HID-Control" },
{ 0x0013, "HID-Interrupt" },
{ 0x0015, "UPnP" },
{ 0x0017, "AVCTP-Control" },
{ 0x0019, "AVDTP" },
{ 0x001B, "AVCTP-Browsing" },
{ 0x001D, "UDI_C-Plane" },
{ 0x001F, "ATT" },
{ 0x0021, "3DSP" },
{ 0, NULL }
};
value_string_ext ext_psm_vals = VALUE_STRING_EXT_INIT(psm_vals);
static const value_string result_vals[] = {
{ 0x0000, "Successful" },
{ 0x0001, "Pending" },
{ 0x0002, "Refused - PSM not supported" },
{ 0x0003, "Refused - security block" },
{ 0x0004, "Refused - no resources available" },
{ 0x0005, "Refused - Controller ID not supported" },
{ 0, NULL }
};
static const value_string move_result_vals[] = {
{ 0x0000, "Success" },
{ 0x0001, "Pending" },
{ 0x0002, "Refused - Controller ID not supported" },
{ 0x0003, "Refused - New Controller ID is same as old" },
{ 0x0004, "Refused - Configuration not supported" },
{ 0x0005, "Refused - Move Channel collision" },
{ 0x0006, "Refused - Channel not allowed to be moved" },
{ 0, NULL }
};
static const value_string move_result_confirmation_vals[] = {
{ 0x0000, "Success - both sides succeed" },
{ 0x0001, "Failure - one or both sides refuse" },
{ 0, NULL }
};
static const value_string configuration_result_vals[] = {
{ 0x0000, "Success"},
{ 0x0001, "Failure - unacceptable parameters" },
{ 0x0002, "Failure - reject (no reason provided)" },
{ 0x0003, "Failure - unknown options" },
{ 0x0004, "Pending" },
{ 0x0005, "Failure - flow spec rejected" },
{ 0, NULL }
};
static const value_string conn_param_result_vals[] = {
{ 0x0000, "Accepted" },
{ 0x0001, "Rejected" },
{ 0, NULL }
};
static const value_string status_vals[] = {
{ 0x0000, "No further information available" },
{ 0x0001, "Authentication pending" },
{ 0x0002, "Authorization pending" },
{ 0, NULL }
};
static const value_string reason_vals[] = {
{ 0x0000, "Command not understood" },
{ 0x0001, "Signaling MTU exceeded" },
{ 0x0002, "Invalid CID in request" },
{ 0, NULL }
};
static const value_string info_type_vals[] = {
{ 0x0001, "Connectionless MTU" },
{ 0x0002, "Extended Features Mask" },
{ 0x0003, "Fixed Channels Supported" },
{ 0, NULL }
};
static const value_string info_result_vals[] = {
{ 0x0000, "Success" },
{ 0x0001, "Not Supported" },
{ 0, NULL }
};
static const value_string option_servicetype_vals[] = {
{ 0x00, "No traffic" },
{ 0x01, "Best effort (Default)" },
{ 0x02, "Guaranteed" },
{ 0, NULL }
};
static const value_string option_type_vals[] = {
{ 0x01, "Maximum Transmission Unit" },
{ 0x02, "Flush Timeout" },
{ 0x03, "Quality of Service" },
{ 0x04, "Retransmission and Flow Control" },
{ 0x05, "FCS" },
{ 0x06, "Extended Flow Specification" },
{ 0x07, "Extended Window Size" },
{ 0, NULL }
};
static const value_string option_retransmissionmode_vals[] = {
{ 0x00, "Basic Mode" },
{ 0x01, "Retransmission Mode" },
{ 0x02, "Flow Control Mode" },
{ 0x03, "Enhanced Retransmission Mode" },
{ 0x04, "Streaming Mode" },
{ 0, NULL }
};
static const value_string control_sar_vals[] = {
{ 0x00, "Unsegmented" },
{ 0x01, "Start" },
{ 0x02, "End" },
{ 0x03, "Continuation" },
{ 0, NULL }
};
static const value_string control_supervisory_vals[] = {
{ 0x00, "RR" },
{ 0x01, "REJ" },
{ 0x02, "RNR" },
{ 0x03, "SREJ" },
{ 0, NULL }
};
static const value_string control_type_vals[] = {
{ 0x00, "I-Frame" },
{ 0x01, "S-Frame" },
{ 0, NULL }
};
static const value_string option_fcs_vals[] = {
{ 0x00, "No FCS" },
{ 0x01, "16-bit FCS" },
{ 0, NULL }
};
static const value_string ctrl_id_code_vals[] = {
{ 0x00, "Bluetooth BR/EDR" },
{ 0x01, "Wifi 802.11" },
{ 0, NULL }
};
static const range_string cid_rvals[] = {
{ 0x0000, 0x0000, "Null identifier" },
{ 0x0001, 0x0001, "L2CAP Signaling Channel" },
{ 0x0002, 0x0002, "Connectionless Channel" },
{ 0x0003, 0x0003, "AMP Manager Protocol" },
{ 0x0004, 0x0004, "Attribute Protocol" },
{ 0x0005, 0x0005, "Low Energy L2CAP Signaling Channel" },
{ 0x0006, 0x0006, "Security Manager Protocol" },
{ 0x0007, 0x003E, "Reserved" },
{ 0x003F, 0x003F, "AMP Test Manager" },
{ 0x0040, 0xFFFF, "Dynamically Allocated Channel" },
{ 0, 0, NULL }
};
#define BTL2CAP_CID_CONV 0
#define BTL2CAP_PSM_CONV 1
#define BTL2CAP_SERV_CONV 2
void proto_register_btl2cap(void);
void proto_reg_handoff_btl2cap(void);
static void btl2cap_cid_prompt(packet_info *pinfo, gchar* result)
{
g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "L2CAP CID 0x%04x as",
GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_CID_CONV )));
}
static gpointer btl2cap_cid_value(packet_info *pinfo)
{
return p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_CID_CONV );
}
static void btl2cap_psm_prompt(packet_info *pinfo, gchar* result)
{
g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "L2CAP PSM 0x%04x as",
GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV )));
}
static gpointer btl2cap_psm_value(packet_info *pinfo)
{
return p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV );
}
static void btl2cap_serv_prompt(packet_info *pinfo, gchar* result)
{
g_snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "L2CAP SERVICE 0x%04x as",
GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_SERV_CONV )));
}
static gpointer btl2cap_serv_value(packet_info *pinfo)
{
return p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_SERV_CONV );
}
static guint16
get_service_uuid(packet_info *pinfo, btl2cap_data_t *l2cap_data, guint16 psm, gboolean is_local_psm)
{
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;
guint32 interface_id;
guint32 adapter_id;
guint32 remote_bd_addr_oui;
guint32 remote_bd_addr_id;
service_info_t *service_info;
interface_id = l2cap_data->interface_id;
adapter_id = l2cap_data->adapter_id;
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_sdp_psm = SDP_PSM_DEFAULT;
k_direction = (is_local_psm) ? P2P_DIR_SENT : P2P_DIR_RECV;
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;
} else {
k_bd_addr_oui = 0;
k_bd_addr_id = 0;
}
remote_bd_addr_oui = k_bd_addr_oui;
remote_bd_addr_id = k_bd_addr_id;
k_service_type = BTSDP_L2CAP_PROTOCOL_UUID;
k_service_channel = psm;
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[3].length = 1;
key[3].key = &k_direction;
key[4].length = 1;
key[4].key = &k_bd_addr_oui;
key[5].length = 1;
key[5].key = &k_bd_addr_id;
key[6].length = 1;
key[6].key = &k_service_type;
key[7].length = 1;
key[7].key = &k_service_channel;
key[8].length = 1;
key[8].key = &k_frame_number;
key[9].length = 0;
key[9].key = NULL;
if (sdp_service_infos)
service_info = (service_info_t *) wmem_tree_lookup32_array_le(sdp_service_infos, key);
else
service_info = NULL;
if (service_info && service_info->interface_id == interface_id &&
service_info->adapter_id == adapter_id &&
service_info->sdp_psm == SDP_PSM_DEFAULT &&
((service_info->direction == P2P_DIR_RECV &&
service_info->bd_addr_oui == remote_bd_addr_oui &&
service_info->bd_addr_id == remote_bd_addr_id) ||
(service_info->direction != P2P_DIR_RECV &&
service_info->bd_addr_oui == 0 &&
service_info->bd_addr_id == 0)) &&
service_info->type == BTSDP_L2CAP_PROTOCOL_UUID &&
service_info->channel == psm) {
return service_info->uuid.bt_uuid;
}
return 0;
}
static int
dissect_comrej(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
{
guint16 reason;
reason = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_rej_reason, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
switch (reason) {
case 0x0000: /* Command not understood */
break;
case 0x0001: /* Signaling MTU exceeded */
proto_tree_add_item(tree, hf_btl2cap_sig_mtu, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
break;
case 0x0002: /* Invalid CID in requets */
proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_btl2cap_dcid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
break;
default:
break;
}
return offset;
}
static int
dissect_connrequest(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, gboolean is_ch_request, bthci_acl_data_t *acl_data,
btl2cap_data_t *l2cap_data)
{
guint16 scid;
guint16 psm;
const gchar *psm_str = "<NONE>";
psm = tvb_get_letohs(tvb, offset);
if (p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV ) == NULL) {
p_add_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV, GUINT_TO_POINTER((guint)psm));
}
if (psm < BTL2CAP_DYNAMIC_PSM_START) {
proto_tree_add_item(tree, hf_btl2cap_psm, tvb, offset, 2, ENC_LITTLE_ENDIAN);
psm_str = val_to_str_const(psm, psm_vals, "Unknown PSM");
} else {
proto_item *item;
guint16 uuid;
item = proto_tree_add_item(tree, hf_btl2cap_psm_dynamic, tvb, offset, 2, ENC_LITTLE_ENDIAN);
uuid = get_service_uuid(pinfo, l2cap_data, psm, (pinfo->p2p_dir == P2P_DIR_RECV) ? TRUE : FALSE);
if (uuid) {
psm_str = val_to_str_ext_const(uuid, &vs_service_classes_ext, "Unknown PSM");
proto_item_append_text(item, " (%s)", psm_str);
}
}
offset += 2;
scid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s, SCID: 0x%04x)", psm_str, scid);
if (is_ch_request) {
proto_tree_add_item(tree, hf_btl2cap_controller, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
}
if (!pinfo->fd->flags.visited) {
wmem_tree_key_t key[6];
guint32 k_interface_id;
guint32 k_adapter_id;
guint32 k_chandle;
guint32 k_cid;
guint32 k_frame_number;
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
psm_data_t *psm_data;
interface_id = (acl_data) ? acl_data->interface_id : HCI_INTERFACE_AMP;
adapter_id = (acl_data) ? acl_data->adapter_id : HCI_ADAPTER_DEFAULT;
chandle = (acl_data) ? acl_data->chandle : 0;
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_chandle = chandle;
k_cid = scid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x80000000 : 0x00000000);
k_frame_number = pinfo->fd->num;
psm_data = wmem_new(wmem_file_scope(), psm_data_t);
psm_data->scid = (scid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x80000000 : 0x00000000));
psm_data->dcid = 0;
psm_data->psm = psm;
psm_data->first_scid_frame = 0;
psm_data->first_dcid_frame = 0;
psm_data->local_service = (pinfo->p2p_dir == P2P_DIR_RECV) ? TRUE : FALSE;
psm_data->in.mode = 0;
psm_data->in.txwindow = 0;
psm_data->in.start_fragments = wmem_tree_new(wmem_file_scope());
psm_data->out.mode = 0;
psm_data->out.txwindow = 0;
psm_data->out.start_fragments = wmem_tree_new(wmem_file_scope());
psm_data->interface_id = k_interface_id;
psm_data->adapter_id = k_adapter_id;
psm_data->chandle = k_chandle;
psm_data->disconnect_in_frame = G_MAXUINT32;
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_cid;
key[4].length = 1;
key[4].key = &k_frame_number;
key[5].length = 0;
key[5].key = NULL;
wmem_tree_insert32_array(cid_to_psm_table, key, psm_data);
}
return offset;
}
static int
dissect_movechanrequest(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
guint16 icid;
guint8 ctrl_id;
icid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_icid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
ctrl_id = tvb_get_guint8(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_dcontroller, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
col_append_fstr(pinfo->cinfo, COL_INFO, " (ICID: 0x%04x, move to %s)", icid,
val_to_str_const(ctrl_id, ctrl_id_code_vals, "Unknown controller"));
return offset;
}
static int
dissect_options(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree, int length, config_data_t *config_data)
{
proto_item *ti_option;
proto_tree *ti_option_subtree;
guint8 option_type, option_length;
while (length > 0) {
option_type = tvb_get_guint8(tvb, offset);
option_length = tvb_get_guint8(tvb, offset + 1);
ti_option = proto_tree_add_none_format(tree,
hf_btl2cap_option, tvb,
offset, option_length + 2,
"Option: ");
ti_option_subtree = proto_item_add_subtree(ti_option, ett_btl2cap_option);
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_length, tvb, offset + 1, 1, ENC_LITTLE_ENDIAN);
offset += 2;
if (option_length != 0) {
switch (option_type) {
case 0x01: /* MTU */
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_mtu, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_item_append_text(ti_option, "MTU");
break;
case 0x02: /* Flush timeout */
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_flushTO, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_item_append_text(ti_option, "Flush Timeout");
break;
case 0x03: /* QOS */
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_flags, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_service_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_tokenrate, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_tokenbucketsize, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_peakbandwidth, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_latency, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_delayvariation, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_item_append_text(ti_option, "QOS");
break;
case 0x04: /* Retransmission and Flow Control*/
if (config_data)
{
config_data->mode = tvb_get_guint8(tvb, offset);
config_data->txwindow = tvb_get_guint8(tvb, offset + 1);
}
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_retransmissionmode, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_txwindow, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_maxtransmit, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_retransmittimeout, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_monitortimeout, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_mps, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_item_append_text(ti_option, "Retransmission and Flow Control");
break;
case 0x05: /* FCS */
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_fcs, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_item_append_text(ti_option, "FCS");
break;
case 0x06: /* Extended Flow Specification */
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_identifier, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_service_type, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_sdu_size, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_sdu_arrival_time, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_access_latency, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_flush_to_us, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_item_append_text(ti_option, "Extended Flow Specification");
break;
case 0x07: /* Extended Window Size */
proto_tree_add_item(ti_option_subtree, hf_btl2cap_option_window, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_item_append_text(ti_option, "Extended Window Size");
break;
default:
proto_item_append_text(ti_option, "unknown");
offset += option_length;
break;
}
}
length -= (option_length + 2);
}
return offset;
}
static int
dissect_configrequest(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, guint16 length, bthci_acl_data_t *acl_data)
{
guint16 dcid;
dcid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_dcid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (DCID: 0x%04x)", dcid);
proto_tree_add_item(tree, hf_btl2cap_continuation_flag, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
if (tvb_reported_length_remaining(tvb, offset) > 0) {
psm_data_t *psm_data;
config_data_t *config_data;
wmem_tree_key_t key[6];
guint32 k_interface_id;
guint32 k_adapter_id;
guint32 k_chandle;
guint32 k_cid;
guint32 k_frame_number;
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
guint32 cid;
interface_id = (acl_data) ? acl_data->interface_id : HCI_INTERFACE_AMP;
adapter_id = (acl_data) ? acl_data->adapter_id : HCI_ADAPTER_DEFAULT;
chandle = (acl_data) ? acl_data->chandle : 0;
cid = dcid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x00000000 : 0x80000000);
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_chandle = chandle;
k_cid = cid;
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_cid;
key[4].length = 1;
key[4].key = &k_frame_number;
key[5].length = 0;
key[5].key = NULL;
psm_data = (psm_data_t *)wmem_tree_lookup32_array_le(cid_to_psm_table, key);
if (psm_data && psm_data->interface_id == interface_id &&
psm_data->adapter_id == adapter_id &&
psm_data->chandle == chandle &&
psm_data->dcid == cid &&
psm_data->disconnect_in_frame > pinfo->fd->num) {
if (pinfo->p2p_dir == P2P_DIR_RECV)
config_data = &(psm_data->out);
else
config_data = &(psm_data->in);
} else {
config_data = NULL;
}
offset = dissect_options(tvb, offset, pinfo, tree, length - 4, config_data);
}
return offset;
}
static int
dissect_inforequest(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
guint16 info_type;
info_type = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_info_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)", val_to_str_const(info_type, info_type_vals, "Unknown type"));
return offset;
}
static int
dissect_inforesponse(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
guint16 info_type, result;
info_type = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_info_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_info_result, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s, %s)",
val_to_str_const(info_type, info_type_vals, "Unknown type"),
val_to_str_const(result, info_result_vals, "Unknown result"));
if (tvb_reported_length_remaining(tvb, offset) > 0) {
proto_item *ti_features;
proto_tree *ti_features_subtree;
guint32 features;
switch (info_type) {
case 0x0001: /* Connectionless MTU */
proto_tree_add_item(tree, hf_btl2cap_info_mtu, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
break;
case 0x0002: /* Extended Features */
ti_features = proto_tree_add_none_format(tree,
hf_btl2cap_info_extfeatures, tvb,
offset, 4,
"Features: ");
ti_features_subtree = proto_item_add_subtree(ti_features, ett_btl2cap_extfeatures);
features = tvb_get_letohl(tvb, offset);
if (features & 0x1)
proto_item_append_text(ti_features, "FlowControl ");
if (features & 0x2)
proto_item_append_text(ti_features, "Retransmission ");
if (features & 0x4)
proto_item_append_text(ti_features, "BiDirQOS ");
if (features & 0x8)
proto_item_append_text(ti_features, "EnhRetransmission ");
if (features & 0x10)
proto_item_append_text(ti_features, "Streaming ");
if (features & 0x20)
proto_item_append_text(ti_features, "FCS ");
if (features & 0x40)
proto_item_append_text(ti_features, "FlowSpec ");
if (features & 0x80)
proto_item_append_text(ti_features, "FixedChan ");
if (features & 0x100)
proto_item_append_text(ti_features, "WindowSize ");
if (features & 0x200)
proto_item_append_text(ti_features, "Unicast ");
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_flowcontrol, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_retransmission, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_bidirqos, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_enh_retransmission, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_streaming, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_fcs, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_flow_spec, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_fixedchan, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_window, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_unicast, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
break;
case 0x0003: /* Fixed Channels Supported */
ti_features = proto_tree_add_none_format(tree,
hf_btl2cap_info_fixedchans, tvb,
offset, 8,
"Fixed Channels Supported:");
ti_features_subtree = proto_item_add_subtree(ti_features, ett_btl2cap_fixedchans);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_fixedchans_null, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_fixedchans_signal, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_fixedchans_connless, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_fixedchans_amp_man, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
proto_tree_add_item(ti_features_subtree, hf_btl2cap_info_fixedchans_amp_test, tvb, offset, 4, ENC_LITTLE_ENDIAN);
offset += 4;
break;
default:
proto_tree_add_item(tree, hf_btl2cap_cmd_data, tvb, offset, -1, ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
break;
}
}
return offset;
}
static int
dissect_configresponse(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, guint16 length, bthci_acl_data_t *acl_data)
{
guint16 scid;
guint16 result;
scid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_btl2cap_continuation_flag, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_configuration_result, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " - %s (SCID: 0x%04x)",
val_to_str_const(result, configuration_result_vals, "Unknown"), scid);
if (tvb_reported_length_remaining(tvb, offset) > 0) {
psm_data_t *psm_data;
config_data_t *config_data;
wmem_tree_key_t key[6];
guint32 k_interface_id;
guint32 k_adapter_id;
guint32 k_chandle;
guint32 k_cid;
guint32 k_frame_number;
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
guint32 cid;
interface_id = (acl_data) ? acl_data->interface_id : HCI_INTERFACE_AMP;
adapter_id = (acl_data) ? acl_data->adapter_id : HCI_ADAPTER_DEFAULT;
chandle = (acl_data) ? acl_data->chandle : 0;
cid = scid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x00000000 : 0x80000000);
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_chandle = chandle;
k_cid = cid;
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_cid;
key[4].length = 1;
key[4].key = &k_frame_number;
key[5].length = 0;
key[5].key = NULL;
psm_data = (psm_data_t *)wmem_tree_lookup32_array_le(cid_to_psm_table, key);
if (psm_data && psm_data->interface_id == interface_id &&
psm_data->adapter_id == adapter_id &&
psm_data->chandle == chandle &&
psm_data->scid == cid &&
psm_data->disconnect_in_frame > pinfo->fd->num) {
if (pinfo->p2p_dir == P2P_DIR_RECV)
config_data = &(psm_data->out);
else
config_data = &(psm_data->in);
} else {
config_data = NULL;
}
offset = dissect_options(tvb, offset, pinfo, tree, length - 6, config_data);
}
return offset;
}
static int
dissect_connresponse(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, bthci_acl_data_t *acl_data)
{
guint16 scid, dcid, result;
dcid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_dcid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
scid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_result, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(tree, hf_btl2cap_status, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
if (result == 0) {
col_append_fstr(pinfo->cinfo, COL_INFO, " - Success (SCID: 0x%04x, DCID: 0x%04x)", scid, dcid);
}
else {
col_append_fstr(pinfo->cinfo, COL_INFO, " - %s (SCID: 0x%04x)",
val_to_str_const(result, result_vals, "Unknown"), scid);
}
if (pinfo->fd->flags.visited == 0) {
psm_data_t *psm_data;
wmem_tree_key_t key[6];
guint32 k_interface_id;
guint32 k_adapter_id;
guint32 k_chandle;
guint32 k_cid;
guint32 k_frame_number;
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
guint32 cid;
interface_id = (acl_data) ? acl_data->interface_id : HCI_INTERFACE_AMP;
adapter_id = (acl_data) ? acl_data->adapter_id : HCI_ADAPTER_DEFAULT;
chandle = (acl_data) ? acl_data->chandle : 0;
cid = scid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x00000000 : 0x80000000);
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_chandle = chandle;
k_cid = cid;
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_cid;
key[4].length = 1;
key[4].key = &k_frame_number;
key[5].length = 0;
key[5].key = NULL;
psm_data = (psm_data_t *)wmem_tree_lookup32_array_le(cid_to_psm_table, key);
if (psm_data && psm_data->interface_id == interface_id &&
psm_data->adapter_id == adapter_id &&
psm_data->chandle == chandle &&
psm_data->scid == (scid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x00000000 : 0x80000000)) &&
psm_data->disconnect_in_frame > pinfo->fd->num) {
cid = dcid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x80000000 : 0x00000000);
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_chandle = chandle;
k_cid = cid;
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_cid;
key[4].length = 1;
key[4].key = &k_frame_number;
key[5].length = 0;
key[5].key = NULL;
psm_data->dcid = cid;
wmem_tree_insert32_array(cid_to_psm_table, key, psm_data);
}
}
return offset;
}
static int
dissect_chanresponse(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, bthci_acl_data_t *acl_data)
{
return dissect_connresponse(tvb, offset, pinfo, tree, acl_data);
}
static int
dissect_movechanresponse(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
guint16 icid, result;
icid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_icid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_move_result, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (ICID: 0x%04x, %s)", icid,
val_to_str_const(result, move_result_vals, "Unknown result"));
return offset;
}
static int
dissect_movechanconfirmation(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
guint16 icid, result;
icid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_icid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_move_confirmation_result, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (ICID: 0x%04x, %s)", icid,
val_to_str_const(result, move_result_confirmation_vals, "Unknown result"));
return offset;
}
static int
dissect_movechanconfirmationresponse(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
guint16 icid;
icid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_icid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (ICID: 0x%04x)", icid);
return offset;
}
static int
dissect_connparamrequest(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
proto_item *item;
guint16 max_interval, slave_latency;
item = proto_tree_add_item(tree, hf_btl2cap_min_interval, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_item_append_text(item, " (%g msec)", tvb_get_letohs(tvb, offset) * 1.25);
offset += 2;
item = proto_tree_add_item(tree, hf_btl2cap_max_interval, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_item_append_text(item, " (%g msec)", tvb_get_letohs(tvb, offset) * 1.25);
max_interval = tvb_get_letohs(tvb, offset);
offset += 2;
item = proto_tree_add_item(tree, hf_btl2cap_slave_latency, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_item_append_text(item, " LL Connection Events");
slave_latency = tvb_get_letohs(tvb, offset);
if(slave_latency >= 500 || slave_latency > 10.0 * tvb_get_letohs(tvb, offset + 2) / (max_interval *1.25))
expert_add_info(pinfo, item, &ei_btl2cap_parameter_mismatch);
offset += 2;
item = proto_tree_add_item(tree, hf_btl2cap_timeout_multiplier, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_item_append_text(item, " (%g sec)", tvb_get_letohs(tvb, offset) * 0.01);
offset += 2;
return offset;
}
static int
dissect_connparamresponse(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree)
{
guint16 result;
result = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_conn_param_result, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (%s)",
val_to_str_const(result, conn_param_result_vals, "Unknown result"));
return offset;
}
static int
dissect_disconnrequestresponse(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, bthci_acl_data_t *acl_data)
{
guint16 scid;
guint16 dcid;
dcid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_dcid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
scid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(tree, hf_btl2cap_scid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
col_append_fstr(pinfo->cinfo, COL_INFO, " (SCID: 0x%04x, DCID: 0x%04x)", scid, dcid);
if (!pinfo->fd->flags.visited) {
psm_data_t *psm_data;
wmem_tree_key_t key[6];
guint32 k_interface_id;
guint32 k_adapter_id;
guint32 k_chandle;
guint32 k_cid;
guint32 k_frame_number;
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
guint32 key_scid;
guint32 key_dcid;
interface_id = (acl_data) ? acl_data->interface_id : HCI_INTERFACE_AMP;
adapter_id = (acl_data) ? acl_data->adapter_id : HCI_ADAPTER_DEFAULT;
chandle = (acl_data) ? acl_data->chandle : 0;
key_dcid = dcid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x00000000 : 0x80000000);
key_scid = scid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x00000000 : 0x80000000);
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_chandle = chandle;
k_cid = key_dcid;
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_cid;
key[4].length = 1;
key[4].key = &k_frame_number;
key[5].length = 0;
key[5].key = NULL;
psm_data = (psm_data_t *)wmem_tree_lookup32_array_le(cid_to_psm_table, key);
if (psm_data && psm_data->interface_id == interface_id &&
psm_data->adapter_id == adapter_id &&
psm_data->chandle == chandle &&
psm_data->dcid == key_dcid &&
psm_data->disconnect_in_frame == G_MAXUINT32) {
psm_data->disconnect_in_frame = pinfo->fd->num;
}
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_chandle = chandle;
k_cid = key_scid;
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_cid;
key[4].length = 1;
key[4].key = &k_frame_number;
key[5].length = 0;
key[5].key = NULL;
psm_data = (psm_data_t *)wmem_tree_lookup32_array_le(cid_to_psm_table, key);
if (psm_data && psm_data->interface_id == interface_id &&
psm_data->adapter_id == adapter_id &&
psm_data->chandle == chandle &&
psm_data->scid == key_scid &&
psm_data->disconnect_in_frame == G_MAXUINT32) {
psm_data->disconnect_in_frame = pinfo->fd->num;
}
}
return offset;
}
static int
dissect_b_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
proto_tree *btl2cap_tree, guint16 cid, guint16 psm,
gboolean is_local_psm, guint16 length, int offset, btl2cap_data_t *l2cap_data)
{
tvbuff_t *next_tvb;
next_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset), length);
col_append_str(pinfo->cinfo, COL_INFO, "Connection oriented channel");
if (psm) {
proto_item *psm_item;
guint16 uuid;
if (p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV ) == NULL) {
p_add_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV, GUINT_TO_POINTER((guint)psm));
}
uuid = get_service_uuid(pinfo, l2cap_data, psm, is_local_psm);
if (p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_SERV_CONV ) == NULL) {
p_add_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_SERV_CONV, GUINT_TO_POINTER((guint)uuid));
}
if (psm < BTL2CAP_DYNAMIC_PSM_START) {
psm_item = proto_tree_add_uint(btl2cap_tree, hf_btl2cap_psm, tvb, offset, 0, psm);
}
else {
psm_item = proto_tree_add_uint(btl2cap_tree, hf_btl2cap_psm_dynamic, tvb, offset, 0, psm);
if (uuid)
proto_item_append_text(psm_item, ": %s",
val_to_str_ext_const(uuid, &vs_service_classes_ext, "Unknown service"));
}
PROTO_ITEM_SET_GENERATED(psm_item);
/* call next dissector */
if (!dissector_try_uint_new(l2cap_cid_dissector_table, (guint32) cid, next_tvb, pinfo, tree, TRUE, l2cap_data)) {
if (!dissector_try_uint_new(l2cap_psm_dissector_table, (guint32) psm, next_tvb, pinfo, tree, TRUE, l2cap_data)) {
/* not a known fixed PSM, try to find a registered service to a dynamic PSM */
if (!dissector_try_uint_new(l2cap_service_dissector_table, uuid, next_tvb, pinfo, tree, TRUE, l2cap_data)) {
/* unknown protocol. declare as data */
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, ENC_NA);
}
}
}
offset += tvb_length_remaining(tvb, offset);
} else {
if (!dissector_try_uint_new(l2cap_cid_dissector_table, (guint32) cid, next_tvb, pinfo, tree, TRUE, l2cap_data)) {
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, ENC_NA);
offset += tvb_length_remaining(tvb, offset);
}
}
return offset;
}
static int
dissect_i_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
proto_tree *btl2cap_tree, psm_data_t *psm_data, guint16 length,
gint offset, config_data_t *config_data, btl2cap_data_t *l2cap_data)
{
tvbuff_t *next_tvb = NULL;
guint16 control, segment;
guint16 sdulen;
proto_item* ti_control;
proto_tree* ti_control_subtree;
sdu_reassembly_t *mfp = NULL;
guint16 psm = (psm_data ? psm_data->psm : 0);
control = tvb_get_letohs(tvb, offset);
segment = (control & 0xC000) >> 14;
switch (segment) {
case 0:
col_append_str(pinfo->cinfo, COL_INFO, "[I] Unsegmented SDU");
break;
case 1:
col_append_str(pinfo->cinfo, COL_INFO, "[I] Start SDU");
break;
case 2:
col_append_str(pinfo->cinfo, COL_INFO, "[I] End SDU");
break;
case 3:
col_append_str(pinfo->cinfo, COL_INFO, "[I] Continuation SDU");
break;
}
ti_control = proto_tree_add_none_format(btl2cap_tree, hf_btl2cap_control, tvb,
offset, 2, "Control: %s reqseq:%d r:%d txseq:%d",
val_to_str_const((control & 0xC000) >> 14, control_sar_vals, "unknown"),
(control & 0x3F00) >> 8,
(control & 0x0080) >> 7,
(control & 0x007E) >> 1);
ti_control_subtree = proto_item_add_subtree(ti_control, ett_btl2cap_control);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_sar, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_reqseq, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_retransmissiondisable, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_txseq, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
/*Segmented frames with SAR = start have an extra SDU length header field*/
if (segment == 0x01) {
proto_item *pi;
sdulen = tvb_get_letohs(tvb, offset);
pi = proto_tree_add_item(btl2cap_tree, hf_btl2cap_sdulength, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
/* Detect malformed data */
if (length <= 6) {
expert_add_info_format(pinfo, pi, &ei_btl2cap_sdulength_bad,
"SDU length too short: %u", length);
THROW(ReportedBoundsError);
}
length -= 6; /*Control, SDUlength, FCS*/
if (sdulen < length) {
sdulen = length;
expert_add_info_format(pinfo, pi, &ei_btl2cap_sdulength_bad,
"SDU length less than length of first packet (%u < %u)", sdulen, length);
}
if (!pinfo->fd->flags.visited) {
mfp = wmem_new(wmem_file_scope(), sdu_reassembly_t);
mfp->first_frame = pinfo->fd->num;
mfp->last_frame = 0;
mfp->tot_len = sdulen;
mfp->reassembled = (guint8 *) wmem_alloc(wmem_file_scope(), sdulen);
tvb_memcpy(tvb, mfp->reassembled, offset, sdulen);
mfp->cur_off = sdulen;
wmem_tree_insert32(config_data->start_fragments, pinfo->fd->num, mfp);
} else {
mfp = (sdu_reassembly_t *)wmem_tree_lookup32(config_data->start_fragments, pinfo->fd->num);
}
if (mfp != NULL && mfp->last_frame) {
proto_item *item;
item = proto_tree_add_uint(btl2cap_tree, hf_btl2cap_reassembled_in, tvb, 0, 0, mfp->last_frame);
PROTO_ITEM_SET_GENERATED(item);
col_append_fstr(pinfo->cinfo, COL_INFO, "[Reassembled in #%u] ", mfp->last_frame);
}
} else {
if (length <= 4) {
expert_add_info_format(pinfo, btl2cap_tree, &ei_btl2cap_length_bad,
"Control / FCS length too short: %u", length);
THROW(ReportedBoundsError);
}
length -= 4; /*Control, FCS*/
}
if (segment == 0x02 || segment == 0x03) {
mfp = (sdu_reassembly_t *)wmem_tree_lookup32_le(config_data->start_fragments, pinfo->fd->num);
if (!pinfo->fd->flags.visited) {
if (mfp != NULL && !mfp->last_frame && (mfp->tot_len>=mfp->cur_off + length)) {
tvb_memcpy(tvb, mfp->reassembled + mfp->cur_off, offset, length);
mfp->cur_off += length;
if (segment == 0x02) {
mfp->last_frame = pinfo->fd->num;
}
}
}
if (mfp) {
proto_item *item;
item = proto_tree_add_uint(btl2cap_tree, hf_btl2cap_continuation_to, tvb, 0, 0, mfp->first_frame);
PROTO_ITEM_SET_GENERATED(item);
col_append_fstr(pinfo->cinfo, COL_INFO, "[Continuation to #%u] ", mfp->first_frame);
}
}
if (segment == 0x02 && mfp != NULL && mfp->last_frame == pinfo->fd->num) {
next_tvb = tvb_new_child_real_data(tvb, (guint8 *)mfp->reassembled, mfp->tot_len, mfp->tot_len);
add_new_data_source(pinfo, next_tvb, "Reassembled L2CAP");
}
/*pass up to higher layer if we have a complete packet*/
if (segment == 0x00) {
next_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset) - 2, length);
}
if (next_tvb) {
if (psm) {
proto_item *psm_item;
guint16 uuid;
if (p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV ) == NULL) {
p_add_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV, GUINT_TO_POINTER((guint)psm));
}
uuid = get_service_uuid(pinfo, l2cap_data, psm, psm_data->local_service);
if (p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_SERV_CONV ) == NULL) {
p_add_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_SERV_CONV, GUINT_TO_POINTER((guint)uuid));
}
if (psm < BTL2CAP_DYNAMIC_PSM_START) {
psm_item = proto_tree_add_uint(btl2cap_tree, hf_btl2cap_psm, tvb, offset, 0, psm);
} else {
psm_item = proto_tree_add_uint(btl2cap_tree, hf_btl2cap_psm_dynamic, tvb, offset, 0, psm);
if (uuid)
proto_item_append_text(psm_item, " (%s)",
val_to_str_ext_const(uuid, &vs_service_classes_ext, "Unknown service"));
}
PROTO_ITEM_SET_GENERATED(psm_item);
/* call next dissector */
if (!dissector_try_uint_new(l2cap_psm_dissector_table, (guint32) psm, next_tvb, pinfo, tree, TRUE, l2cap_data)) {
/* not a known fixed PSM, try to find a registered service to a dynamic PSM */
if (!dissector_try_uint_new(l2cap_service_dissector_table, uuid, next_tvb, pinfo, tree, TRUE, l2cap_data)) {
/* unknown protocol. declare as data */
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, next_tvb, 0, tvb_length(next_tvb), ENC_NA);
}
}
}
else {
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, next_tvb, 0, tvb_length(next_tvb), ENC_NA);
}
}
offset += (tvb_length_remaining(tvb, offset) - 2);
proto_tree_add_item(btl2cap_tree, hf_btl2cap_fcs, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
return offset;
}
static int
dissect_s_frame(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree _U_, proto_tree *btl2cap_tree,
guint16 psm _U_, guint16 length _U_, int offset, config_data_t *config_data _U_)
{
proto_item *ti_control;
proto_tree *ti_control_subtree;
guint16 control;
control = tvb_get_letohs(tvb, offset);
switch ((control & 0x000C) >> 2) {
case 0:
col_append_str(pinfo->cinfo, COL_INFO, "[S] Receiver Ready");
break;
case 1:
col_append_str(pinfo->cinfo, COL_INFO, "[S] Reject");
break;
default:
col_append_str(pinfo->cinfo, COL_INFO, "[S] Unknown supervisory frame");
break;
}
ti_control = proto_tree_add_none_format(btl2cap_tree, hf_btl2cap_control, tvb,
offset, 2, "Control: %s reqseq:%d r:%d",
val_to_str_const((control & 0x000C) >> 2, control_supervisory_vals, "unknown"),
(control & 0x3F00) >> 8,
(control & 0x0080) >> 7);
ti_control_subtree = proto_item_add_subtree(ti_control, ett_btl2cap_control);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_reqseq, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_retransmissiondisable, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_supervisory, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(btl2cap_tree, hf_btl2cap_fcs, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
return offset;
}
static gint
dissect_btl2cap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data)
{
gint offset = 0;
proto_item *ti;
proto_tree *btl2cap_tree;
guint16 length;
guint16 cid;
guint16 psm;
guint16 control;
tvbuff_t *next_tvb = NULL;
psm_data_t *psm_data;
bthci_acl_data_t *acl_data;
btl2cap_data_t *l2cap_data;
ti = proto_tree_add_item(tree, proto_btl2cap, tvb, offset, -1, ENC_NA);
btl2cap_tree = proto_item_add_subtree(ti, ett_btl2cap);
col_set_str(pinfo->cinfo, COL_PROTOCOL, "L2CAP");
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_add_fstr(pinfo->cinfo, COL_INFO, "Unknown direction %d ",
pinfo->p2p_dir);
break;
}
acl_data = (bthci_acl_data_t *) data;
DISSECTOR_ASSERT(acl_data);
length = tvb_get_letohs(tvb, offset);
proto_tree_add_item(btl2cap_tree, hf_btl2cap_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
cid = tvb_get_letohs(tvb, offset);
proto_tree_add_item(btl2cap_tree, hf_btl2cap_cid, tvb, offset, 2, ENC_LITTLE_ENDIAN);
if (p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_CID_CONV ) == NULL) {
p_add_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_CID_CONV, GUINT_TO_POINTER((guint)cid));
}
offset += 2;
l2cap_data = wmem_new(wmem_packet_scope(), btl2cap_data_t);
l2cap_data->interface_id = (acl_data) ? acl_data->interface_id : HCI_INTERFACE_AMP;
l2cap_data->adapter_id = (acl_data) ? acl_data->adapter_id : HCI_ADAPTER_DEFAULT;
l2cap_data->chandle = (acl_data) ? acl_data->chandle : 0;
l2cap_data->cid = cid;
l2cap_data->psm = 0;
l2cap_data->first_scid_frame = 0;
l2cap_data->remote_bd_addr_oui = (acl_data) ? acl_data->remote_bd_addr_oui : 0;
l2cap_data->remote_bd_addr_id = (acl_data) ? acl_data->remote_bd_addr_id : 0;
if (cid == BTL2CAP_FIXED_CID_SIGNAL || cid == BTL2CAP_FIXED_CID_LE_SIGNAL) {
/* This is a command packet*/
while (offset < (length + 4)) {
proto_item *ti_command;
proto_tree *btl2cap_cmd_tree;
guint8 cmd_code;
guint16 cmd_length;
const gchar *cmd_str;
ti_command = proto_tree_add_none_format(btl2cap_tree,
hf_btl2cap_command, tvb,
offset, length,
"Command: ");
btl2cap_cmd_tree = proto_item_add_subtree(ti_command, ett_btl2cap_cmd);
cmd_code = tvb_get_guint8(tvb, offset);
proto_tree_add_item(btl2cap_cmd_tree, hf_btl2cap_cmd_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
proto_tree_add_item(btl2cap_cmd_tree, hf_btl2cap_cmd_ident, tvb, offset, 1, ENC_LITTLE_ENDIAN);
offset += 1;
cmd_length = tvb_get_letohs(tvb, offset);
proto_tree_add_item(btl2cap_cmd_tree, hf_btl2cap_cmd_length, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_item_set_len(ti_command, cmd_length + 4);
offset += 2;
cmd_str = val_to_str_const(cmd_code, command_code_vals, "Unknown cmd");
proto_item_append_text(ti_command, "%s", cmd_str);
col_append_str(pinfo->cinfo, COL_INFO, cmd_str);
switch (cmd_code) {
case 0x01: /* Command Reject */
offset = dissect_comrej(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
case 0x02: /* Connection Request */
offset = dissect_connrequest(tvb, offset, pinfo, btl2cap_cmd_tree, FALSE, acl_data, l2cap_data);
break;
case 0x03: /* Connection Response */
offset = dissect_connresponse(tvb, offset, pinfo, btl2cap_cmd_tree, acl_data);
break;
case 0x04: /* Configure Request */
offset = dissect_configrequest(tvb, offset, pinfo, btl2cap_cmd_tree, cmd_length, acl_data);
break;
case 0x05: /* Configure Response */
offset = dissect_configresponse(tvb, offset, pinfo, btl2cap_cmd_tree, cmd_length, acl_data);
break;
case 0x06: /* Disconnect Request */
offset = dissect_disconnrequestresponse(tvb, offset, pinfo, btl2cap_cmd_tree, acl_data);
break;
case 0x07: /* Disconnect Response */
offset = dissect_disconnrequestresponse(tvb, offset, pinfo, btl2cap_cmd_tree, acl_data);
break;
case 0x08: /* Echo Request */
offset += tvb_reported_length_remaining(tvb, offset);
break;
case 0x09: /* Echo Response */
offset += tvb_reported_length_remaining(tvb, offset);
break;
case 0x0a: /* Information Request */
offset = dissect_inforequest(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
case 0x0b: /* Information Response */
offset = dissect_inforesponse(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
case 0x0c: /* Create Channel Request */
offset = dissect_connrequest(tvb, offset, pinfo, btl2cap_cmd_tree, TRUE, acl_data, l2cap_data);
break;
case 0x0d: /* Create Channel Response */
offset = dissect_chanresponse(tvb, offset, pinfo, btl2cap_cmd_tree, acl_data);
break;
case 0x0e: /* Move Channel Request */
offset = dissect_movechanrequest(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
case 0x0f: /* Move Channel Response */
offset = dissect_movechanresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
case 0x10: /* Move Channel Confirmation */
offset = dissect_movechanconfirmation(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
case 0x11: /* Move Channel Confirmation Response */
offset = dissect_movechanconfirmationresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
case 0x12: /* Connection Parameter Request */
offset = dissect_connparamrequest(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
case 0x13: /* Connection Parameter Response */
offset = dissect_connparamresponse(tvb, offset, pinfo, btl2cap_cmd_tree);
break;
default:
proto_tree_add_item(btl2cap_cmd_tree, hf_btl2cap_cmd_data, tvb, offset, -1, ENC_NA);
offset += tvb_reported_length_remaining(tvb, offset);
break;
}
}
}
else if (cid == BTL2CAP_FIXED_CID_CONNLESS) { /* Connectionless reception channel */
col_append_str(pinfo->cinfo, COL_INFO, "Connectionless reception channel");
psm = tvb_get_letohs(tvb, offset);
l2cap_data->psm = psm;
if (p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV ) == NULL) {
p_add_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_PSM_CONV, GUINT_TO_POINTER((guint)psm));
}
proto_tree_add_item(btl2cap_tree, hf_btl2cap_psm, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
next_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset), length);
/* call next dissector */
if (!dissector_try_uint_new(l2cap_psm_dissector_table, (guint32) psm, next_tvb, pinfo, tree, TRUE, l2cap_data)) {
/* not a known fixed PSM, try to find a registered service to a dynamic PSM */
guint16 uuid;
uuid = get_service_uuid(pinfo, l2cap_data, psm, (pinfo->p2p_dir == P2P_DIR_RECV) ? TRUE : FALSE );
if (p_get_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_SERV_CONV ) == NULL) {
p_add_proto_data(pinfo->pool, pinfo, proto_btl2cap, BTL2CAP_SERV_CONV, GUINT_TO_POINTER((guint)uuid));
}
if (!dissector_try_uint_new(l2cap_service_dissector_table, uuid, next_tvb, pinfo, tree, TRUE, l2cap_data)) {
/* unknown protocol. declare as data */
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, ENC_NA);
}
}
}
else if (cid < BTL2CAP_FIXED_CID_MAX) {
if (cid == BTL2CAP_FIXED_CID_AMP_MAN) {
control = tvb_get_letohs(tvb, offset);
if (control & 0x1) {
dissect_s_frame(tvb, pinfo, tree, btl2cap_tree, 0 /* unused */, length, offset, NULL /* unused */);
} else {
proto_item* ti_control;
proto_tree* ti_control_subtree;
ti_control = proto_tree_add_none_format(btl2cap_tree, hf_btl2cap_control, tvb,
offset, 2, "Control: %s reqseq:%d r:%d txseq:%d",
val_to_str_const((control & 0xC000) >> 14, control_sar_vals, "unknown"),
(control & 0x3F00) >> 8,
(control & 0x0080) >> 7,
(control & 0x007E) >> 1);
ti_control_subtree = proto_item_add_subtree(ti_control, ett_btl2cap_control);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_sar, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_reqseq, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_retransmissiondisable, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_txseq, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(ti_control_subtree, hf_btl2cap_control_type, tvb, offset, 2, ENC_LITTLE_ENDIAN);
offset += 2;
proto_tree_add_item(btl2cap_tree, hf_btl2cap_fcs, tvb, tvb_length(tvb)-2, 2, ENC_LITTLE_ENDIAN);
next_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset)-2, length);
}
}
else {
next_tvb = tvb_new_subset(tvb, offset, tvb_length_remaining(tvb, offset), length);
}
/* call next dissector */
if (next_tvb && !dissector_try_uint_new(l2cap_cid_dissector_table, (guint32) cid,
next_tvb, pinfo, tree, TRUE, l2cap_data)) {
/* unknown protocol. declare as data */
proto_tree_add_item(btl2cap_tree, hf_btl2cap_payload, tvb, offset, length, ENC_NA);
}
}
else /* if (cid >= BTL2CAP_FIXED_CID_MAX) */ { /* Connection oriented channel */
wmem_tree_key_t key[6];
guint32 k_interface_id;
guint32 k_adapter_id;
guint32 k_chandle;
guint32 k_cid;
guint32 k_frame_number;
guint32 interface_id;
guint32 adapter_id;
guint32 chandle;
guint32 key_cid;
interface_id = (acl_data) ? acl_data->interface_id : HCI_INTERFACE_AMP;
adapter_id = (acl_data) ? acl_data->adapter_id : HCI_ADAPTER_DEFAULT;
chandle = (acl_data) ? acl_data->chandle : 0;
key_cid = cid | ((pinfo->p2p_dir == P2P_DIR_RECV) ? 0x00000000 : 0x80000000);
k_interface_id = interface_id;
k_adapter_id = adapter_id;
k_chandle = chandle;
k_cid = key_cid;
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_cid;
key[4].length = 1;
key[4].key = &k_frame_number;
key[5].length = 0;
key[5].key = NULL;
psm_data = (psm_data_t *)wmem_tree_lookup32_array_le(cid_to_psm_table, key);
if (psm_data && psm_data->interface_id == interface_id &&
psm_data->adapter_id == adapter_id &&
psm_data->chandle == chandle &&
(psm_data->scid == key_cid ||
psm_data->dcid == key_cid) &&
psm_data->disconnect_in_frame > pinfo->fd->num) {
config_data_t *config_data;
if ((psm_data->scid == key_cid) &&
psm_data->first_scid_frame == 0) {
psm_data->first_scid_frame = pinfo->fd->num;
}
if ((psm_data->dcid == key_cid) &&
psm_data->first_dcid_frame == 0) {
psm_data->first_dcid_frame = pinfo->fd->num;
}
psm = psm_data->psm;
l2cap_data->psm = psm;
l2cap_data->first_scid_frame = psm_data->first_scid_frame;
l2cap_data->first_dcid_frame = psm_data->first_dcid_frame;
if (pinfo->p2p_dir == P2P_DIR_RECV)
config_data = &(psm_data->in);
else
config_data = &(psm_data->out);
if (config_data->mode == 0) {
dissect_b_frame(tvb, pinfo, tree, btl2cap_tree, cid, psm, psm_data->local_service, length, offset, l2cap_data);
} else {
control = tvb_get_letohs(tvb, offset);
if (control & 0x1) {
dissect_s_frame(tvb, pinfo, tree, btl2cap_tree, psm, length, offset, config_data);
} else {
dissect_i_frame(tvb, pinfo, tree, btl2cap_tree, psm_data, length, offset, config_data, l2cap_data);
}
}
} else {
psm = 0;
dissect_b_frame(tvb, pinfo, tree, btl2cap_tree, cid, psm, FALSE, length, offset, l2cap_data);
}
}
return offset;
}
static int
btl2cap_sdp_tap_packet(void *arg _U_, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *arg2)
{
const sdp_package_t *sdp_package = (const sdp_package_t *) arg2;
if (sdp_service_infos == NULL) {
sdp_service_infos = sdp_package->service_infos;
}
return 0;
}
/* Register the protocol with Wireshark */
void
proto_register_btl2cap(void)
{
expert_module_t *expert_btl2cap;
/* Setup list of header fields See Section 1.6.1 for details*/
static hf_register_info hf[] = {
{ &hf_btl2cap_length,
{ "Length", "btl2cap.length",
FT_UINT16, BASE_DEC, NULL, 0x0,
"L2CAP Payload Length", HFILL }
},
{ &hf_btl2cap_cid,
{ "CID", "btl2cap.cid",
FT_UINT16, BASE_HEX | BASE_RANGE_STRING, RVALS(cid_rvals), 0x0,
"L2CAP Channel Identifier", HFILL }
},
{ &hf_btl2cap_payload,
{ "Payload", "btl2cap.payload",
FT_BYTES, BASE_NONE, NULL, 0x0,
"L2CAP Payload", HFILL }
},
{ &hf_btl2cap_command,
{ "Command", "btl2cap.command",
FT_NONE, BASE_NONE, NULL, 0x0,
"L2CAP Command", HFILL }
},
{ &hf_btl2cap_cmd_code,
{ "Command Code", "btl2cap.cmd_code",
FT_UINT8, BASE_HEX, VALS(command_code_vals), 0x0,
"L2CAP Command Code", HFILL }
},
{ &hf_btl2cap_cmd_ident,
{ "Command Identifier", "btl2cap.cmd_ident",
FT_UINT8, BASE_HEX, NULL, 0x0,
"L2CAP Command Identifier", HFILL }
},
{ &hf_btl2cap_cmd_length,
{ "Command Length", "btl2cap.cmd_length",
FT_UINT8, BASE_DEC, NULL, 0x0,
"L2CAP Command Length", HFILL }
},
{ &hf_btl2cap_cmd_data,
{ "Command Data", "btl2cap.cmd_data",
FT_NONE, BASE_NONE, NULL, 0x0,
"L2CAP Command Data", HFILL }
},
{ &hf_btl2cap_psm,
{ "PSM", "btl2cap.psm",
FT_UINT16, BASE_HEX, VALS(psm_vals), 0x0,
"Protocol/Service Multiplexer", HFILL }
},
{ &hf_btl2cap_psm_dynamic,
{ "Dynamic PSM", "btl2cap.psm",
FT_UINT16, BASE_HEX, NULL, 0x0,
"Dynamic Protocol/Service Multiplexer", HFILL }
},
{ &hf_btl2cap_scid,
{ "Source CID", "btl2cap.scid",
FT_UINT16, BASE_HEX | BASE_RANGE_STRING, RVALS(cid_rvals), 0x0,
"Source Channel Identifier", HFILL }
},
{ &hf_btl2cap_dcid,
{ "Destination CID", "btl2cap.dcid",
FT_UINT16, BASE_HEX | BASE_RANGE_STRING, RVALS(cid_rvals), 0x0,
"Destination Channel Identifier", HFILL }
},
{ &hf_btl2cap_icid,
{ "Initiator CID", "btl2cap.icid",
FT_UINT16, BASE_HEX | BASE_RANGE_STRING, RVALS(cid_rvals), 0x0,
"Initiator Channel Identifier", HFILL }
},
{ &hf_btl2cap_controller,
{ "Controller ID", "btl2cap.ctrl_id",
FT_UINT8, BASE_DEC, VALS(ctrl_id_code_vals), 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_dcontroller,
{ "Controller ID", "btl2cap.dctrl_id",
FT_UINT8, BASE_DEC, VALS(ctrl_id_code_vals), 0x0,
"Destination Controller ID", HFILL }
},
{ &hf_btl2cap_result,
{ "Result", "btl2cap.result",
FT_UINT16, BASE_HEX, VALS(result_vals), 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_move_result,
{ "Move Result", "btl2cap.move_result",
FT_UINT16, BASE_HEX, VALS(move_result_vals), 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_move_confirmation_result,
{ "Move Result", "btl2cap.move_result",
FT_UINT16, BASE_HEX, VALS(move_result_confirmation_vals), 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_status,
{ "Status", "btl2cap.status",
FT_UINT16, BASE_HEX, VALS(status_vals), 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_rej_reason,
{ "Reason", "btl2cap.rej_reason",
FT_UINT16, BASE_HEX, VALS(reason_vals), 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_sig_mtu,
{ "Maximum Signalling MTU", "btl2cap.sig_mtu",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_info_mtu,
{ "Remote Entity MTU", "btl2cap.info_mtu",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Remote entity acceptable connectionless MTU", HFILL }
},
{ &hf_btl2cap_info_flowcontrol,
{ "Flow Control Mode", "btl2cap.info_flowcontrol",
FT_UINT32, BASE_DEC, NULL, 0x01,
"Flow Control mode support", HFILL }
},
{ &hf_btl2cap_info_retransmission,
{ "Retransmission Mode", "btl2cap.info_retransmission",
FT_UINT32, BASE_DEC, NULL, 0x02,
"Retransmission mode support", HFILL }
},
{ &hf_btl2cap_info_bidirqos,
{ "Bi-Directional QOS", "btl2cap.info_bidirqos",
FT_UINT32, BASE_DEC, NULL, 0x04,
"Bi-Directional QOS support", HFILL }
},
{ &hf_btl2cap_info_enh_retransmission,
{ "Enhanced Retransmission Mode", "btl2cap.info_enh_retransmission",
FT_UINT32, BASE_DEC, NULL, 0x08,
"Enhanced Retransmission mode support", HFILL }
},
{ &hf_btl2cap_info_streaming,
{ "Streaming Mode", "btl2cap.info_streaming",
FT_UINT32, BASE_DEC, NULL, 0x10,
"Streaming mode support", HFILL }
},
{ &hf_btl2cap_info_fcs,
{ "FCS", "btl2cap.info_fcs",
FT_UINT32, BASE_DEC, NULL, 0x20,
"FCS support", HFILL }
},
{ &hf_btl2cap_info_flow_spec,
{ "Extended Flow Specification for BR/EDR", "btl2cap.info_flow_spec",
FT_UINT32, BASE_DEC, NULL, 0x40,
"Extended Flow Specification for BR/EDR support", HFILL }
},
{ &hf_btl2cap_info_fixedchan,
{ "Fixed Channels", "btl2cap.info_fixedchan",
FT_UINT32, BASE_DEC, NULL, 0x80,
"Fixed Channels support", HFILL }
},
{ &hf_btl2cap_info_window,
{ "Extended Window Size", "btl2cap.info_window",
FT_UINT32, BASE_DEC, NULL, 0x01,
"Extended Window Size support", HFILL }
},
{ &hf_btl2cap_info_unicast,
{ "Unicast Connectionless Data Reception", "btl2cap.info_unicast",
FT_UINT32, BASE_DEC, NULL, 0x02,
"Unicast Connectionless Data Reception support", HFILL }
},
{ &hf_btl2cap_info_fixedchans,
{ "Fixed Channels", "btl2cap.info_fixedchans",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_info_fixedchans_null,
{ "Null identifier", "btl2cap.info_fixedchans_null",
FT_UINT32, BASE_DEC, NULL, 0x1,
NULL, HFILL }
},
{ &hf_btl2cap_info_fixedchans_signal,
{ "L2CAP signaling channel", "btl2cap.info_fixedchans_signal",
FT_UINT32, BASE_DEC, NULL, 0x2,
NULL, HFILL }
},
{ &hf_btl2cap_info_fixedchans_connless,
{ "Connectionless reception", "btl2cap.info_fixedchans_connless",
FT_UINT32, BASE_DEC, NULL, 0x4,
NULL, HFILL }
},
{ &hf_btl2cap_info_fixedchans_amp_man,
{ "AMP Manager protocol", "btl2cap.info_fixedchans_amp_man",
FT_UINT32, BASE_DEC, NULL, 0x8,
NULL, HFILL }
},
{ &hf_btl2cap_info_fixedchans_amp_test,
{ "AMP Test Manager", "btl2cap.info_fixedchans_amp_test",
FT_UINT32, BASE_DEC, NULL, 0x80000000,
NULL, HFILL }
},
{ &hf_btl2cap_info_type,
{ "Information Type", "btl2cap.info_type",
FT_UINT16, BASE_HEX, VALS(info_type_vals), 0x0,
"Type of implementation-specific information", HFILL }
},
{ &hf_btl2cap_info_result,
{ "Result", "btl2cap.info_result",
FT_UINT16, BASE_HEX, VALS(info_result_vals), 0x0,
"Information about the success of the request", HFILL }
},
{ &hf_btl2cap_info_extfeatures,
{ "Extended Features", "btl2cap.info_extfeatures",
FT_NONE, BASE_NONE, NULL, 0x0,
"Extended Features Mask", HFILL }
},
{ &hf_btl2cap_continuation_flag,
{ "Continuation Flag", "btl2cap.continuation",
FT_BOOLEAN, 16, NULL, 0x0001,
NULL, HFILL }
},
{ &hf_btl2cap_configuration_result,
{ "Result", "btl2cap.conf_result",
FT_UINT16, BASE_HEX, VALS(configuration_result_vals), 0x0,
"Configuration Result", HFILL }
},
{ &hf_btl2cap_option_type,
{ "Type", "btl2cap.option_type",
FT_UINT8, BASE_HEX, VALS(option_type_vals), 0x0,
"Type of option", HFILL }
},
{ &hf_btl2cap_option_length,
{ "Length", "btl2cap.option_length",
FT_UINT8, BASE_DEC, NULL, 0x0,
"Number of octets in option payload", HFILL }
},
{ &hf_btl2cap_option_mtu,
{ "MTU", "btl2cap.option_mtu",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Maximum Transmission Unit", HFILL }
},
{ &hf_btl2cap_option_flushTO,
{ "Flush Timeout (ms)", "btl2cap.option_flushto",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Flush Timeout in milliseconds", HFILL }
},
{ &hf_btl2cap_option_flush_to_us,
{ "Flush Timeout (us)", "btl2cap.option_flushto",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Flush Timeout (microseconds)", HFILL }
},
{ &hf_btl2cap_option_sdu_size,
{ "Maximum SDU Size", "btl2cap.option_sdu_size",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_option_sdu_arrival_time,
{ "SDU Inter-arrival Time (us)", "btl2cap.option_sdu_arrival_time",
FT_UINT32, BASE_DEC, NULL, 0x0,
"SDU Inter-arrival Time (microseconds)", HFILL }
},
{ &hf_btl2cap_option_identifier,
{ "Identifier", "btl2cap.option_ident",
FT_UINT8, BASE_HEX, NULL, 0x0,
"Flow Specification Identifier", HFILL }
},
{ &hf_btl2cap_option_access_latency,
{ "Access Latency (us)", "btl2cap.option_access_latency",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Access Latency (microseconds)", HFILL }
},
{ &hf_btl2cap_option_flags,
{ "Flags", "btl2cap.option_flags",
FT_UINT8, BASE_HEX, NULL, 0x0,
"Flags - must be set to 0 (Reserved for future use)", HFILL }
},
{ &hf_btl2cap_option_service_type,
{ "Service Type", "btl2cap.option_servicetype",
FT_UINT8, BASE_HEX, VALS(option_servicetype_vals), 0x0,
"Level of service required", HFILL }
},
{ &hf_btl2cap_option_tokenrate,
{ "Token Rate (bytes/s)", "btl2cap.option_tokenrate",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Rate at which traffic credits are granted (bytes/s)", HFILL }
},
{ &hf_btl2cap_option_tokenbucketsize,
{ "Token Bucket Size (bytes)", "btl2cap.option_tokenbsize",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Size of the token bucket (bytes)", HFILL }
},
{ &hf_btl2cap_option_peakbandwidth,
{ "Peak Bandwidth (bytes/s)", "btl2cap.option_peakbandwidth",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Limit how fast packets may be sent (bytes/s)", HFILL }
},
{ &hf_btl2cap_option_latency,
{ "Latency (microseconds)", "btl2cap.option_latency",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Maximal acceptable delay (microseconds)", HFILL }
},
{ &hf_btl2cap_option_delayvariation,
{ "Delay Variation (microseconds)", "btl2cap.option_delayvar",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Difference between maximum and minimum delay (microseconds)", HFILL }
},
{ &hf_btl2cap_option_retransmissionmode,
{ "Mode", "btl2cap.retransmissionmode",
FT_UINT8, BASE_HEX, VALS(option_retransmissionmode_vals), 0x0,
"Retransmission/Flow Control mode", HFILL }
},
{ &hf_btl2cap_option_txwindow,
{ "TxWindow", "btl2cap.txwindow",
FT_UINT8, BASE_DEC, NULL, 0x0,
"Retransmission window size", HFILL }
},
{ &hf_btl2cap_option_maxtransmit,
{ "MaxTransmit", "btl2cap.maxtransmit",
FT_UINT8, BASE_DEC, NULL, 0x0,
"Maximum I-frame retransmissions", HFILL }
},
{ &hf_btl2cap_option_retransmittimeout,
{ "Retransmit timeout (ms)", "btl2cap.retransmittimeout",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Retransmission timeout (milliseconds)", HFILL }
},
{ &hf_btl2cap_option_monitortimeout,
{ "Monitor Timeout (ms)", "btl2cap.monitortimeout",
FT_UINT16, BASE_DEC, NULL, 0x0,
"S-frame transmission interval (milliseconds)", HFILL }
},
{ &hf_btl2cap_option_mps,
{ "MPS", "btl2cap.mps",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Maximum PDU Payload Size", HFILL }
},
{ &hf_btl2cap_option_fcs,
{ "FCS", "btl2cap.option_fcs",
FT_UINT16, BASE_HEX, VALS(option_fcs_vals), 0x0,
"Frame Check Sequence", HFILL }
},
{ &hf_btl2cap_option_window,
{ "Extended Window Size", "btl2cap.option_window",
FT_UINT16, BASE_DEC, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_option,
{ "Configuration Parameter Option", "btl2cap.conf_param_option",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_control_sar,
{ "Segmentation and reassembly", "btl2cap.control_sar",
FT_UINT16, BASE_HEX, VALS(control_sar_vals), 0xC000,
NULL, HFILL }
},
{ &hf_btl2cap_control_reqseq,
{ "ReqSeq", "btl2cap.control_reqseq",
FT_UINT16, BASE_DEC, NULL, 0x3F00,
"Request Sequence Number", HFILL }
},
{ &hf_btl2cap_control_txseq,
{ "TxSeq", "btl2cap.control_txseq",
FT_UINT16, BASE_DEC, NULL, 0x007E,
"Transmitted Sequence Number", HFILL }
},
{ &hf_btl2cap_control_retransmissiondisable,
{ "R", "btl2cap.control_retransmissiondisable",
FT_UINT16, BASE_HEX, NULL, 0x0080,
"Retransmission Disable", HFILL }
},
{ &hf_btl2cap_control_supervisory,
{ "S", "btl2cap.control_supervisory",
FT_UINT16, BASE_HEX, VALS(control_supervisory_vals), 0x000C,
"Supervisory Function", HFILL }
},
{ &hf_btl2cap_control_type,
{ "Frame Type", "btl2cap.control_type",
FT_UINT16, BASE_HEX, VALS(control_type_vals), 0x0001,
NULL, HFILL }
},
{ &hf_btl2cap_control,
{ "Control field", "btl2cap.control",
FT_NONE, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_btl2cap_fcs,
{ "FCS", "btl2cap.fcs",
FT_UINT16, BASE_HEX, NULL, 0,
"Frame Check Sequence", HFILL }
},
{ &hf_btl2cap_sdulength,
{ "SDU Length", "btl2cap.sdulength",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL }
},
{ &hf_btl2cap_reassembled_in,
{ "This SDU is reassembled in frame", "btl2cap.reassembled_in",
FT_FRAMENUM, BASE_NONE, NULL, 0,
"This SDU is reassembled in frame #", HFILL }
},
{ &hf_btl2cap_continuation_to,
{ "This is a continuation to the SDU in frame", "btl2cap.continuation_to",
FT_FRAMENUM, BASE_NONE, NULL, 0,
"This is a continuation to the SDU in frame #", HFILL }
},
{ &hf_btl2cap_min_interval,
{ "Min. Interval", "btl2cap.min_interval",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL }
},
{ &hf_btl2cap_max_interval,
{ "Max. Interval", "btl2cap.max_interval",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL }
},
{ &hf_btl2cap_slave_latency,
{ "Slave Latency", "btl2cap.slave_latency",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL }
},
{ &hf_btl2cap_timeout_multiplier,
{ "Timeout Multiplier", "btl2cap.timeout_multiplier",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL }
},
{ &hf_btl2cap_conn_param_result,
{ "Move Result", "btl2cap.move_result",
FT_UINT16, BASE_HEX, VALS(conn_param_result_vals), 0x0,
NULL, HFILL }
}
};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_btl2cap,
&ett_btl2cap_cmd,
&ett_btl2cap_option,
&ett_btl2cap_extfeatures,
&ett_btl2cap_fixedchans,
&ett_btl2cap_control
};
static ei_register_info ei[] = {
{ &ei_btl2cap_parameter_mismatch, { "btl2cap.parameter_mismatch", PI_PROTOCOL, PI_WARN, "Unexpected frame", EXPFILL }},
{ &ei_btl2cap_sdulength_bad, { "btl2cap.sdulength.bad", PI_MALFORMED, PI_WARN, "SDU length bad", EXPFILL }},
{ &ei_btl2cap_length_bad, { "btl2cap.length.bad", PI_MALFORMED, PI_WARN, "Length bad", EXPFILL }},
};
/* Decode As handling */
static build_valid_func btl2cap_cid_da_build_value[1] = {btl2cap_cid_value};
static decode_as_value_t btl2cap_cid_da_values = {btl2cap_cid_prompt, 1, btl2cap_cid_da_build_value};
static decode_as_t btl2cap_cid_da = {"btl2cap", "L2CAP CID", "btl2cap.cid", 1, 0, &btl2cap_cid_da_values, NULL, NULL,
decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL};
static build_valid_func btl2cap_psm_da_build_value[1] = {btl2cap_psm_value};
static decode_as_value_t btl2cap_psm_da_values = {btl2cap_psm_prompt, 1, btl2cap_psm_da_build_value};
static decode_as_t btl2cap_psm_da = {"btl2cap", "L2CAP PSM", "btl2cap.psm", 1, 0, &btl2cap_psm_da_values, NULL, NULL,
decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL};
static build_valid_func btl2cap_serv_da_build_value[1] = {btl2cap_serv_value};
static decode_as_value_t btl2cap_serv_da_values = {btl2cap_serv_prompt, 1, btl2cap_serv_da_build_value};
static decode_as_t btl2cap_serv_da = {"btl2cap", "L2CAP SERVICE", "btl2cap.service", 1, 0, &btl2cap_serv_da_values, NULL, NULL,
decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL};
/* Register the protocol name and description */
proto_btl2cap = proto_register_protocol("Bluetooth L2CAP Protocol", "BT L2CAP", "btl2cap");
new_register_dissector("btl2cap", dissect_btl2cap, proto_btl2cap);
/* subdissector code */
l2cap_psm_dissector_table = register_dissector_table("btl2cap.psm", "L2CAP PSM", FT_UINT16, BASE_HEX);
l2cap_service_dissector_table = register_dissector_table("btl2cap.service", "L2CAP Service", FT_UINT16, BASE_HEX);
l2cap_cid_dissector_table = register_dissector_table("btl2cap.cid", "L2CAP CID", FT_UINT16, BASE_HEX);
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_btl2cap, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_btl2cap = expert_register_protocol(proto_btl2cap);
expert_register_field_array(expert_btl2cap, ei, array_length(ei));
cid_to_psm_table = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope()); /* scid: psm */
register_decode_as(&btl2cap_cid_da);
register_decode_as(&btl2cap_psm_da);
register_decode_as(&btl2cap_serv_da);
}
void
proto_reg_handoff_btl2cap(void)
{
/* tap into the btsdp dissector to look for l2cap PSM infomation that
helps us determine the type of l2cap payload, i.e. which service is
using the PSM channel so we know which sub-dissector to call */
register_tap_listener("btsdp", NULL, NULL, TL_IS_DISSECTOR_HELPER, NULL, btl2cap_sdp_tap_packet, NULL);
}
/*
* 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:
*/