l2cap: handle negative and invalid information requests during ERTM config

This commit is contained in:
Matthias Ringwald 2019-08-08 12:24:41 +02:00
parent b90eac910b
commit 0defadfba9
2 changed files with 46 additions and 43 deletions

View File

@ -14,12 +14,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- POSIX, WICED, Windows, FreeRTOS: handle time_ms overrun after 49 days
- L2CAP ERTM: stop timers on disconnect
- L2CAP ERTM: fix sending of segmented SDUs
- L2CAP ERTM: handle unsupported Information Response
### Added
- btstack_run_loop_base: portable implementation of timer and data source managment as base for platform specific implementations
## Changed
-- FreeRTOS: use xQueueCreateStatic (static variant of xQueueCreate) if available for static memory-only setups
- FreeRTOS: use xQueueCreateStatic (static variant of xQueueCreate) if available for static memory-only setups
- L2CAP ERTM: handle invalid Information Response (assume no extended features supported)
## Changes July 2019

View File

@ -2751,52 +2751,53 @@ static void l2cap_signaling_handler_dispatch(hci_con_handle_t handle, uint8_t *
case INFORMATION_RESPONSE: {
hci_connection_t * connection = hci_connection_for_handle(handle);
if (!connection) return;
if (cmd_len >= 4) {
if (connection->l2cap_state.information_state != L2CAP_INFORMATION_STATE_W4_EXTENDED_FEATURE_RESPONSE) return;
// get extended features from response if valid
connection->l2cap_state.extended_feature_mask = 0;
if (cmd_len >= 6) {
uint16_t info_type = little_endian_read_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET);
uint16_t result = little_endian_read_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET+2);
if (result != 0) return;
if (info_type != L2CAP_INFO_TYPE_EXTENDED_FEATURES_SUPPORTED) return;
if (cmd_len >= 6) {
connection->l2cap_state.information_state = L2CAP_INFORMATION_STATE_DONE;
uint16_t result = little_endian_read_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET+2);
if (result == 0 && info_type == L2CAP_INFO_TYPE_EXTENDED_FEATURES_SUPPORTED) {
connection->l2cap_state.extended_feature_mask = little_endian_read_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET+4);
log_info("extended features mask 0x%02x", connection->l2cap_state.extended_feature_mask);
// trigger connection request
btstack_linked_list_iterator_init(&it, &l2cap_channels);
while (btstack_linked_list_iterator_has_next(&it)){
l2cap_channel_t * channel = (l2cap_channel_t *) btstack_linked_list_iterator_next(&it);
if (!l2cap_is_dynamic_channel_type(channel->channel_type)) continue;
if (channel->con_handle != handle) continue;
// bail if ERTM was requested but is not supported
if ((channel->mode == L2CAP_CHANNEL_MODE_ENHANCED_RETRANSMISSION) && ((connection->l2cap_state.extended_feature_mask & 0x08) == 0)){
if (channel->ertm_mandatory){
// channel closed
channel->state = L2CAP_STATE_CLOSED;
// map l2cap connection response result to BTstack status enumeration
l2cap_handle_channel_open_failed(channel, L2CAP_CONNECTION_RESPONSE_RESULT_ERTM_NOT_SUPPORTED);
// discard channel
btstack_linked_list_remove(&l2cap_channels, (btstack_linked_item_t *) channel);
l2cap_free_channel_entry(channel);
continue;
} else {
// fallback to Basic mode
l2cap_emit_simple_event_with_cid(channel, L2CAP_EVENT_ERTM_BUFFER_RELEASED);
channel->mode = L2CAP_CHANNEL_MODE_BASIC;
}
}
// start connecting
if (channel->state == L2CAP_STATE_WAIT_OUTGOING_EXTENDED_FEATURES){
channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST;
}
// respond to connection request
if (channel->state == L2CAP_STATE_WAIT_INCOMING_EXTENDED_FEATURES){
channel->state = L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT;
l2cap_emit_incoming_connection(channel);
}
}
return; // cmd len valid
}
}
l2cap_register_signaling_response(handle, COMMAND_REJECT, sig_id, 0, L2CAP_REJ_CMD_UNKNOWN);
connection->l2cap_state.information_state = L2CAP_INFORMATION_STATE_DONE;
log_info("extended features mask 0x%02x", connection->l2cap_state.extended_feature_mask);
// trigger connection request
btstack_linked_list_iterator_init(&it, &l2cap_channels);
while (btstack_linked_list_iterator_has_next(&it)){
l2cap_channel_t * channel = (l2cap_channel_t *) btstack_linked_list_iterator_next(&it);
if (!l2cap_is_dynamic_channel_type(channel->channel_type)) continue;
if (channel->con_handle != handle) continue;
// bail if ERTM was requested but is not supported
if ((channel->mode == L2CAP_CHANNEL_MODE_ENHANCED_RETRANSMISSION) && ((connection->l2cap_state.extended_feature_mask & 0x08) == 0)){
if (channel->ertm_mandatory){
// channel closed
channel->state = L2CAP_STATE_CLOSED;
// map l2cap connection response result to BTstack status enumeration
l2cap_handle_channel_open_failed(channel, L2CAP_CONNECTION_RESPONSE_RESULT_ERTM_NOT_SUPPORTED);
// discard channel
btstack_linked_list_remove(&l2cap_channels, (btstack_linked_item_t *) channel);
l2cap_free_channel_entry(channel);
continue;
} else {
// fallback to Basic mode
l2cap_emit_simple_event_with_cid(channel, L2CAP_EVENT_ERTM_BUFFER_RELEASED);
channel->mode = L2CAP_CHANNEL_MODE_BASIC;
}
}
// start connecting
if (channel->state == L2CAP_STATE_WAIT_OUTGOING_EXTENDED_FEATURES){
channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST;
}
// respond to connection request
if (channel->state == L2CAP_STATE_WAIT_INCOMING_EXTENDED_FEATURES){
channel->state = L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT;
l2cap_emit_incoming_connection(channel);
}
}
return;
}
#endif