2 * Routines for Bluetooth Attribute Protocol dissection
4 * Copyright 2012, Allan M. Madsen <allan.m@madsen.dk>
6 * Wireshark - Network traffic analyzer
7 * By Gerald Combs <gerald@wireshark.org>
8 * Copyright 1998 Gerald Combs
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1998 Gerald Combs
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
31 #include <epan/packet.h>
32 #include <epan/prefs.h>
33 #include <epan/expert.h>
35 #include "packet-bluetooth-hci.h"
36 #include "packet-btl2cap.h"
38 /* Initialize the protocol and registered fields */
39 static int proto_btatt = -1;
41 static int hf_btatt_opcode = -1;
42 static int hf_btatt_handle = -1;
43 static int hf_btatt_starting_handle = -1;
44 static int hf_btatt_ending_handle = -1;
45 static int hf_btatt_group_end_handle = -1;
46 static int hf_btatt_value = -1;
47 static int hf_btatt_req_opcode_in_error = -1;
48 static int hf_btatt_handle_in_error = -1;
49 static int hf_btatt_error_code = -1;
50 static int hf_btatt_uuid16 = -1;
51 static int hf_btatt_uuid128 = -1;
52 static int hf_btatt_client_rx_mtu = -1;
53 static int hf_btatt_server_rx_mtu = -1;
54 static int hf_btatt_uuid_format = -1;
55 static int hf_btatt_length = -1;
56 static int hf_btatt_offset = -1;
57 static int hf_btatt_flags = -1;
58 static int hf_btatt_sign_counter = -1;
59 static int hf_btatt_signature = -1;
60 static int hf_btatt_attribute_data = -1;
61 static int hf_btatt_handles_info = -1;
63 /* Initialize the subtree pointers */
64 static gint ett_btatt = -1;
65 static gint ett_btatt_list = -1;
67 static expert_field ei_btatt_uuid_format_unknown = EI_INIT;
68 static expert_field ei_btatt_handle_too_few = EI_INIT;
70 static dissector_handle_t btatt_handle;
73 static const value_string opcode_vals[] = {
74 {0x01, "Error Response"},
75 {0x02, "Exchange MTU Request"},
76 {0x03, "Exchange MTU Response"},
77 {0x04, "Find Information Request"},
78 {0x05, "Find Information Response"},
79 {0x06, "Find By Type Value Request"},
80 {0x07, "Find By Type Value Response"},
81 {0x08, "Read By Type Request"},
82 {0x09, "Read By Type Response"},
83 {0x0a, "Read Request"},
84 {0x0b, "Read Response"},
85 {0x0c, "Read Blob Request"},
86 {0x0d, "Read Blob Response"},
87 {0x0e, "Read Multiple Request"},
88 {0x0f, "Read Multiple Response"},
89 {0x10, "Read By Group Type Request"},
90 {0x11, "Read By Group Type Response"},
91 {0x12, "Write Request"},
92 {0x13, "Write Response"},
93 {0x16, "Prepare Write Request"},
94 {0x17, "Prepare Write Response"},
95 {0x18, "Execute Write Request"},
96 {0x19, "Execute Write Response"},
97 {0x1B, "Handle Value Notification"},
98 {0x1D, "Handle Value Indication"},
99 {0x1E, "Handle Value Confirmation"},
100 {0x52, "Write Command"},
101 {0xD2, "Signed Write Command"},
106 static const value_string error_vals[] = {
107 {0x01, "Invalid Handle"},
108 {0x02, "Read Not Permitted"},
109 {0x03, "Write Not Permitted"},
110 {0x04, "Invalid PDU"},
111 {0x05, "Insufficient Authentication"},
112 {0x06, "Request Not Supported"},
113 {0x07, "Invalid Offset"},
114 {0x08, "Insufficient Authorization"},
115 {0x09, "Prepare Queue Full"},
116 {0x0a, "Attribute Not Found"},
117 {0x0b, "Attribute Not Long"},
118 {0x0c, "Insufficient Encryption Key Size"},
119 {0x0d, "Invalid Attribute Value Length"},
120 {0x0e, "Unlikely Error"},
121 {0x0f, "Insufficient Encryption"},
122 {0x10, "Unsupported Group Type"},
123 {0x11, "Insufficient Resources"},
124 {0x80, "Application Error"},
125 {0xfd, "Improper Client Characteristic Configuration Descriptor"},
126 {0xfe, "Procedure Already In Progress"},
127 {0xff, "Out of Range"},
131 static const value_string uuid_format_vals[] = {
132 {0x01, "16-bit UUIDs"},
133 {0x02, "128-bit UUIDs"},
137 static const value_string flags_vals[] = {
138 {0x00, "Cancel All"},
139 {0x01, "Immediately Write All"},
143 void proto_register_btatt(void);
144 void proto_reg_handoff_btatt(void);
147 dissect_btatt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
150 proto_item *ti, *item;
151 proto_tree *st, *ltree;
154 if (tvb_length_remaining(tvb, 0) < 1)
157 ti = proto_tree_add_item(tree, proto_btatt, tvb, 0, -1, ENC_NA);
158 st = proto_item_add_subtree(ti, ett_btatt);
160 col_set_str(pinfo->cinfo, COL_PROTOCOL, "ATT");
162 switch (pinfo->p2p_dir) {
164 col_set_str(pinfo->cinfo, COL_INFO, "Sent ");
167 col_set_str(pinfo->cinfo, COL_INFO, "Rcvd ");
170 col_add_fstr(pinfo->cinfo, COL_INFO, "Unknown direction %d ",
175 item = proto_tree_add_item(st, hf_btatt_opcode, tvb, 0, 1, ENC_LITTLE_ENDIAN);
176 opcode = tvb_get_guint8(tvb, 0);
179 col_append_str(pinfo->cinfo, COL_INFO, val_to_str_const(opcode, opcode_vals, "<unknown>"));
182 case 0x01: /* Error Response */
183 proto_tree_add_item(st, hf_btatt_req_opcode_in_error, tvb, offset, 1, ENC_LITTLE_ENDIAN);
185 proto_tree_add_item(st, hf_btatt_handle_in_error, tvb, offset, 2, ENC_LITTLE_ENDIAN);
186 col_append_fstr(pinfo->cinfo, COL_INFO, " - %s, Handle: 0x%04x",
187 val_to_str_const(tvb_get_guint8(tvb, offset+2), error_vals, "<unknown>"),
188 tvb_get_letohs(tvb, offset));
190 proto_tree_add_item(st, hf_btatt_error_code, tvb, offset, 1, ENC_LITTLE_ENDIAN);
194 case 0x02: /* Exchange MTU Request */
195 col_append_fstr(pinfo->cinfo, COL_INFO, ", Client Rx MTU: %u", tvb_get_letohs(tvb, offset));
196 proto_tree_add_item(st, hf_btatt_client_rx_mtu, tvb, offset, 2, ENC_LITTLE_ENDIAN);
200 case 0x03: /* Exchange MTU Response */
201 col_append_fstr(pinfo->cinfo, COL_INFO, ", Server Rx MTU: %u", tvb_get_letohs(tvb, offset));
202 proto_tree_add_item(st, hf_btatt_server_rx_mtu, tvb, offset, 2, ENC_LITTLE_ENDIAN);
206 case 0x04: /* Find Information Request */
207 col_append_fstr(pinfo->cinfo, COL_INFO, ", Handles: 0x%04x..0x%04x",
208 tvb_get_letohs(tvb, offset), tvb_get_letohs(tvb, offset+2));
209 proto_tree_add_item(st, hf_btatt_starting_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
211 proto_tree_add_item(st, hf_btatt_ending_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
215 case 0x05: /* Find Information Response */
217 guint8 format = tvb_get_guint8(tvb, offset);
219 item = proto_tree_add_item(st, hf_btatt_uuid_format, tvb, offset, 1, ENC_LITTLE_ENDIAN);
223 while( tvb_length_remaining(tvb, offset) > 0) {
224 proto_tree_add_item(st, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
226 proto_tree_add_item(st, hf_btatt_uuid16, tvb, offset, 2, ENC_LITTLE_ENDIAN);
230 else if( format == 2 ) {
231 while( tvb_length_remaining(tvb, offset) > 0) {
232 proto_tree_add_item(st, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
234 proto_tree_add_item(st, hf_btatt_uuid128, tvb, offset, 16, ENC_NA);
239 expert_add_info(pinfo, item, &ei_btatt_uuid_format_unknown);
244 case 0x06: /* Find By Type Value Request */
245 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s, Handles: 0x%04x..0x%04x",
246 val_to_str_ext_const(tvb_get_letohs(tvb, offset+4), &bt_sig_uuid_vals_ext, "<unknown>"),
247 tvb_get_letohs(tvb, offset), tvb_get_letohs(tvb, offset+2));
249 proto_tree_add_item(st, hf_btatt_starting_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
251 proto_tree_add_item(st, hf_btatt_ending_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
253 proto_tree_add_item(st, hf_btatt_uuid16, tvb, offset, 2, ENC_LITTLE_ENDIAN);
255 proto_tree_add_item(st, hf_btatt_value, tvb, offset, -1, ENC_NA);
256 offset = tvb_reported_length(tvb);
259 case 0x07: /* Find By Type Value Response */
260 while( tvb_length_remaining(tvb, offset) > 0 ) {
261 item = proto_tree_add_none_format(st, hf_btatt_handles_info, tvb, offset, 4,
262 "Handles Info, Handle: 0x%04x, Group End Handle: 0x%04x",
263 tvb_get_letohs(tvb, offset), tvb_get_letohs(tvb, offset+2));
265 ltree = proto_item_add_subtree(item, ett_btatt_list);
267 proto_tree_add_item(ltree, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
269 proto_tree_add_item(ltree, hf_btatt_group_end_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
274 case 0x08: /* Read By Type Request */
275 case 0x10: /* Read By Group Type Request */
276 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s, Handles: 0x%04x..0x%04x",
277 val_to_str_ext_const(tvb_get_letohs(tvb, offset+4), &bt_sig_uuid_vals_ext, "<unknown>"),
278 tvb_get_letohs(tvb, offset), tvb_get_letohs(tvb, offset+2));
280 proto_tree_add_item(st, hf_btatt_starting_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
282 proto_tree_add_item(st, hf_btatt_ending_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
285 if (tvb_length_remaining(tvb, offset) == 2) {
286 proto_tree_add_item(st, hf_btatt_uuid16, tvb, offset, 2, ENC_LITTLE_ENDIAN);
289 else if (tvb_length_remaining(tvb, offset) == 16) {
290 item = proto_tree_add_item(st, hf_btatt_uuid128, tvb, offset, 16, ENC_NA);
291 proto_item_append_text(item, " (%s)", val_to_str_ext_const(tvb_get_letohs(tvb, offset),
292 &bt_sig_uuid_vals_ext, "<unknown>"));
297 case 0x09: /* Read By Type Response */
299 guint8 length = tvb_get_guint8(tvb, offset);
301 proto_tree_add_item(st, hf_btatt_length, tvb, offset, 1, ENC_LITTLE_ENDIAN);
305 col_append_fstr(pinfo->cinfo, COL_INFO, ", Attribute List Length: %u",
306 tvb_length_remaining(tvb, offset)/length);
308 while (tvb_length_remaining(tvb, offset) >= length)
310 item = proto_tree_add_none_format(st, hf_btatt_attribute_data, tvb,
311 offset, length, "Attribute Data, Handle: 0x%04x",
312 tvb_get_letohs(tvb, offset));
314 ltree = proto_item_add_subtree(item, ett_btatt_list);
316 proto_tree_add_item(ltree, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
318 proto_tree_add_item(ltree, hf_btatt_value, tvb, offset, length - 2, ENC_NA);
319 offset += (length-2);
325 case 0x0a: /* Read Request */
326 col_append_fstr(pinfo->cinfo, COL_INFO, ", Handle: 0x%04x", tvb_get_letohs(tvb, offset));
327 proto_tree_add_item(st, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
331 case 0x0b: /* Read Response */
332 case 0x0d: /* Read Blob Response */
333 case 0x0f: /* Multiple Read Response */
334 proto_tree_add_item(st, hf_btatt_value, tvb, offset, -1, ENC_NA);
335 offset = tvb_reported_length(tvb);
338 case 0x0c: /* Read Blob Request */
339 col_append_fstr(pinfo->cinfo, COL_INFO, ", Handle: 0x%04x, Offset: %u",
340 tvb_get_letohs(tvb, offset), tvb_get_letohs(tvb, offset+2));
341 proto_tree_add_item(st, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
343 proto_tree_add_item(st, hf_btatt_offset, tvb, offset, 2, ENC_LITTLE_ENDIAN);
347 case 0x0e: /* Multiple Read Request */
348 if(tvb_length_remaining(tvb, offset) < 4) {
349 expert_add_info(pinfo, item, &ei_btatt_handle_too_few);
353 col_append_str(pinfo->cinfo, COL_INFO, ", Handles: ");
354 while (tvb_length_remaining(tvb, offset) >= 2) {
355 proto_tree_add_item(st, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
356 col_append_fstr(pinfo->cinfo, COL_INFO, "0x%04x ", tvb_get_letohs(tvb, offset));
361 case 0x11: /* Read By Group Type Response */
363 guint8 length = tvb_get_guint8(tvb, offset);
365 proto_tree_add_item(st, hf_btatt_length, tvb, offset, 1, ENC_LITTLE_ENDIAN);
369 col_append_fstr(pinfo->cinfo, COL_INFO, ", Attribute List Length: %u", tvb_length_remaining(tvb, offset)/length);
371 while (tvb_length_remaining(tvb, offset) >= length) {
372 item = proto_tree_add_none_format(st, hf_btatt_attribute_data, tvb, offset, length,
373 "Attribute Data, Handle: 0x%04x, Group End Handle: 0x%04x",
374 tvb_get_letohs(tvb, offset), tvb_get_letohs(tvb, offset+2));
376 ltree = proto_item_add_subtree(item, ett_btatt_list);
378 proto_tree_add_item(ltree, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
380 proto_tree_add_item(ltree, hf_btatt_group_end_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
382 proto_tree_add_item(ltree, hf_btatt_value, tvb, offset, length - 4, ENC_NA);
383 offset += (length-4);
389 case 0x12: /* Write Request */
390 case 0x52: /* Write Command */
391 case 0x1b: /* Handle Value Notification */
392 case 0x1d: /* Handle Value Indication */
393 col_append_fstr(pinfo->cinfo, COL_INFO, ", Handle: 0x%04x", tvb_get_letohs(tvb, offset));
394 proto_tree_add_item(st, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
396 proto_tree_add_item(st, hf_btatt_value, tvb, offset, -1, ENC_NA);
397 offset = tvb_reported_length(tvb);
400 case 0x16: /* Prepare Write Request */
401 case 0x17: /* Prepare Write Response */
402 col_append_fstr(pinfo->cinfo, COL_INFO, ", Handle: 0x%04x, Offset: %u",
403 tvb_get_letohs(tvb, offset), tvb_get_letohs(tvb, offset+2));
404 proto_tree_add_item(st, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
406 proto_tree_add_item(st, hf_btatt_offset, tvb, offset, 2, ENC_LITTLE_ENDIAN);
408 proto_tree_add_item(st, hf_btatt_value, tvb, offset, -1, ENC_NA);
409 offset = tvb_reported_length(tvb);
412 case 0x18: /* Execute Write Request */
413 col_append_fstr(pinfo->cinfo, COL_INFO, ", %s",
414 val_to_str_const(tvb_get_guint8(tvb, offset), flags_vals, "<unknown>"));
415 proto_tree_add_item(st, hf_btatt_flags, tvb, offset, 1, ENC_LITTLE_ENDIAN);
419 case 0xd2: /* Signed Write Command */
423 col_append_fstr(pinfo->cinfo, COL_INFO, ", Handle: 0x%04x", tvb_get_letohs(tvb, offset));
424 proto_tree_add_item(st, hf_btatt_handle, tvb, offset, 2, ENC_LITTLE_ENDIAN);
426 length = tvb_length_remaining(tvb, offset);
428 proto_tree_add_item(st, hf_btatt_value, tvb, offset, length-12, ENC_NA);
432 proto_tree_add_item(st, hf_btatt_sign_counter, tvb, offset, 4, ENC_LITTLE_ENDIAN);
434 proto_tree_add_item(st, hf_btatt_signature, tvb, offset, 8, ENC_NA);
445 proto_register_btatt(void)
449 static hf_register_info hf[] = {
451 {"Opcode", "btatt.opcode",
452 FT_UINT8, BASE_HEX, VALS(opcode_vals), 0x0,
455 {&hf_btatt_handles_info,
456 {"Handles Info", "btatt.handles_info",
457 FT_NONE, BASE_NONE, NULL, 0x0,
460 {&hf_btatt_attribute_data,
461 {"Attribute Data", "btatt.attribute_data",
462 FT_NONE, BASE_NONE, NULL, 0x0,
466 {"Handle", "btatt.handle",
467 FT_UINT16, BASE_HEX, NULL, 0x0,
470 {&hf_btatt_starting_handle,
471 {"Starting Handle", "btatt.starting_handle",
472 FT_UINT16, BASE_HEX, NULL, 0x0,
475 {&hf_btatt_ending_handle,
476 {"Ending Handle", "btatt.ending_handle",
477 FT_UINT16, BASE_HEX, NULL, 0x0,
480 {&hf_btatt_group_end_handle,
481 {"Group End Handle", "btatt.group_end_handle",
482 FT_UINT16, BASE_HEX, NULL, 0x0,
486 {"Value", "btatt.value",
487 FT_BYTES, BASE_NONE, NULL, 0x0,
490 {&hf_btatt_req_opcode_in_error,
491 {"Request Opcode in Error", "btatt.req_opcode_in_error",
492 FT_UINT8, BASE_HEX, VALS(opcode_vals), 0x0,
495 {&hf_btatt_handle_in_error,
496 {"Handle in Error", "btatt.handle_in_error",
497 FT_UINT16, BASE_HEX, NULL, 0x0,
500 {&hf_btatt_error_code,
501 {"Error Code", "btatt.error_code",
502 FT_UINT8, BASE_HEX, VALS(error_vals), 0x0,
506 {"UUID", "btatt.uuid16",
507 FT_UINT16, BASE_HEX |BASE_EXT_STRING, &bt_sig_uuid_vals_ext, 0x0,
511 {"UUID", "btatt.uuid128",
512 FT_BYTES, BASE_NONE, NULL, 0x0,
515 {&hf_btatt_client_rx_mtu,
516 {"Client Rx MTU", "btatt.client_rx_mtu",
517 FT_UINT16, BASE_DEC, NULL, 0x0,
520 {&hf_btatt_server_rx_mtu,
521 {"Server Rx MTU", "btatt.server_rx_mtu",
522 FT_UINT16, BASE_DEC, NULL, 0x0,
525 {&hf_btatt_uuid_format,
526 {"UUID Format", "btatt.uuid_format",
527 FT_UINT8, BASE_HEX, VALS(uuid_format_vals), 0x0,
531 {"Length", "btatt.length",
532 FT_UINT8, BASE_DEC, NULL, 0x0,
533 "Length of Handle/Value Pair", HFILL}
536 {"Offset", "btatt.offset",
537 FT_UINT16, BASE_DEC, NULL, 0x0,
541 {"Flags", "btatt.flags",
542 FT_UINT8, BASE_HEX, VALS(flags_vals), 0x0,
545 {&hf_btatt_sign_counter,
546 {"Sign Counter", "btatt.sign_counter",
547 FT_UINT32, BASE_DEC, NULL, 0x0,
550 {&hf_btatt_signature,
551 {"Signature", "btatt.signature",
552 FT_BYTES, BASE_NONE, NULL, 0x0,
557 /* Setup protocol subtree array */
558 static gint *ett[] = {
563 static ei_register_info ei[] = {
564 { &ei_btatt_uuid_format_unknown, { "btatt.uuid_format.unknown", PI_PROTOCOL, PI_WARN, "Unknown format", EXPFILL }},
565 { &ei_btatt_handle_too_few, { "btatt.handle.too_few", PI_PROTOCOL, PI_WARN, "Too few handles, should be 2 or more", EXPFILL }},
568 expert_module_t* expert_btatt;
570 /* Register the protocol name and description */
571 proto_btatt = proto_register_protocol("Bluetooth Attribute Protocol", "BT ATT", "btatt");
573 btatt_handle = new_register_dissector("btatt", dissect_btatt, proto_btatt);
575 /* Required function calls to register the header fields and subtrees used */
576 proto_register_field_array(proto_btatt, hf, array_length(hf));
577 proto_register_subtree_array(ett, array_length(ett));
578 expert_btatt = expert_register_protocol(proto_btatt);
579 expert_register_field_array(expert_btatt, ei, array_length(ei));
581 module = prefs_register_protocol(proto_btatt, NULL);
582 prefs_register_static_text_preference(module, "att.version",
583 "Bluetooth Protocol ATT version from Core 4.0",
584 "Version of protocol supported by this dissector.");
588 proto_reg_handoff_btatt(void)
590 dissector_add_uint("btl2cap.psm", BTL2CAP_PSM_ATT, btatt_handle);
591 dissector_add_uint("btl2cap.cid", BTL2CAP_FIXED_CID_ATT, btatt_handle);
595 * Editor modelines - http://www.wireshark.org/tools/modelines.html
600 * indent-tabs-mode: nil
603 * vi: set shiftwidth=4 tabstop=8 expandtab:
604 * :indentSize=4:tabSize=8:noTabs=true: