Command - Completion Event Association Bug in bthci dissectors
In the bthci_evt and bthci_cmd dissectors there is the following bug:
Let's say the host issues a get remote version information command to the controller. The controller returns a pending event to the host and after a while a completion event. Clicking the command in the Wireshark UI and looking at the command-completion and command-pending response times and the little arrows on the left, it's evident that the command is associated with the pending and completion event correctly in the happy case. However, if an HCI disconnect complete event is introduced anywhere between the command and the completion event, according to the Bluetooth specification:
...When the Host receives an HCI_Disconnection_Complete, HCI_Disconnection_Physical_Link_Complete or HCI_Disconnection_Logical_Link_Complete event, the Host shall assume that all unacknowledged HCI Data packets that have been sent to the Controller for the returned Handle have been flushed, and that the corresponding data buffers have been freed...
So there should not be any pending and/or completion events (depending where the disconnect event was received) after the disconnect complete event. However, this is not supported by the dissector, as when the next completion event for the same command opcode arrives at a later time, that completion event is associated with the command that arrived before the disconnect event and not the actual command that caused it. In other words, the current implementation of associating commands with pending/completion events in bthci assumes that there must exist pending and completion events for an HCI command, which is not always true in reality. This mangles the reported command-pending and command-completion time differences for that command's opcode from the disconnect event onward.
I had to use said time difference calculation feature of tshark so I fixed this issue locally by adding code to the function dissect_bthci_evt_disconnect_complete
to search all HCI commands that were yet to be associated with an HCI event in the bthci_cmds
global variable, which holds all HCI commands, for the specific interface_id
and adapter_id
and setting the pending_in_frame
and response_in_frame
to 0 if they haven't been set yet. I am using the following code to iterate over bthci_cmd
:
key[0].length = 1;
key[0].key = &interface_id;
key[1].length = 1;
key[1].key = &adapter_id;
key[2].length = 0;
key[2].key = NULL;
/* Get all commands from bthci_cmds which correspond to this
* interface_id and adapter_id
*/
subtree = (wmem_tree_t *) wmem_tree_lookup32_array(bthci_cmds, key);
// Iterate over bthci_cmds and call call_foreach() on each entry
wmem_tree_foreach(bthci_cmds, call_foreach, (void*)pinfo);
and the function call_foreach is
static gboolean
call_foreach(const void *key _U_, void *value, void *data _U_){
bthci_cmd_data_t *bthci_cmd_data = (bthci_cmd_data_t*)value;
if (bthci_cmd_data->pending_in_frame == max_disconnect_in_frame)
{
bthci_cmd_data->pending_in_frame = 0;
}
if (bthci_cmd_data->response_in_frame == max_disconnect_in_frame)
{
bthci_cmd_data->response_in_frame = 0;
}
return FALSE;
}
However, I'm afraid that for very large captures this might be suboptimal, because I need to iterate over all previous commands for each disconnection event. I would appreciate some input from seasoned developers on this approach.