Bluetooth: controller: Privacy filtering in scanner and initiator

This commit introduces controller-based privacy for both the scanner and
the initiator roles. All the features in the specification are
implemented except:

* RPA resolution for directed advertising (TargetA address)
* RPA generation for scan requests and conn ind packets

Follow-up patches will cover the 2 items of functionality still missing
from the basic implementation. Hosts not using controller-based privacy
should not be affected by this change.

Signed-off-by: Carles Cufi <carles.cufi@nordicsemi.no>
This commit is contained in:
Carles Cufi 2017-07-03 10:31:55 +02:00
commit af93255aa5
7 changed files with 208 additions and 62 deletions

View file

@ -112,8 +112,14 @@ struct scanner {
u8_t adv_addr_type:1;
u8_t init_addr_type:1;
u8_t adv_addr[BDADDR_SIZE];
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
u8_t rpa_gen:1;
/* initiator only */
u8_t rl_idx;
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
u8_t init_addr[BDADDR_SIZE];
u8_t adv_addr[BDADDR_SIZE];
u32_t ticks_window;
u16_t conn_interval;
@ -726,15 +732,16 @@ static inline bool isr_adv_sr_check(struct pdu_adv *adv, struct pdu_adv *sr,
{
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
return ((((_radio.advertiser.filter_policy & 0x01) == 0) &&
ctrl_rl_allowed(sr->tx_addr,
sr->payload.scan_req.scan_addr, rl_idx)) ||
ctrl_rl_addr_allowed(sr->tx_addr,
sr->payload.scan_req.scan_addr,
rl_idx)) ||
(devmatch_ok) || (ctrl_irk_whitelisted(*rl_idx))) &&
isr_adv_sr_adva_check(adv, sr);
#else
return (((_radio.advertiser.filter_policy & 0x01) == 0) ||
(devmatch_ok)) &&
isr_adv_sr_adva_check(adv, sr);
#endif
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
}
static inline bool isr_adv_ci_tgta_check(struct pdu_adv *adv, struct pdu_adv *ci,
@ -744,7 +751,7 @@ static inline bool isr_adv_ci_tgta_check(struct pdu_adv *adv, struct pdu_adv *ci
if (rl_idx != FILTER_IDX_NONE) {
return rl_idx == _radio.advertiser.rl_idx;
}
#endif
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
return (adv->rx_addr == ci->tx_addr) &&
!memcmp(adv->payload.direct_ind.tgt_addr,
ci->payload.connect_ind.init_addr, BDADDR_SIZE);
@ -767,8 +774,9 @@ static inline bool isr_adv_ci_check(struct pdu_adv *adv, struct pdu_adv *ci,
/* LL 4.3.2: filter policy shall be ignored for directed adv */
if (adv->type == PDU_ADV_TYPE_DIRECT_IND) {
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
return ctrl_rl_allowed(ci->tx_addr,
ci->payload.connect_ind.init_addr, rl_idx) &&
return ctrl_rl_addr_allowed(ci->tx_addr,
ci->payload.connect_ind.init_addr,
rl_idx) &&
#else
return (1) &&
#endif
@ -778,15 +786,16 @@ static inline bool isr_adv_ci_check(struct pdu_adv *adv, struct pdu_adv *ci,
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
return ((((_radio.advertiser.filter_policy & 0x02) == 0) &&
ctrl_rl_allowed(ci->tx_addr,
ci->payload.connect_ind.init_addr, rl_idx)) ||
ctrl_rl_addr_allowed(ci->tx_addr,
ci->payload.connect_ind.init_addr,
rl_idx)) ||
(devmatch_ok) || (ctrl_irk_whitelisted(*rl_idx))) &&
isr_adv_ci_adva_check(adv, ci);
#else
return (((_radio.advertiser.filter_policy & 0x02) == 0) ||
(devmatch_ok)) &&
isr_adv_ci_adva_check(adv, ci);
#endif
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
}
static inline u32_t isr_rx_adv(u8_t devmatch_ok, u8_t devmatch_id,
@ -797,7 +806,8 @@ static inline u32_t isr_rx_adv(u8_t devmatch_ok, u8_t devmatch_id,
struct radio_pdu_node_rx *radio_pdu_node_rx;
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
/* An IRK match implies address resolution enabled */
u8_t rl_idx = irkmatch_ok ? ctrl_rl_idx(irkmatch_id) : FILTER_IDX_NONE;
u8_t rl_idx = irkmatch_ok ? ctrl_rl_irk_idx(irkmatch_id) :
FILTER_IDX_NONE;
#else
u8_t rl_idx = FILTER_IDX_NONE;
#endif
@ -1097,7 +1107,71 @@ static u32_t isr_rx_scan_report(u8_t rssi_ready)
return 0;
}
static inline u32_t isr_rx_scan(u8_t irkmatch_id, u8_t rssi_ready)
static inline bool isr_rx_scan_check(u8_t irkmatch_ok, u8_t devmatch_ok,
u8_t rl_idx)
{
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
return ((((_radio.scanner.filter_policy & 0x01) == 0) &&
(!devmatch_ok || ctrl_rl_idx_allowed(irkmatch_ok, rl_idx))) ||
devmatch_ok || ctrl_irk_whitelisted(rl_idx));
#else
return ((_radio.scanner.filter_policy & 0x01) == 0) ||
devmatch_ok;
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
}
static inline bool isr_scan_init_adva_check(struct pdu_adv *pdu,
u8_t rl_idx)
{
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
/* Only applies to initiator with no whitelist */
if (rl_idx != FILTER_IDX_NONE) {
return (rl_idx == _radio.scanner.rl_idx);
}
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
return ((_radio.scanner.adv_addr_type == pdu->tx_addr) &&
(memcmp(&_radio.scanner.adv_addr[0],
&pdu->payload.adv_ind.addr[0], BDADDR_SIZE) == 0));
}
static inline bool isr_scan_tgta_check(bool init, struct pdu_adv *pdu,
u8_t rl_idx)
{
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
if (ctrl_rl_addr_resolve(pdu->rx_addr,
pdu->payload.direct_ind.tgt_addr, rl_idx)) {
return true;
}
return (((!init || _radio.scanner.rl_idx == FILTER_IDX_NONE) &&
#else
return (((1) &&
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
((_radio.scanner.init_addr_type == pdu->rx_addr) &&
(memcmp(&_radio.scanner.init_addr[0],
&pdu->payload.direct_ind.tgt_addr[0],
BDADDR_SIZE) == 0))) ||
/* allow directed adv packets where TargetA address
* is resolvable private address (scanner only)
*/
(((_radio.scanner.filter_policy & 0x02) != 0) &&
(pdu->rx_addr != 0) &&
((pdu->payload.direct_ind.tgt_addr[5] & 0xc0) ==
0x40)));
}
static inline bool isr_scan_init_check(struct pdu_adv *pdu, u8_t rl_idx)
{
return ((((_radio.scanner.filter_policy & 0x01) != 0) ||
isr_scan_init_adva_check(pdu, rl_idx)) &&
((pdu->type == PDU_ADV_TYPE_ADV_IND) ||
((pdu->type == PDU_ADV_TYPE_DIRECT_IND) &&
(/* allow directed adv packets addressed to this device */
isr_scan_tgta_check(true, pdu, rl_idx)))));
}
static inline u32_t isr_rx_scan(u8_t devmatch_ok, u8_t devmatch_id,
u8_t irkmatch_id, u8_t rl_idx, u8_t rssi_ready)
{
struct pdu_adv *pdu_adv_rx;
@ -1107,25 +1181,7 @@ static inline u32_t isr_rx_scan(u8_t irkmatch_id, u8_t rssi_ready)
/* Initiator */
if ((_radio.scanner.conn) && ((_radio.fc_ena == 0) ||
(_radio.fc_req == _radio.fc_ack)) &&
(((pdu_adv_rx->type == PDU_ADV_TYPE_ADV_IND) &&
(((_radio.scanner.filter_policy & 0x01) != 0) ||
((_radio.scanner.adv_addr_type == pdu_adv_rx->tx_addr) &&
(memcmp(&_radio.scanner.adv_addr[0],
&pdu_adv_rx->payload.adv_ind.addr[0],
BDADDR_SIZE) == 0)))) ||
((pdu_adv_rx->type == PDU_ADV_TYPE_DIRECT_IND) &&
(/* allow directed adv packets addressed to this device */
((_radio.scanner.init_addr_type == pdu_adv_rx->rx_addr) &&
(memcmp(&_radio.scanner.init_addr[0],
&pdu_adv_rx->payload.direct_ind.tgt_addr[0],
BDADDR_SIZE) == 0)) ||
/* allow directed adv packets where initiator address
* is resolvable private address
*/
(((_radio.scanner.filter_policy & 0x02) != 0) &&
(pdu_adv_rx->rx_addr != 0) &&
((pdu_adv_rx->payload.direct_ind.tgt_addr[5] & 0xc0) ==
0x40))))) &&
isr_scan_init_check(pdu_adv_rx, rl_idx) &&
((radio_tmr_end_get() + 502) <
(TICKER_TICKS_TO_US(_radio.scanner.hdr.ticks_slot) -
RADIO_TICKER_START_PART_US))) {
@ -1166,11 +1222,19 @@ static inline u32_t isr_rx_scan(u8_t irkmatch_id, u8_t rssi_ready)
pdu_adv_tx->chan_sel = 0;
}
pdu_adv_tx->tx_addr = _radio.scanner.init_addr_type;
pdu_adv_tx->rx_addr = pdu_adv_rx->tx_addr;
pdu_adv_tx->len = sizeof(struct pdu_adv_payload_connect_ind);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
/*@todo: obtain RPA based on rl_idx if rl_idx != NONE and
* own address > 0x01 */
pdu_adv_tx->tx_addr = _radio.scanner.init_addr_type;
memcpy(&pdu_adv_tx->payload.connect_ind.init_addr[0],
&_radio.scanner.init_addr[0], BDADDR_SIZE);
#else
pdu_adv_tx->tx_addr = _radio.scanner.init_addr_type;
memcpy(&pdu_adv_tx->payload.connect_ind.init_addr[0],
&_radio.scanner.init_addr[0], BDADDR_SIZE);
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
memcpy(&pdu_adv_tx->payload.connect_ind.adv_addr[0],
&pdu_adv_rx->payload.adv_ind.addr[0], BDADDR_SIZE);
memcpy(&pdu_adv_tx->payload.connect_ind.lldata.
@ -1370,11 +1434,19 @@ static inline u32_t isr_rx_scan(u8_t irkmatch_id, u8_t rssi_ready)
/* prepare the scan request packet */
pdu_adv_tx = (struct pdu_adv *)radio_pkt_scratch_get();
pdu_adv_tx->type = PDU_ADV_TYPE_SCAN_REQ;
pdu_adv_tx->tx_addr = _radio.scanner.init_addr_type;
pdu_adv_tx->rx_addr = pdu_adv_rx->tx_addr;
pdu_adv_tx->len = sizeof(struct pdu_adv_payload_scan_req);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
/*@todo: obtain RPA based on rl_idx if rl_idx != NONE and
* own address > 0x01 */
pdu_adv_tx->tx_addr = _radio.scanner.init_addr_type;
memcpy(&pdu_adv_tx->payload.scan_req.scan_addr[0],
&_radio.scanner.init_addr[0], BDADDR_SIZE);
#else
pdu_adv_tx->tx_addr = _radio.scanner.init_addr_type;
memcpy(&pdu_adv_tx->payload.scan_req.scan_addr[0],
&_radio.scanner.init_addr[0], BDADDR_SIZE);
#endif
memcpy(&pdu_adv_tx->payload.scan_req.adv_addr[0],
&pdu_adv_rx->payload.adv_ind.addr[0], BDADDR_SIZE);
@ -1393,16 +1465,7 @@ static inline u32_t isr_rx_scan(u8_t irkmatch_id, u8_t rssi_ready)
else if (((pdu_adv_rx->type == PDU_ADV_TYPE_ADV_IND) ||
((pdu_adv_rx->type == PDU_ADV_TYPE_DIRECT_IND) &&
(/* allow directed adv packets addressed to this device */
((_radio.scanner.init_addr_type == pdu_adv_rx->rx_addr) &&
(memcmp(&_radio.scanner.init_addr[0],
&pdu_adv_rx->payload.direct_ind.tgt_addr[0],
BDADDR_SIZE) == 0)) ||
/* allow directed adv packets where initiator address
* is resolvable private address
*/
(((_radio.scanner.filter_policy & 0x02) != 0) &&
(pdu_adv_rx->rx_addr != 0) &&
((pdu_adv_rx->payload.direct_ind.tgt_addr[5] & 0xc0) == 0x40)))) ||
isr_scan_tgta_check(false, pdu_adv_rx, rl_idx))) ||
(pdu_adv_rx->type == PDU_ADV_TYPE_NONCONN_IND) ||
(pdu_adv_rx->type == PDU_ADV_TYPE_SCAN_IND) ||
#if defined(CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT)
@ -2997,6 +3060,7 @@ static inline void isr_radio_state_rx(u8_t trx_done, u8_t crc_ok,
u8_t rssi_ready)
{
u32_t err;
u8_t rl_idx;
if (!((trx_done) || ((SILENT_CONNECTION) &&
(_radio.role == ROLE_SLAVE)))) {
@ -3021,10 +3085,19 @@ static inline void isr_radio_state_rx(u8_t trx_done, u8_t crc_ok,
break;
case ROLE_SCAN:
if ((crc_ok) &&
(((_radio.scanner.filter_policy & 0x01) == 0) ||
(devmatch_ok) || (irkmatch_ok))) {
err = isr_rx_scan(irkmatch_id, rssi_ready);
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
rl_idx = devmatch_ok ?
ctrl_rl_idx(!!(_radio.scanner.filter_policy & 0x01),
devmatch_id) :
irkmatch_ok ? ctrl_rl_irk_idx(irkmatch_id) :
FILTER_IDX_NONE;
#else
rl_idx = FILTER_IDX_NONE;
#endif
if (crc_ok &&
isr_rx_scan_check(irkmatch_ok, devmatch_ok, rl_idx)) {
err = isr_rx_scan(devmatch_ok, devmatch_id, irkmatch_id,
rl_idx, rssi_ready);
} else {
err = 1;
}
@ -8768,7 +8841,8 @@ u32_t radio_adv_filter_pol_get(void)
}
u32_t radio_scan_enable(u8_t type, u8_t init_addr_type, u8_t *init_addr,
u16_t interval, u16_t window, u8_t filter_policy)
u16_t interval, u16_t window, u8_t filter_policy,
u8_t rpa_gen, u8_t rl_idx)
{
u32_t volatile ret_cb = TICKER_STATUS_BUSY;
u32_t ticks_slot_offset;
@ -8787,6 +8861,11 @@ u32_t radio_scan_enable(u8_t type, u8_t init_addr_type, u8_t *init_addr,
_radio.scanner.phy = type >> 1;
#endif /* CONFIG_BLUETOOTH_CONTROLLER_ADV_EXT */
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
_radio.scanner.rpa_gen = rpa_gen;
_radio.scanner.rl_idx = rl_idx;
#endif /* CONFIG_BLUETOOTH_CONTROLLER_PRIVACY */
_radio.scanner.init_addr_type = init_addr_type;
memcpy(&_radio.scanner.init_addr[0], init_addr, BDADDR_SIZE);
_radio.scanner.ticks_window =

View file

@ -340,9 +340,9 @@ u32_t radio_adv_disable(void);
u32_t radio_adv_is_enabled(void);
u32_t radio_adv_filter_pol_get(void);
/* Downstream - Scanner */
u32_t radio_scan_enable(u8_t type, u8_t init_addr_type,
u8_t *init_addr, u16_t interval,
u16_t window, u8_t filter_policy);
u32_t radio_scan_enable(u8_t type, u8_t init_addr_type, u8_t *init_addr,
u16_t interval, u16_t window, u8_t filter_policy,
u8_t rpa_gen, u8_t rl_idx);
u32_t radio_scan_disable(void);
u32_t radio_scan_is_enabled(void);
u32_t radio_scan_filter_pol_get(void);

View file

@ -337,7 +337,7 @@ u32_t ll_adv_enable(u8_t enable)
{
struct radio_adv_data *radio_scan_data;
struct radio_adv_data *radio_adv_data;
int rl_idx = FILTER_IDX_NONE;
u8_t rl_idx = FILTER_IDX_NONE;
struct pdu_adv *pdu_scan;
struct pdu_adv *pdu_adv;
u32_t status;

View file

@ -221,7 +221,24 @@ u8_t *ctrl_irks_get(u8_t *count)
return (u8_t *)peer_irks;
}
u8_t ctrl_rl_idx(u8_t irkmatch_id)
u8_t ctrl_rl_idx(bool whitelist, u8_t devmatch_id)
{
u8_t i;
if (whitelist) {
LL_ASSERT(devmatch_id < ARRAY_SIZE(wl));
LL_ASSERT(wl[devmatch_id].taken);
i = wl[devmatch_id].rl_idx;
} else {
LL_ASSERT(devmatch_id < ARRAY_SIZE(rl));
i = devmatch_id;
LL_ASSERT(rl[i].taken);
}
return i;
}
u8_t ctrl_rl_irk_idx(u8_t irkmatch_id)
{
u8_t i;
@ -349,7 +366,7 @@ static void filter_rl_update(void)
filter_clear(&rl_filter);
for (i = 0; i < CONFIG_BLUETOOTH_CONTROLLER_RL_SIZE; i++) {
if (rl[i].taken && (!rl[i].pirk || rl[i].dev)) {
if (rl[i].taken) {
filter_insert(&rl_filter, i, rl[i].id_addr_type,
rl[i].id_addr.val);
}
@ -403,11 +420,28 @@ u8_t ll_rl_find(u8_t id_addr_type, u8_t *id_addr, u8_t *free)
return FILTER_IDX_NONE;
}
bool ctrl_rl_allowed(u8_t id_addr_type, u8_t *id_addr, u8_t *rl_idx)
bool ctrl_rl_idx_allowed(u8_t irkmatch_ok, u8_t rl_idx)
{
/* If AR is disabled or we don't know the device or we matched an IRK
* then we're all set.
*/
if (!rl_enable || rl_idx >= ARRAY_SIZE(rl) || irkmatch_ok) {
return true;
}
LL_ASSERT(rl_idx < CONFIG_BLUETOOTH_CONTROLLER_RL_SIZE);
LL_ASSERT(rl[rl_idx].taken);
return !rl[rl_idx].pirk || rl[rl_idx].dev;
}
bool ctrl_rl_addr_allowed(u8_t id_addr_type, u8_t *id_addr, u8_t *rl_idx)
{
int i, j;
/* If AR is disabled or we matched an IRK then we're all set */
/* If AR is disabled or we matched an IRK then we're all set. No hw
* filters are used in this case.
*/
if (!rl_enable || *rl_idx != FILTER_IDX_NONE) {
return true;
}
@ -431,6 +465,20 @@ bool ctrl_rl_allowed(u8_t id_addr_type, u8_t *id_addr, u8_t *rl_idx)
return true;
}
bool ctrl_rl_addr_resolve(u8_t id_addr_type, u8_t *id_addr, u8_t rl_idx)
{
/* Unable to resolve if AR is disabled, no RL entry or no local IRK */
if (!rl_enable || rl_idx >= ARRAY_SIZE(rl) || !rl[rl_idx].lirk) {
return false;
}
if ((id_addr_type != 0) && ((id_addr[5] & 0xc0) == 0x40)) {
/*@todo: resolve address */
}
return false;
}
bool ctrl_rl_enabled(void)
{
return rl_enable;

View file

@ -20,12 +20,15 @@ void ll_filters_scan_update(u8_t scan_fp);
struct ll_filter *ctrl_filter_get(bool whitelist);
u8_t *ctrl_irks_get(u8_t *count);
u8_t ctrl_rl_idx(u8_t irkmatch_id);
u8_t ctrl_rl_idx(bool whitelist, u8_t devmatch_id);
u8_t ctrl_rl_irk_idx(u8_t irkmatch_id);
bool ctrl_irk_whitelisted(u8_t rl_idx);
bool ctrl_rl_enabled(void);
void ll_rl_rpa_update(bool timeout);
u8_t ll_rl_find(u8_t id_addr_type, u8_t *id_addr, u8_t *free);
bool ctrl_rl_allowed(u8_t id_addr_type, u8_t *id_addr, u8_t *rl_idx);
bool ctrl_rl_addr_allowed(u8_t id_addr_type, u8_t *id_addr, u8_t *rl_idx);
bool ctrl_rl_addr_resolve(u8_t id_addr_type, u8_t *id_addr, u8_t rl_idx);
bool ctrl_rl_idx_allowed(u8_t irkmatch_ok, u8_t rl_idx);
void ll_rl_pdu_adv_update(int idx, struct pdu_adv *pdu);

View file

@ -22,6 +22,8 @@ u32_t ll_create_connection(u16_t scan_interval, u16_t scan_window,
u16_t timeout)
{
u32_t status;
u8_t rpa_gen = 0;
u8_t rl_idx = FILTER_IDX_NONE;
if (radio_scan_is_enabled()) {
return BT_HCI_ERR_CMD_DISALLOWED;
@ -36,13 +38,23 @@ u32_t ll_create_connection(u16_t scan_interval, u16_t scan_window,
#if defined(CONFIG_BLUETOOTH_CONTROLLER_PRIVACY)
ll_filters_scan_update(filter_policy);
if (!filter_policy && ctrl_rl_enabled()) {
/* Look up the resolving list */
rl_idx = ll_rl_find(peer_addr_type, peer_addr, NULL);
}
if (own_addr_type == BT_ADDR_LE_PUBLIC_ID ||
own_addr_type == BT_ADDR_LE_RANDOM_ID) {
/* Generate RPAs if required */
ll_rl_rpa_update(false);
own_addr_type &= 0x1;
rpa_gen = 1;
}
#endif
return radio_scan_enable(0, own_addr_type,
ll_addr_get(own_addr_type, NULL),
scan_interval, scan_window, filter_policy);
scan_interval, scan_window,
filter_policy, rpa_gen, rl_idx);
}

View file

@ -60,6 +60,7 @@ u32_t ll_scan_params_set(u8_t type, u16_t interval, u16_t window,
u32_t ll_scan_enable(u8_t enable)
{
u32_t status;
u8_t rpa_gen = 0;
if (!enable) {
return radio_scan_disable();
@ -76,12 +77,15 @@ u32_t ll_scan_enable(u8_t enable)
ll_scan.own_addr_type == BT_ADDR_LE_RANDOM_ID)) {
/* Generate RPAs if required */
ll_rl_rpa_update(false);
rpa_gen = 1;
}
#endif
status = radio_scan_enable(ll_scan.type, ll_scan.own_addr_type,
ll_addr_get(ll_scan.own_addr_type, NULL),
status = radio_scan_enable(ll_scan.type, ll_scan.own_addr_type & 0x1,
ll_addr_get(ll_scan.own_addr_type & 0x1,
NULL),
ll_scan.interval, ll_scan.window,
ll_scan.filter_policy);
ll_scan.filter_policy, rpa_gen,
FILTER_IDX_NONE);
return status;
}