Bluetooth: controller: Disallow mixing legacy/ext adv HCI commands

As per Core 5.2, Part B, section 3.1.1 controller should not allow
mixing of legacy and extended advertising commands. Once 1st command
is received from host, we should only allow commands of the same type
and return an error on other commands.

If legacy advertising interface is selected by 1st command received,
we need to make sure that set with handle=0 is created since it's
used by all legacy commands.

When not using external host, we assume that only extended advertising
interface will be used thus all checks can be skipped (optimized and
compiled-out).

Signed-off-by: Andrzej Kaczmarek <andrzej.kaczmarek@codecoup.pl>
This commit is contained in:
Andrzej Kaczmarek 2020-06-30 12:25:04 +02:00 committed by Carles Cufí
commit ae987f3f3a
3 changed files with 174 additions and 0 deletions

View file

@ -103,6 +103,51 @@ static uint64_t event_mask = DEFAULT_EVENT_MASK;
static uint64_t event_mask_page_2 = DEFAULT_EVENT_MASK_PAGE_2; static uint64_t event_mask_page_2 = DEFAULT_EVENT_MASK_PAGE_2;
static uint64_t le_event_mask = DEFAULT_LE_EVENT_MASK; static uint64_t le_event_mask = DEFAULT_LE_EVENT_MASK;
static struct net_buf *cmd_complete_status(uint8_t status);
#if defined(CONFIG_BT_CTLR_ADV_EXT)
static int adv_cmds_legacy_check(struct net_buf **cc_evt)
{
int err;
#if defined(CONFIG_BT_HCI_RAW)
err = ll_adv_cmds_set(LL_ADV_CMDS_LEGACY);
if (err && cc_evt) {
*cc_evt = cmd_complete_status(BT_HCI_ERR_CMD_DISALLOWED);
}
#else
if (cc_evt) {
*cc_evt = cmd_complete_status(BT_HCI_ERR_CMD_DISALLOWED);
}
err = -EINVAL;
#endif /* CONFIG_BT_HCI_RAW */
return err;
}
static int adv_cmds_ext_check(struct net_buf **cc_evt)
{
int err;
#if defined(CONFIG_BT_HCI_RAW)
err = ll_adv_cmds_set(LL_ADV_CMDS_EXT);
if (err && cc_evt) {
*cc_evt = cmd_complete_status(BT_HCI_ERR_CMD_DISALLOWED);
}
#else
err = 0;
#endif /* CONFIG_BT_HCI_RAW */
return err;
}
#else
static inline int adv_cmds_legacy_check(struct net_buf **cc_evt)
{
return 0;
}
#endif /* CONFIG_BT_CTLR_ADV_EXT */
#if defined(CONFIG_BT_CONN) #if defined(CONFIG_BT_CONN)
static void le_conn_complete(struct pdu_data *pdu_data, uint16_t handle, static void le_conn_complete(struct pdu_data *pdu_data, uint16_t handle,
struct net_buf *buf); struct net_buf *buf);
@ -912,6 +957,10 @@ static void le_set_adv_param(struct net_buf *buf, struct net_buf **evt)
uint16_t min_interval; uint16_t min_interval;
uint8_t status; uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
min_interval = sys_le16_to_cpu(cmd->min_interval); min_interval = sys_le16_to_cpu(cmd->min_interval);
if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) && if (IS_ENABLED(CONFIG_BT_CTLR_PARAM_CHECK) &&
@ -945,6 +994,10 @@ static void le_read_adv_chan_tx_power(struct net_buf *buf, struct net_buf **evt)
{ {
struct bt_hci_rp_le_read_chan_tx_power *rp; struct bt_hci_rp_le_read_chan_tx_power *rp;
if (adv_cmds_legacy_check(evt)) {
return;
}
rp = hci_cmd_complete(evt, sizeof(*rp)); rp = hci_cmd_complete(evt, sizeof(*rp));
rp->status = 0x00; rp->status = 0x00;
@ -957,6 +1010,10 @@ static void le_set_adv_data(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_adv_data *cmd = (void *)buf->data; struct bt_hci_cp_le_set_adv_data *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT) #if defined(CONFIG_BT_CTLR_ADV_EXT)
status = ll_adv_data_set(0, cmd->len, &cmd->data[0]); status = ll_adv_data_set(0, cmd->len, &cmd->data[0]);
#else /* !CONFIG_BT_CTLR_ADV_EXT */ #else /* !CONFIG_BT_CTLR_ADV_EXT */
@ -971,6 +1028,10 @@ static void le_set_scan_rsp_data(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_scan_rsp_data *cmd = (void *)buf->data; struct bt_hci_cp_le_set_scan_rsp_data *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT) #if defined(CONFIG_BT_CTLR_ADV_EXT)
status = ll_adv_scan_rsp_set(0, cmd->len, &cmd->data[0]); status = ll_adv_scan_rsp_set(0, cmd->len, &cmd->data[0]);
#else /* !CONFIG_BT_CTLR_ADV_EXT */ #else /* !CONFIG_BT_CTLR_ADV_EXT */
@ -985,6 +1046,10 @@ static void le_set_adv_enable(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_adv_enable *cmd = (void *)buf->data; struct bt_hci_cp_le_set_adv_enable *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
#if defined(CONFIG_BT_CTLR_ADV_EXT) || defined(CONFIG_BT_HCI_MESH_EXT) #if defined(CONFIG_BT_CTLR_ADV_EXT) || defined(CONFIG_BT_HCI_MESH_EXT)
#if defined(CONFIG_BT_HCI_MESH_EXT) #if defined(CONFIG_BT_HCI_MESH_EXT)
status = ll_adv_enable(0, cmd->enable, 0, 0, 0, 0, 0); status = ll_adv_enable(0, cmd->enable, 0, 0, 0, 0, 0);
@ -1007,6 +1072,10 @@ static void le_set_scan_param(struct net_buf *buf, struct net_buf **evt)
uint16_t window; uint16_t window;
uint8_t status; uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
interval = sys_le16_to_cpu(cmd->interval); interval = sys_le16_to_cpu(cmd->interval);
window = sys_le16_to_cpu(cmd->window); window = sys_le16_to_cpu(cmd->window);
@ -1021,6 +1090,10 @@ static void le_set_scan_enable(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_scan_enable *cmd = (void *)buf->data; struct bt_hci_cp_le_set_scan_enable *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_legacy_check(evt)) {
return;
}
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 #if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
/* initialize duplicate filtering */ /* initialize duplicate filtering */
if (cmd->enable && cmd->filter_dup) { if (cmd->enable && cmd->filter_dup) {
@ -1049,6 +1122,11 @@ static void le_create_connection(struct net_buf *buf, struct net_buf **evt)
uint16_t scan_window; uint16_t scan_window;
uint8_t status; uint8_t status;
if (adv_cmds_legacy_check(NULL)) {
*evt = cmd_status(BT_HCI_ERR_CMD_DISALLOWED);
return;
}
scan_interval = sys_le16_to_cpu(cmd->scan_interval); scan_interval = sys_le16_to_cpu(cmd->scan_interval);
scan_window = sys_le16_to_cpu(cmd->scan_window); scan_window = sys_le16_to_cpu(cmd->scan_window);
conn_interval_max = sys_le16_to_cpu(cmd->conn_interval_max); conn_interval_max = sys_le16_to_cpu(cmd->conn_interval_max);
@ -1581,6 +1659,10 @@ static void le_set_adv_set_random_addr(struct net_buf *buf,
struct bt_hci_cp_le_set_adv_set_random_addr *cmd = (void *)buf->data; struct bt_hci_cp_le_set_adv_set_random_addr *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_aux_random_addr_set(cmd->handle, &cmd->bdaddr.val[0]); status = ll_adv_aux_random_addr_set(cmd->handle, &cmd->bdaddr.val[0]);
*evt = cmd_complete_status(status); *evt = cmd_complete_status(status);
@ -1597,6 +1679,10 @@ static void le_set_ext_adv_param(struct net_buf *buf, struct net_buf **evt)
uint8_t phy_p; uint8_t phy_p;
uint8_t phy_s; uint8_t phy_s;
if (adv_cmds_ext_check(evt)) {
return;
}
evt_prop = sys_le16_to_cpu(cmd->props); evt_prop = sys_le16_to_cpu(cmd->props);
min_interval = sys_get_le24(cmd->prim_min_interval); min_interval = sys_get_le24(cmd->prim_min_interval);
tx_pwr = cmd->tx_power; tx_pwr = cmd->tx_power;
@ -1620,6 +1706,10 @@ static void le_set_ext_adv_data(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_ext_adv_data *cmd = (void *)buf->data; struct bt_hci_cp_le_set_ext_adv_data *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_aux_ad_data_set(cmd->handle, cmd->op, cmd->frag_pref, status = ll_adv_aux_ad_data_set(cmd->handle, cmd->op, cmd->frag_pref,
cmd->len, cmd->data); cmd->len, cmd->data);
@ -1631,6 +1721,10 @@ static void le_set_ext_scan_rsp_data(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_ext_scan_rsp_data *cmd = (void *)buf->data; struct bt_hci_cp_le_set_ext_scan_rsp_data *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_aux_sr_data_set(cmd->handle, cmd->op, cmd->frag_pref, status = ll_adv_aux_sr_data_set(cmd->handle, cmd->op, cmd->frag_pref,
cmd->len, cmd->data); cmd->len, cmd->data);
@ -1645,6 +1739,10 @@ static void le_set_ext_adv_enable(struct net_buf *buf, struct net_buf **evt)
uint8_t enable; uint8_t enable;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
set_num = cmd->set_num; set_num = cmd->set_num;
if (!set_num) { if (!set_num) {
if (cmd->enable) { if (cmd->enable) {
@ -1686,6 +1784,10 @@ static void le_read_max_adv_data_len(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_rp_le_read_max_adv_data_len *rp; struct bt_hci_rp_le_read_max_adv_data_len *rp;
uint16_t max_adv_data_len; uint16_t max_adv_data_len;
if (adv_cmds_ext_check(evt)) {
return;
}
rp = hci_cmd_complete(evt, sizeof(*rp)); rp = hci_cmd_complete(evt, sizeof(*rp));
max_adv_data_len = ll_adv_aux_max_data_length_get(); max_adv_data_len = ll_adv_aux_max_data_length_get();
@ -1698,6 +1800,10 @@ static void le_read_num_adv_sets(struct net_buf *buf, struct net_buf **evt)
{ {
struct bt_hci_rp_le_read_num_adv_sets *rp; struct bt_hci_rp_le_read_num_adv_sets *rp;
if (adv_cmds_ext_check(evt)) {
return;
}
rp = hci_cmd_complete(evt, sizeof(*rp)); rp = hci_cmd_complete(evt, sizeof(*rp));
rp->num_sets = ll_adv_aux_set_count_get(); rp->num_sets = ll_adv_aux_set_count_get();
@ -1709,6 +1815,10 @@ static void le_remove_adv_set(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_remove_adv_set *cmd = (void *)buf->data; struct bt_hci_cp_le_remove_adv_set *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_aux_set_remove(cmd->handle); status = ll_adv_aux_set_remove(cmd->handle);
*evt = cmd_complete_status(status); *evt = cmd_complete_status(status);
@ -1718,6 +1828,10 @@ static void le_clear_adv_sets(struct net_buf *buf, struct net_buf **evt)
{ {
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_aux_set_clear(); status = ll_adv_aux_set_clear();
*evt = cmd_complete_status(status); *evt = cmd_complete_status(status);
@ -1731,6 +1845,10 @@ static void le_set_per_adv_param(struct net_buf *buf, struct net_buf **evt)
uint16_t flags; uint16_t flags;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
interval = sys_le16_to_cpu(cmd->max_interval); interval = sys_le16_to_cpu(cmd->max_interval);
flags = sys_le16_to_cpu(cmd->props); flags = sys_le16_to_cpu(cmd->props);
@ -1744,6 +1862,10 @@ static void le_set_per_adv_data(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_per_adv_data *cmd = (void *)buf->data; struct bt_hci_cp_le_set_per_adv_data *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_sync_ad_data_set(cmd->handle, cmd->op, cmd->len, status = ll_adv_sync_ad_data_set(cmd->handle, cmd->op, cmd->len,
cmd->data); cmd->data);
@ -1755,6 +1877,10 @@ static void le_set_per_adv_enable(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_per_adv_enable *cmd = (void *)buf->data; struct bt_hci_cp_le_set_per_adv_enable *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
status = ll_adv_sync_enable(cmd->handle, cmd->enable); status = ll_adv_sync_enable(cmd->handle, cmd->enable);
*evt = cmd_complete_status(status); *evt = cmd_complete_status(status);
@ -1773,6 +1899,10 @@ static void le_set_ext_scan_param(struct net_buf *buf, struct net_buf **evt)
uint8_t status; uint8_t status;
uint8_t phys; uint8_t phys;
if (adv_cmds_ext_check(evt)) {
return;
}
/* TODO: add parameter checks */ /* TODO: add parameter checks */
own_addr_type = cmd->own_addr_type; own_addr_type = cmd->own_addr_type;
@ -1837,6 +1967,10 @@ static void le_set_ext_scan_enable(struct net_buf *buf, struct net_buf **evt)
struct bt_hci_cp_le_set_ext_scan_enable *cmd = (void *)buf->data; struct bt_hci_cp_le_set_ext_scan_enable *cmd = (void *)buf->data;
uint8_t status; uint8_t status;
if (adv_cmds_ext_check(evt)) {
return;
}
#if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 #if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0
/* initialize duplicate filtering */ /* initialize duplicate filtering */
if (cmd->enable && cmd->filter_dup) { if (cmd->enable && cmd->filter_dup) {
@ -1867,6 +2001,11 @@ static void le_ext_create_connection(struct net_buf *buf, struct net_buf **evt)
uint8_t status; uint8_t status;
uint8_t phys; uint8_t phys;
if (adv_cmds_ext_check(NULL)) {
*evt = cmd_status(BT_HCI_ERR_CMD_DISALLOWED);
return;
}
/* TODO: add parameter checks */ /* TODO: add parameter checks */
filter_policy = cmd->filter_policy; filter_policy = cmd->filter_policy;

View file

@ -7,6 +7,10 @@
#define LL_VERSION_NUMBER BT_HCI_VERSION_5_2 #define LL_VERSION_NUMBER BT_HCI_VERSION_5_2
#define LL_ADV_CMDS_ANY 0 /* Any advertising cmd/evt allowed */
#define LL_ADV_CMDS_LEGACY 1 /* Only legacy advertising cmd/evt allowed */
#define LL_ADV_CMDS_EXT 2 /* Only extended advertising cmd/evt allowed */
int ll_init(struct k_sem *sem_rx); int ll_init(struct k_sem *sem_rx);
void ll_reset(void); void ll_reset(void);
@ -14,6 +18,9 @@ uint8_t *ll_addr_get(uint8_t addr_type, uint8_t *p_bdaddr);
uint8_t ll_addr_set(uint8_t addr_type, uint8_t const *const p_bdaddr); uint8_t ll_addr_set(uint8_t addr_type, uint8_t const *const p_bdaddr);
#if defined(CONFIG_BT_CTLR_ADV_EXT) #if defined(CONFIG_BT_CTLR_ADV_EXT)
#if defined(CONFIG_BT_HCI_RAW)
int ll_adv_cmds_set(uint8_t adv_cmds);
#endif
uint8_t ll_adv_params_set(uint8_t handle, uint16_t evt_prop, uint32_t interval, uint8_t ll_adv_params_set(uint8_t handle, uint16_t evt_prop, uint32_t interval,
uint8_t adv_type, uint8_t own_addr_type, uint8_t adv_type, uint8_t own_addr_type,
uint8_t direct_addr_type, uint8_t const *const direct_addr, uint8_t direct_addr_type, uint8_t const *const direct_addr,

View file

@ -80,6 +80,29 @@ static struct ll_adv_set ll_adv[BT_CTLR_ADV_SET];
static struct ticker_ext ll_adv_ticker_ext[BT_CTLR_ADV_SET]; static struct ticker_ext ll_adv_ticker_ext[BT_CTLR_ADV_SET];
#endif /* CONFIG_BT_TICKER_EXT */ #endif /* CONFIG_BT_TICKER_EXT */
#if defined(CONFIG_BT_HCI_RAW) && defined(CONFIG_BT_CTLR_ADV_EXT)
static uint8_t ll_adv_cmds;
int ll_adv_cmds_set(uint8_t adv_cmds)
{
if (!ll_adv_cmds) {
ll_adv_cmds = adv_cmds;
if (adv_cmds == LL_ADV_CMDS_LEGACY) {
struct ll_adv_set *adv = &ll_adv[0];
adv->is_created = 1;
}
}
if (ll_adv_cmds != adv_cmds) {
return -EINVAL;
}
return 0;
}
#endif
#if defined(CONFIG_BT_CTLR_ADV_EXT) #if defined(CONFIG_BT_CTLR_ADV_EXT)
uint8_t ll_adv_params_set(uint8_t handle, uint16_t evt_prop, uint32_t interval, uint8_t ll_adv_params_set(uint8_t handle, uint16_t evt_prop, uint32_t interval,
uint8_t adv_type, uint8_t own_addr_type, uint8_t adv_type, uint8_t own_addr_type,
@ -1219,6 +1242,11 @@ int ull_adv_reset(void)
int err; int err;
#if defined(CONFIG_BT_CTLR_ADV_EXT) #if defined(CONFIG_BT_CTLR_ADV_EXT)
#if defined(CONFIG_BT_HCI_RAW)
ll_adv_cmds = LL_ADV_CMDS_ANY;
#endif
#if defined(CONFIG_BT_CTLR_ADV_AUX_SET) #if defined(CONFIG_BT_CTLR_ADV_AUX_SET)
if (CONFIG_BT_CTLR_ADV_AUX_SET > 0) { if (CONFIG_BT_CTLR_ADV_AUX_SET > 0) {
err = ull_adv_aux_reset(); err = ull_adv_aux_reset();