From ae987f3f3ac0f4cde04fea3df64a3ae955c80fbe Mon Sep 17 00:00:00 2001 From: Andrzej Kaczmarek Date: Tue, 30 Jun 2020 12:25:04 +0200 Subject: [PATCH] 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 --- subsys/bluetooth/controller/hci/hci.c | 139 ++++++++++++++++++++ subsys/bluetooth/controller/include/ll.h | 7 + subsys/bluetooth/controller/ll_sw/ull_adv.c | 28 ++++ 3 files changed, 174 insertions(+) diff --git a/subsys/bluetooth/controller/hci/hci.c b/subsys/bluetooth/controller/hci/hci.c index b90aa2745e4..1f98a8f51d6 100644 --- a/subsys/bluetooth/controller/hci/hci.c +++ b/subsys/bluetooth/controller/hci/hci.c @@ -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 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) static void le_conn_complete(struct pdu_data *pdu_data, uint16_t handle, 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; uint8_t status; + if (adv_cmds_legacy_check(evt)) { + return; + } + min_interval = sys_le16_to_cpu(cmd->min_interval); 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; + if (adv_cmds_legacy_check(evt)) { + return; + } + rp = hci_cmd_complete(evt, sizeof(*rp)); 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; uint8_t status; + if (adv_cmds_legacy_check(evt)) { + return; + } + #if defined(CONFIG_BT_CTLR_ADV_EXT) status = ll_adv_data_set(0, cmd->len, &cmd->data[0]); #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; uint8_t status; + if (adv_cmds_legacy_check(evt)) { + return; + } + #if defined(CONFIG_BT_CTLR_ADV_EXT) status = ll_adv_scan_rsp_set(0, cmd->len, &cmd->data[0]); #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; 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_HCI_MESH_EXT) 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; uint8_t status; + if (adv_cmds_legacy_check(evt)) { + return; + } + interval = sys_le16_to_cpu(cmd->interval); 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; uint8_t status; + if (adv_cmds_legacy_check(evt)) { + return; + } + #if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 /* initialize duplicate filtering */ 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; 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_window = sys_le16_to_cpu(cmd->scan_window); 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; uint8_t status; + if (adv_cmds_ext_check(evt)) { + return; + } + status = ll_adv_aux_random_addr_set(cmd->handle, &cmd->bdaddr.val[0]); *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_s; + if (adv_cmds_ext_check(evt)) { + return; + } + evt_prop = sys_le16_to_cpu(cmd->props); min_interval = sys_get_le24(cmd->prim_min_interval); 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; uint8_t status; + if (adv_cmds_ext_check(evt)) { + return; + } + status = ll_adv_aux_ad_data_set(cmd->handle, cmd->op, cmd->frag_pref, 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; uint8_t status; + if (adv_cmds_ext_check(evt)) { + return; + } + status = ll_adv_aux_sr_data_set(cmd->handle, cmd->op, cmd->frag_pref, 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 status; + if (adv_cmds_ext_check(evt)) { + return; + } + set_num = cmd->set_num; if (!set_num) { 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; uint16_t max_adv_data_len; + if (adv_cmds_ext_check(evt)) { + return; + } + rp = hci_cmd_complete(evt, sizeof(*rp)); 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; + if (adv_cmds_ext_check(evt)) { + return; + } + rp = hci_cmd_complete(evt, sizeof(*rp)); 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; uint8_t status; + if (adv_cmds_ext_check(evt)) { + return; + } + status = ll_adv_aux_set_remove(cmd->handle); *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; + if (adv_cmds_ext_check(evt)) { + return; + } + status = ll_adv_aux_set_clear(); *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; uint8_t status; + if (adv_cmds_ext_check(evt)) { + return; + } + interval = sys_le16_to_cpu(cmd->max_interval); 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; uint8_t status; + if (adv_cmds_ext_check(evt)) { + return; + } + status = ll_adv_sync_ad_data_set(cmd->handle, cmd->op, cmd->len, 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; uint8_t status; + if (adv_cmds_ext_check(evt)) { + return; + } + status = ll_adv_sync_enable(cmd->handle, cmd->enable); *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 phys; + if (adv_cmds_ext_check(evt)) { + return; + } + /* TODO: add parameter checks */ 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; uint8_t status; + if (adv_cmds_ext_check(evt)) { + return; + } + #if CONFIG_BT_CTLR_DUP_FILTER_LEN > 0 /* initialize duplicate filtering */ 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 phys; + if (adv_cmds_ext_check(NULL)) { + *evt = cmd_status(BT_HCI_ERR_CMD_DISALLOWED); + return; + } + /* TODO: add parameter checks */ filter_policy = cmd->filter_policy; diff --git a/subsys/bluetooth/controller/include/ll.h b/subsys/bluetooth/controller/include/ll.h index 12ef8deeae4..d692c06792d 100644 --- a/subsys/bluetooth/controller/include/ll.h +++ b/subsys/bluetooth/controller/include/ll.h @@ -7,6 +7,10 @@ #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); 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); #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 adv_type, uint8_t own_addr_type, uint8_t direct_addr_type, uint8_t const *const direct_addr, diff --git a/subsys/bluetooth/controller/ll_sw/ull_adv.c b/subsys/bluetooth/controller/ll_sw/ull_adv.c index ebf98055e76..30b97ec5a74 100644 --- a/subsys/bluetooth/controller/ll_sw/ull_adv.c +++ b/subsys/bluetooth/controller/ll_sw/ull_adv.c @@ -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]; #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) 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, @@ -1219,6 +1242,11 @@ int ull_adv_reset(void) int err; #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 (CONFIG_BT_CTLR_ADV_AUX_SET > 0) { err = ull_adv_aux_reset();