Bluetooth: HFP: Implement some Apple-specific AT commands

AT-commands:
+XAPL
+IPHONEACCEV
+APLSIRI
+APLEFM

Add UUID128:
Apple Notification Center Service

Based on: https://developer.apple.com/hardwaredrivers/BluetoothDesignGuidelines.pdf

While adding new UUID remove also tabs from packet-bluetooth.

Change-Id: Ic29b028338a21464fe018f8145ade82297ccd146
Reviewed-on: https://code.wireshark.org/review/28222
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot
Reviewed-by: Anders Broman <a.broman58@gmail.com>
This commit is contained in:
Michał Łabędzki 2018-03-24 17:28:00 +01:00 committed by Anders Broman
parent 69ad89caa9
commit 40f655392f
2 changed files with 331 additions and 20 deletions

View File

@ -2495,19 +2495,19 @@ const value_string bluetooth_address_type_vals[] = {
* Protocol identifier are described in Table 5.2.
*/
#define AMP_U_L2CAP 0x0001
#define AMP_C_ACTIVITY_REPORT 0x0002
#define AMP_C_SECURITY_FRAME 0x0003
#define AMP_C_LINK_SUP_REQUEST 0x0004
#define AMP_C_LINK_SUP_REPLY 0x0005
#define AMP_U_L2CAP 0x0001
#define AMP_C_ACTIVITY_REPORT 0x0002
#define AMP_C_SECURITY_FRAME 0x0003
#define AMP_C_LINK_SUP_REQUEST 0x0004
#define AMP_C_LINK_SUP_REPLY 0x0005
static const value_string bluetooth_pid_vals[] = {
{ AMP_U_L2CAP, "AMP_U L2CAP ACL data" },
{ AMP_C_ACTIVITY_REPORT, "AMP-C Activity Report" },
{ AMP_C_SECURITY_FRAME, "AMP-C Security frames" },
{ AMP_C_LINK_SUP_REQUEST, "AMP-C Link supervision request" },
{ AMP_C_LINK_SUP_REPLY, "AMP-C Link supervision reply" },
{ 0, NULL }
{ AMP_U_L2CAP, "AMP_U L2CAP ACL data" },
{ AMP_C_ACTIVITY_REPORT, "AMP-C Activity Report" },
{ AMP_C_SECURITY_FRAME, "AMP-C Security frames" },
{ AMP_C_LINK_SUP_REQUEST, "AMP-C Link supervision request" },
{ AMP_C_LINK_SUP_REPLY, "AMP-C Link supervision reply" },
{ 0, NULL }
};
guint32 max_disconnect_in_frame = G_MAXUINT32;
@ -3095,7 +3095,7 @@ proto_register_bluetooth(void)
static hf_register_info oui_hf[] = {
{ &hf_llc_bluetooth_pid,
{ "PID", "llc.bluetooth_pid",
{ "PID", "llc.bluetooth_pid",
FT_UINT16, BASE_HEX, VALS(bluetooth_pid_vals), 0x0,
"Protocol ID", HFILL }
}
@ -3140,7 +3140,7 @@ proto_register_bluetooth(void)
bluetooth_hci_summary_tap = register_tap("bluetooth.hci_summary");
bluetooth_uuid_table = register_dissector_table("bluetooth.uuid", "BT Service UUID", proto_bluetooth, FT_STRING, BASE_NONE);
llc_add_oui(OUI_BLUETOOTH, "llc.bluetooth_pid", "LLC Bluetooth OUI PID", oui_hf, proto_bluetooth);
llc_add_oui(OUI_BLUETOOTH, "llc.bluetooth_pid", "LLC Bluetooth OUI PID", oui_hf, proto_bluetooth);
register_conversation_table(proto_bluetooth, TRUE, bluetooth_conversation_packet, bluetooth_hostlist_packet);
@ -3154,8 +3154,8 @@ proto_reg_handoff_bluetooth(void)
dissector_handle_t bluetooth_bthci_handle = create_dissector_handle(dissect_bluetooth_bthci, proto_bluetooth);
dissector_handle_t bluetooth_btmon_handle = create_dissector_handle(dissect_bluetooth_btmon, proto_bluetooth);
dissector_handle_t bluetooth_usb_handle = create_dissector_handle(dissect_bluetooth_usb, proto_bluetooth);
dissector_handle_t eapol_handle;
dissector_handle_t btl2cap_handle;
dissector_handle_t eapol_handle;
dissector_handle_t btl2cap_handle;
btle_handle = find_dissector_add_dependency("btle", proto_bluetooth);
hci_usb_handle = find_dissector_add_dependency("hci_usb", proto_bluetooth);
@ -3185,12 +3185,13 @@ proto_reg_handoff_bluetooth(void)
wmem_tree_insert_string(bluetooth_uuids, "00000001-0000-1000-8000-0002EE000002", "SyncML Server", 0);
wmem_tree_insert_string(bluetooth_uuids, "00000002-0000-1000-8000-0002EE000002", "SyncML Client", 0);
wmem_tree_insert_string(bluetooth_uuids, "7905F431-B5CE-4E99-A40F-4B1E122D00D0", "Apple Notification Center Service", 0);
eapol_handle = find_dissector("eapol");
btl2cap_handle = find_dissector("btl2cap");
eapol_handle = find_dissector("eapol");
btl2cap_handle = find_dissector("btl2cap");
dissector_add_uint("llc.bluetooth_pid", AMP_C_SECURITY_FRAME, eapol_handle);
dissector_add_uint("llc.bluetooth_pid", AMP_U_L2CAP, btl2cap_handle);
dissector_add_uint("llc.bluetooth_pid", AMP_C_SECURITY_FRAME, eapol_handle);
dissector_add_uint("llc.bluetooth_pid", AMP_U_L2CAP, btl2cap_handle);
/* TODO: Add UUID128 verion of UUID16; UUID32? UUID16? */
}

View File

@ -116,6 +116,23 @@ static int hf_biev_value = -1;
static int hf_bind_parameter = -1;
static int hf_bia_indicator[20] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
static int hf_indicator[20] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1};
static int hf_aplefm_state = -1;
static int hf_aplsiri_state = -1;
static int hf_iphoneaccev_count = -1;
static int hf_iphoneaccev_key = -1;
static int hf_iphoneaccev_value = -1;
static int hf_xapl_accessory_info = -1;
static int hf_xapl_accessory_info_vendor_id = -1;
static int hf_xapl_accessory_info_product_id = -1;
static int hf_xapl_accessory_info_version = -1;
static int hf_xapl_host_info = -1;
static int hf_xapl_features = -1;
static int hf_xapl_features_reserved_x = -1;
static int hf_xapl_features_noise_reduction_status_reporting = -1;
static int hf_xapl_features_siri_status_reporting = -1;
static int hf_xapl_features_docked_or_powered = -1;
static int hf_xapl_features_battery_reporting = -1;
static int hf_xapl_features_reserved = -1;
static expert_field ei_non_mandatory_command = EI_INIT;
static expert_field ei_invalid_usage = EI_INIT;
@ -145,6 +162,10 @@ static expert_field ei_vts_dtmf = EI_INIT;
static expert_field ei_at_type = EI_INIT;
static expert_field ei_cnum_service = EI_INIT;
static expert_field ei_cnum_itc = EI_INIT;
static expert_field ei_aplefm_out_of_range = EI_INIT;
static expert_field ei_aplsiri_out_of_range = EI_INIT;
static expert_field ei_iphoneaccev_key_out_of_range = EI_INIT;
static expert_field ei_xapl_features_reserved = EI_INIT;
static expert_field ei_parameter_blank = EI_INIT;
static gint ett_bthfp = -1;
@ -152,6 +173,8 @@ static gint ett_bthfp_command = -1;
static gint ett_bthfp_parameters = -1;
static gint ett_bthfp_brsf_hf = -1;
static gint ett_bthfp_brsf_ag = -1;
static gint ett_bthfp_xapl_features = -1;
static gint ett_bthfp_xapl_accessory_info = -1;
static dissector_handle_t bthfp_handle;
@ -462,6 +485,25 @@ static const value_string biev_assigned_number_vals[] = {
{ 0, NULL }
};
static const value_string aplefm_state_vals[] = {
{ 0, "Disable" },
{ 1, "Enable" },
{ 0, NULL }
};
static const value_string aplsiri_state_vals[] = {
{ 1, "Enabled" },
{ 2, "Disabled" },
{ 0, NULL }
};
static const value_string iphoneaccev_key_vals[] = {
{ 1, "Battery Level" },
{ 2, "Dock State" },
{ 0, NULL }
};
static const unit_name_string units_slash15 = { "/15", NULL };
extern value_string_ext csd_data_rate_vals_ext;
@ -482,6 +524,46 @@ static guint32 get_uint_parameter(guint8 *parameter_stream, gint parameter_lengt
return value;
}
static guint32 get_uint_hex_parameter(guint8 *parameter_stream, gint parameter_length)
{
guint32 value;
guint8 *val;
val = (guint8 *) wmem_alloc(wmem_packet_scope(), parameter_length + 1);
memcpy(val, parameter_stream, parameter_length);
val[parameter_length] = '\0';
value = (guint32) g_ascii_strtoull(val, NULL, 16);
return value;
}
static gboolean check_aplefm(gint role, guint16 type) {
if (role == ROLE_HS && type == TYPE_ACTION) return TRUE;
if (role == ROLE_AG && type == TYPE_RESPONSE) return TRUE;
return FALSE;
}
static gboolean check_aplsiri(gint role, guint16 type) {
if (role == ROLE_HS && type == TYPE_READ) return TRUE;
if (role == ROLE_AG && type == TYPE_RESPONSE) return TRUE;
return FALSE;
}
static gboolean check_iphoneaccev(gint role, guint16 type) {
if (role == ROLE_HS && type == TYPE_ACTION) return TRUE;
return FALSE;
}
static gboolean check_xapl(gint role, guint16 type) {
if (role == ROLE_HS && type == TYPE_ACTION) return TRUE;
if (role == ROLE_AG && (type == TYPE_RESPONSE || type == TYPE_ACTION)) return TRUE;
return FALSE;
}
static gboolean check_biev(gint role, guint16 type) {
if (role == ROLE_HS && type == TYPE_ACTION) return TRUE;
@ -918,6 +1000,138 @@ dissect_bind_parameter(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree,
return FALSE;
}
static gint
dissect_aplefm_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gint offset, gint role, guint16 type, guint8 *parameter_stream,
guint parameter_number, gint parameter_length, void **data _U_)
{
proto_item *pitem;
guint32 value;
if (!check_aplefm(role, type)) return FALSE;
if (parameter_number == 0) {
value = get_uint_parameter(parameter_stream, parameter_length);
pitem = proto_tree_add_uint(tree, hf_aplefm_state, tvb, offset,
parameter_length, value);
if (value > 1) {
expert_add_info(pinfo, pitem, &ei_aplefm_out_of_range);
}
} else return FALSE;
return TRUE;
}
static gint
dissect_aplsiri_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gint offset, gint role, guint16 type, guint8 *parameter_stream,
guint parameter_number, gint parameter_length, void **data _U_)
{
proto_item *pitem;
guint32 value;
if (!check_aplsiri(role, type)) return FALSE;
if (parameter_number == 0) {
value = get_uint_parameter(parameter_stream, parameter_length);
pitem = proto_tree_add_uint(tree, hf_aplsiri_state, tvb, offset,
parameter_length, value);
if (value < 1 || value > 2) {
expert_add_info(pinfo, pitem, &ei_aplsiri_out_of_range);
}
} else return FALSE;
return TRUE;
}
static gint
dissect_iphoneaccev_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gint offset, gint role, guint16 type, guint8 *parameter_stream,
guint parameter_number, gint parameter_length, void **data _U_)
{
proto_item *pitem;
guint32 value;
if (!check_iphoneaccev(role, type)) return FALSE;
if (parameter_number == 0) {
value = get_uint_parameter(parameter_stream, parameter_length);
proto_tree_add_uint(tree, hf_iphoneaccev_count, tvb, offset,
parameter_length, value);
} else if (parameter_number % 2 == 1) {
value = get_uint_parameter(parameter_stream, parameter_length);
pitem = proto_tree_add_uint(tree, hf_iphoneaccev_key, tvb, offset,
parameter_length, value);
if (value < 1 || value > 2) {
expert_add_info(pinfo, pitem, &ei_iphoneaccev_key_out_of_range);
}
} else {
value = get_uint_parameter(parameter_stream, parameter_length);
pitem = proto_tree_add_uint(tree, hf_iphoneaccev_value, tvb, offset,
parameter_length, value);
}
return TRUE;
}
static gint
dissect_xapl_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gint offset, gint role, guint16 type, guint8 *parameter_stream,
guint parameter_number, gint parameter_length, void **data _U_)
{
proto_item *pitem;
proto_tree *ptree;
guint32 value;
if (!check_xapl(role, type)) return FALSE;
if (parameter_number == 0) {
if (role == ROLE_HS) {
pitem = proto_tree_add_item(tree, hf_xapl_accessory_info, tvb, offset, parameter_length, ENC_NA | ENC_ASCII);
ptree = proto_item_add_subtree(pitem, ett_bthfp_xapl_accessory_info);
value = get_uint_hex_parameter(parameter_stream + (4 + 1) * 0, 4);
proto_tree_add_uint(ptree, hf_xapl_accessory_info_vendor_id, tvb, offset, 4, value);
value = get_uint_hex_parameter(parameter_stream + (4 + 1) * 1, 4);
proto_tree_add_uint(ptree, hf_xapl_accessory_info_product_id, tvb, offset + (4 + 1) * 1, 4, value);
value = get_uint_hex_parameter(parameter_stream + (4 + 1) * 2, 4);
proto_tree_add_uint(ptree, hf_xapl_accessory_info_version, tvb, offset + (4 + 1) * 2, 4, value);
} else {
proto_tree_add_item(tree, hf_xapl_host_info, tvb, offset, parameter_length, ENC_NA | ENC_ASCII);
}
} else if (parameter_number == 1) {
static const int * hfx[] = {
&hf_xapl_features_reserved_x,
&hf_xapl_features_noise_reduction_status_reporting,
&hf_xapl_features_siri_status_reporting,
&hf_xapl_features_docked_or_powered,
&hf_xapl_features_battery_reporting,
&hf_xapl_features_reserved,
NULL
};
value = get_uint_parameter(parameter_stream, parameter_length);
pitem = proto_tree_add_bitmask_value_with_flags(tree, tvb, offset, hf_xapl_features, ett_bthfp_xapl_features, hfx, value, BMT_NO_APPEND);
if (value >> 5) {
expert_add_info(pinfo, pitem, &ei_xapl_features_reserved);
}
} else return FALSE;
return TRUE;
}
static gint
dissect_biev_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
gint offset, gint role, guint16 type, guint8 *parameter_stream,
@ -1522,6 +1736,11 @@ dissect_ciev_parameter(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
Some commands can use TYPE_TEST respose to properly dissect parameters,
for example: AT+CIND=?, AT+CIND? */
static const at_cmd_t at_cmds[] = {
/* Vendor specific: Apple */
{ "+XAPL", "Apple Bluetooth Accessory Identification", check_xapl, dissect_xapl_parameter },
{ "+IPHONEACCEV", "Apple Bluetooth Headset Battery Level Indication", check_iphoneaccev, dissect_iphoneaccev_parameter },
{ "+APLSIRI", "Apple Siri Availability Information", check_aplsiri, dissect_aplsiri_parameter },
{ "+APLEFM", "Apple Siri Eyes Free Mode", check_aplefm, dissect_aplefm_parameter },
/* Bluetooth HFP specific AT Commands */
{ "+BIEV", "Bluetooth Indicator Enter Value", check_biev, dissect_biev_parameter }, /* HFP 1.7 */
{ "+BIND", "Bluetooth Indicator", check_bind, dissect_bind_parameter }, /* HFP 1.7 */
@ -2881,6 +3100,91 @@ proto_register_bthfp(void)
{ "Indicator 20", "bthfp.indicator.20",
FT_STRING, BASE_NONE, NULL, 0,
NULL, HFILL}
},
{ &hf_aplefm_state,
{ "State", "bthfp.aplefm.state",
FT_UINT16, BASE_DEC, VALS(aplefm_state_vals), 0,
NULL, HFILL}
},
{ &hf_aplsiri_state,
{ "Siri State", "bthfp.aplsiri.state",
FT_UINT16, BASE_DEC, VALS(aplsiri_state_vals), 0,
NULL, HFILL}
},
{ &hf_iphoneaccev_count,
{ "Count", "bthfp.iphoneaccev.count",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL}
},
{ &hf_iphoneaccev_key,
{ "Key", "bthfp.iphoneaccev.key",
FT_UINT16, BASE_DEC, VALS(iphoneaccev_key_vals), 0,
NULL, HFILL}
},
{ &hf_iphoneaccev_value,
{ "Value", "bthfp.iphoneaccev.value",
FT_UINT16, BASE_DEC, NULL, 0,
NULL, HFILL}
},
{ &hf_xapl_accessory_info,
{ "Accessory Info", "bthfp.xapl.accessory_info",
FT_STRING, BASE_NONE, NULL, 0,
NULL, HFILL}
},
{ &hf_xapl_accessory_info_vendor_id,
{ "Vendor ID", "bthfp.xapl.accessory_info.vendor_id",
FT_UINT16, BASE_HEX, NULL, 0,
NULL, HFILL}
},
{ &hf_xapl_accessory_info_product_id,
{ "Product ID", "bthfp.xapl.accessory_info.product_id",
FT_UINT16, BASE_HEX, NULL, 0,
NULL, HFILL}
},
{ &hf_xapl_accessory_info_version,
{ "Version", "bthfp.xapl.accessory_info.version",
FT_UINT16, BASE_HEX, NULL, 0,
NULL, HFILL}
},
{ &hf_xapl_host_info,
{ "Host Info", "bthfp.xapl.host_info",
FT_STRING, BASE_NONE, NULL, 0,
NULL, HFILL}
},
{ &hf_xapl_features,
{ "Features", "bthfp.xapl.features",
FT_UINT32, BASE_DEC, NULL, 0,
NULL, HFILL}
},
{ &hf_xapl_features_reserved,
{ "Reserved", "bthfp.xapl.features.reserved.0",
FT_BOOLEAN, 32, NULL, 0x00000001,
NULL, HFILL}
},
{ &hf_xapl_features_battery_reporting,
{ "Battery Reporting", "bthfp.xapl.features.battery_reporting",
FT_BOOLEAN, 32, NULL, 0x00000002,
NULL, HFILL}
},
{ &hf_xapl_features_docked_or_powered,
{ "Accessory is Docked or Powered", "bthfp.xapl.features.docked_or_powered",
FT_BOOLEAN, 32, NULL, 0x00000004,
NULL, HFILL}
},
{ &hf_xapl_features_siri_status_reporting,
{ "Siri Status Reporting", "bthfp.xapl.features.siri_status_reporting",
FT_BOOLEAN, 32, NULL, 0x00000008,
NULL, HFILL}
},
{ &hf_xapl_features_noise_reduction_status_reporting,
{ "Noise Reduction Status Reporting", "bthfp.xapl.features.noise_reduction_status_reporting",
FT_BOOLEAN, 32, NULL, 0x00000010,
NULL, HFILL}
},
{ &hf_xapl_features_reserved_x,
{ "Reserved", "bthfp.xapl.features.reserved.x",
FT_BOOLEAN, 32, NULL, 0xFFFFFFE0,
NULL, HFILL}
}
};
@ -2914,6 +3218,10 @@ proto_register_bthfp(void)
{ &ei_parameter_blank, { "bthfp.expert.parameter_blank", PI_PROTOCOL, PI_WARN, "Should be blank for HFP", EXPFILL }},
{ &ei_cnum_service, { "bthfp.expert.cnum.service", PI_PROTOCOL, PI_WARN, "Only 0-5 is valid", EXPFILL }},
{ &ei_cnum_itc, { "bthfp.expert.cnum.itc", PI_PROTOCOL, PI_WARN, "Only 0-1 is valid", EXPFILL }},
{ &ei_aplefm_out_of_range, { "bthfp.expert.aplefm.out_of_range", PI_PROTOCOL, PI_WARN, "Only 0-1 is valid", EXPFILL }},
{ &ei_aplsiri_out_of_range, { "bthfp.expert.aplsiri.out_of_range", PI_PROTOCOL, PI_WARN, "Only 1-2 is valid", EXPFILL }},
{ &ei_iphoneaccev_key_out_of_range, { "bthfp.expert.iphoneaccev.out_of_range", PI_PROTOCOL, PI_WARN, "Only 1-2 is valid", EXPFILL }},
{ &ei_xapl_features_reserved, { "bthfp.expert.xapl.reserved", PI_PROTOCOL, PI_WARN, "The reserved bits [6-31] shall be initialized to Zero", EXPFILL }}
};
static gint *ett[] = {
@ -2921,7 +3229,9 @@ proto_register_bthfp(void)
&ett_bthfp_brsf_hf,
&ett_bthfp_brsf_ag,
&ett_bthfp_command,
&ett_bthfp_parameters
&ett_bthfp_parameters,
&ett_bthfp_xapl_features,
&ett_bthfp_xapl_accessory_info
};
fragments = wmem_tree_new_autoreset(wmem_epan_scope(), wmem_file_scope());