From 63a7246a863f756911fbbfd821aa5696b5377a72 Mon Sep 17 00:00:00 2001 From: "matthias.ringwald" Date: Tue, 30 Oct 2012 19:55:24 +0000 Subject: [PATCH] improved l2cap error handling, better config options validation --- src/l2cap.c | 49 +++++++++++++++++++++++++++++++++++++++++++------ src/l2cap.h | 26 ++++++++++++++++++-------- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/l2cap.c b/src/l2cap.c index 4f70e2e8a..f7412f0d2 100644 --- a/src/l2cap.c +++ b/src/l2cap.c @@ -376,7 +376,7 @@ void l2cap_run(void){ hci_con_handle_t handle = signaling_responses[0].handle; uint8_t sig_id = signaling_responses[0].sig_id; uint16_t infoType = signaling_responses[0].data; // INFORMATION_REQUEST - uint16_t result = signaling_responses[0].data; // CONNECTION_REQUEST + uint16_t result = signaling_responses[0].data; // CONNECTION_REQUEST, COMMAND_REJECT switch (signaling_responses[0].code){ case CONNECTION_REQUEST: @@ -395,6 +395,9 @@ void l2cap_run(void){ l2cap_send_signaling_packet(handle, INFORMATION_RESPONSE, sig_id, infoType, 1, 0, NULL); } break; + case COMMAND_REJECT: + l2cap_send_signaling_packet(handle, COMMAND_REJECT, sig_id, result); + break; default: // should not happen break; @@ -453,9 +456,25 @@ void l2cap_run(void){ case L2CAP_STATE_CONFIG: if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP){ + uint16_t flags = 0; channelStateVarClearFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP); - channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP); - l2cap_send_signaling_packet(channel->handle, CONFIGURE_RESPONSE, channel->remote_sig_id, channel->remote_cid, 0, 0, 0, NULL); + if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_CONT) { + flags = 1; + } else { + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP); + } + if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_INVALID){ + l2cap_send_signaling_packet(channel->handle, CONFIGURE_RESPONSE, channel->remote_sig_id, channel->remote_cid, flags, L2CAP_CONF_RESULT_UNKNOWN_OPTIONS, 0, NULL); + } else if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_MTU){ + config_options[0] = 1; // MTU + config_options[1] = 2; // len param + bt_store_16( (uint8_t*)&config_options, 2, channel->remote_mtu); + l2cap_send_signaling_packet(channel->handle, CONFIGURE_RESPONSE, channel->remote_sig_id, channel->remote_cid, flags, 0, 4, &config_options); + channelStateVarClearFlag(channel,L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_MTU); + } else { + l2cap_send_signaling_packet(channel->handle, CONFIGURE_RESPONSE, channel->remote_sig_id, channel->remote_cid, flags, 0, 0, NULL); + } + channelStateVarClearFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_CONT); } else if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ){ channelStateVarClearFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ); @@ -783,16 +802,30 @@ void l2cap_signaling_handle_configure_request(l2cap_channel_t *channel, uint8_t channel->remote_sig_id = command[L2CAP_SIGNALING_COMMAND_SIGID_OFFSET]; + uint16_t flags = READ_BT_16(command, 6); + if (flags & 1) { + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_CONT); + } + // accept the other's configuration options uint16_t end_pos = 4 + READ_BT_16(command, L2CAP_SIGNALING_COMMAND_LENGTH_OFFSET); uint16_t pos = 8; while (pos < end_pos){ - uint8_t type = command[pos++]; + uint8_t option_hint = command[pos] >> 7; + uint8_t option_type = command[pos] & 0x7f; + log_info("l2cap cid %u, hint %u, type %u", channel->local_cid, option_hint, option_type); + pos++; uint8_t length = command[pos++]; // MTU { type(8): 1, len(8):2, MTU(16) } - if ((type & 0x7f) == 1 && length == 2){ + if (option_type == 1 && length == 2){ channel->remote_mtu = READ_BT_16(command, pos); // log_info("l2cap cid 0x%02x, remote mtu %u\n", channel->local_cid, channel->remote_mtu); + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_MTU); + } + // check for unknown options + if (option_hint == 0 && (option_type == 0 || option_type >= 0x07)){ + log_info("l2cap cid %u, unknown options"); + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_INVALID); } pos += length; } @@ -876,9 +909,12 @@ void l2cap_signaling_handler_channel(l2cap_channel_t *channel, uint8_t *command) case L2CAP_STATE_CONFIG: switch (code) { case CONFIGURE_REQUEST: - channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ); channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP); l2cap_signaling_handle_configure_request(channel, command); + if (!(channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_CONT)){ + // only done if continuation not set + channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ); + } break; case CONFIGURE_RESPONSE: channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP); @@ -927,6 +963,7 @@ void l2cap_signaling_handler_dispatch( hci_con_handle_t handle, uint8_t * comman // not for a particular channel, and not CONNECTION_REQUEST, ECHO_[REQUEST|RESPONSE], INFORMATION_REQUEST if (code < 1 || code == ECHO_RESPONSE || code > INFORMATION_REQUEST){ + l2cap_register_signaling_response(handle, COMMAND_REJECT, sig_id, L2CAP_REJ_CMD_UNKNOWN); return; } diff --git a/src/l2cap.h b/src/l2cap.h index 48b8645fc..a743b5c08 100644 --- a/src/l2cap.h +++ b/src/l2cap.h @@ -75,6 +75,12 @@ extern "C" { #define L2CAP_CID_ATTRIBUTE_PROTOCOL 0x0004 #define L2CAP_CID_SIGNALING_LE 0x0005 #define L2CAP_CID_SECURITY_MANAGER_PROTOCOL 0x0006 + +// L2CAP Configuration Result Codes +#define L2CAP_CONF_RESULT_UNKNOWN_OPTIONS 0x0003 + +// L2CAP Reject Result Codes +#define L2CAP_REJ_CMD_UNKNOWN 0x0000 void l2cap_init(void); void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)); @@ -125,13 +131,17 @@ typedef enum { } L2CAP_STATE; typedef enum { - L2CAP_CHANNEL_STATE_VAR_NONE = 0, - L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ = 1 << 0, - L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP = 1 << 1, - L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ = 1 << 2, - L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP = 1 << 3, - L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ = 1 << 4, - L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP = 1 << 5, + L2CAP_CHANNEL_STATE_VAR_NONE = 0, + L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ = 1 << 0, + L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP = 1 << 1, + L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ = 1 << 2, + L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP = 1 << 3, + L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ = 1 << 4, + L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP = 1 << 5, + L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_MTU = 1 << 6, // in CONF RSP, add MTU field + L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_CONT = 1 << 7, // in CONF RSP, set CONTINUE flag + L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP_INVALID = 1 << 8, // in CONF RSP, send UNKNOWN OPTIONS + L2CAP_CHANNEL_STATE_VAR_SEND_CMD_REJ_UNKNOWN = 1 << 9, // send CMD_REJ with reason unknown } L2CAP_CHANNEL_STATE_VAR; // info regarding an actual coneection @@ -192,7 +202,7 @@ typedef struct l2cap_signaling_response { hci_con_handle_t handle; uint8_t sig_id; uint8_t code; - uint16_t data; // infoType for INFORMATION REQUEST, result for CONNECTION request + uint16_t data; // infoType for INFORMATION REQUEST, result for CONNECTION request and command unknown } l2cap_signaling_response_t;