/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you under the Apache License, Version 2.0 (the
|
* "License"); you may not use this file except in compliance
|
* with the License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing,
|
* software distributed under the License is distributed on an
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* KIND, either express or implied. See the License for the
|
* specific language governing permissions and limitations
|
* under the License.
|
*/
|
#include <stdint.h>
|
#include <assert.h>
|
#include <string.h>
|
#include "nimble_syscfg.h"
|
#include "os/os.h"
|
#include "nimble/ble.h"
|
#include "nimble/nimble_opt.h"
|
#include "ble/xcvr.h"
|
#include "controller/ble_ll_whitelist.h"
|
#include "controller/ble_ll_hci.h"
|
#include "controller/ble_ll_adv.h"
|
#include "controller/ble_ll_scan.h"
|
#include "controller/ble_hw.h"
|
|
#if (MYNEWT_VAL(BLE_LL_WHITELIST_SIZE) < BLE_HW_WHITE_LIST_SIZE)
|
#define BLE_LL_WHITELIST_SIZE MYNEWT_VAL(BLE_LL_WHITELIST_SIZE)
|
#else
|
#define BLE_LL_WHITELIST_SIZE BLE_HW_WHITE_LIST_SIZE
|
#endif
|
|
struct ble_ll_whitelist_entry
|
{
|
uint8_t wl_valid;
|
uint8_t wl_addr_type;
|
uint8_t wl_dev_addr[BLE_DEV_ADDR_LEN];
|
};
|
|
struct ble_ll_whitelist_entry g_ble_ll_whitelist[BLE_LL_WHITELIST_SIZE];
|
|
static int
|
ble_ll_whitelist_chg_allowed(void)
|
{
|
/*
|
* This command is not allowed if:
|
* -> advertising uses the whitelist and we are currently advertising.
|
* -> scanning uses the whitelist and is enabled.
|
* -> initiating uses whitelist and a LE create connection command is in
|
* progress
|
*/
|
#if MYNEWT_VAL(BLE_LL_ROLE_BROADCASTER)
|
if (ble_ll_adv_can_chg_whitelist()) {
|
return 1;
|
}
|
#endif
|
|
#if MYNEWT_VAL(BLE_LL_ROLE_OBSERVER)
|
if (ble_ll_scan_can_chg_whitelist()) {
|
return 1;
|
}
|
#endif
|
|
return 0;
|
}
|
|
/**
|
* Clear the whitelist.
|
*
|
* @return int 0: success, BLE error code otherwise
|
*/
|
int
|
ble_ll_whitelist_clear(void)
|
{
|
int i;
|
struct ble_ll_whitelist_entry *wl;
|
|
/* Check proper state */
|
if (!ble_ll_whitelist_chg_allowed()) {
|
return BLE_ERR_CMD_DISALLOWED;
|
}
|
|
/* Set the number of entries to 0 */
|
wl = &g_ble_ll_whitelist[0];
|
for (i = 0; i < BLE_LL_WHITELIST_SIZE; ++i) {
|
wl->wl_valid = 0;
|
++wl;
|
}
|
|
#if (BLE_USES_HW_WHITELIST == 1)
|
ble_hw_whitelist_clear();
|
#endif
|
|
return BLE_ERR_SUCCESS;
|
}
|
|
/**
|
* Read the size of the whitelist. This is the total number of whitelist
|
* entries allowed by the controller.
|
*
|
* @param rspbuf Pointer to response buffer
|
*
|
* @return int 0: success.
|
*/
|
int
|
ble_ll_whitelist_read_size(uint8_t *rspbuf, uint8_t *rsplen)
|
{
|
struct ble_hci_le_rd_white_list_rp *rsp = (void *) rspbuf;
|
|
rsp->size = BLE_LL_WHITELIST_SIZE;
|
|
*rsplen = sizeof(*rsp);
|
|
return BLE_ERR_SUCCESS;
|
}
|
|
/**
|
* Searches the whitelist to determine if the address is present in the
|
* whitelist. This is an internal API that only searches the link layer
|
* whitelist and does not care about the hardware whitelist
|
*
|
* @param addr Device or identity address to check.
|
* @param addr_type Public address (0) or random address (1)
|
*
|
* @return int 0: device is not on whitelist; otherwise the return value
|
* is the 'position' of the device in the whitelist (the index of the element
|
* plus 1).
|
*/
|
static int
|
ble_ll_whitelist_search(const uint8_t *addr, uint8_t addr_type)
|
{
|
int i;
|
struct ble_ll_whitelist_entry *wl;
|
|
wl = &g_ble_ll_whitelist[0];
|
for (i = 0; i < BLE_LL_WHITELIST_SIZE; ++i) {
|
if ((wl->wl_valid) && (wl->wl_addr_type == addr_type) &&
|
(!memcmp(&wl->wl_dev_addr[0], addr, BLE_DEV_ADDR_LEN))) {
|
return i + 1;
|
}
|
++wl;
|
}
|
|
return 0;
|
}
|
|
/**
|
* Is there a match between the device and a device on the whitelist.
|
*
|
* NOTE: This API uses the HW, if present, to determine if there was a match
|
* between a received address and an address in the whitelist. If the HW does
|
* not support whitelisting this API is the same as the whitelist search API
|
*
|
* @param addr
|
* @param addr_type Public address (0) or random address (1)
|
* @param is_ident True if addr is an identity address; false otherwise
|
*
|
* @return int
|
*/
|
int
|
ble_ll_whitelist_match(uint8_t *addr, uint8_t addr_type, int is_ident)
|
{
|
int rc;
|
#if (BLE_USES_HW_WHITELIST == 1)
|
/*
|
* XXX: This should be changed. This is HW specific: some HW may be able
|
* to both resolve a private address and perform a whitelist check. The
|
* current BLE hw cannot support this.
|
*/
|
if (is_ident) {
|
rc = ble_ll_whitelist_search(addr, addr_type);
|
} else {
|
rc = ble_hw_whitelist_match();
|
}
|
#else
|
rc = ble_ll_whitelist_search(addr, addr_type);
|
#endif
|
return rc;
|
}
|
|
/**
|
* Add a device to the whitelist
|
*
|
* @return int
|
*/
|
int
|
ble_ll_whitelist_add(const uint8_t *cmdbuf, uint8_t len)
|
{
|
const struct ble_hci_le_add_whte_list_cp *cmd = (const void *) cmdbuf;
|
struct ble_ll_whitelist_entry *wl;
|
int rc;
|
int i;
|
|
if (len != sizeof(*cmd)) {
|
return BLE_ERR_INV_HCI_CMD_PARMS;
|
}
|
|
/* Must be in proper state */
|
if (!ble_ll_whitelist_chg_allowed()) {
|
return BLE_ERR_CMD_DISALLOWED;
|
}
|
|
/* Check if we have any open entries */
|
rc = BLE_ERR_SUCCESS;
|
if (!ble_ll_whitelist_search(cmd->addr, cmd->addr_type)) {
|
wl = &g_ble_ll_whitelist[0];
|
for (i = 0; i < BLE_LL_WHITELIST_SIZE; ++i) {
|
if (wl->wl_valid == 0) {
|
memcpy(&wl->wl_dev_addr[0], cmd->addr, BLE_DEV_ADDR_LEN);
|
wl->wl_addr_type = cmd->addr_type;
|
wl->wl_valid = 1;
|
break;
|
}
|
++wl;
|
}
|
|
if (i == BLE_LL_WHITELIST_SIZE) {
|
rc = BLE_ERR_MEM_CAPACITY;
|
} else {
|
#if (BLE_USES_HW_WHITELIST == 1)
|
rc = ble_hw_whitelist_add(cmd->addr, cmd->addr_type);
|
#endif
|
}
|
}
|
|
return rc;
|
}
|
|
/**
|
* Remove a device from the whitelist
|
*
|
* @param cmdbuf
|
*
|
* @return int 0: success, BLE error code otherwise
|
*/
|
int
|
ble_ll_whitelist_rmv(const uint8_t *cmdbuf, uint8_t len)
|
{
|
const struct ble_hci_le_rmv_white_list_cp *cmd = (const void *) cmdbuf;
|
int position;
|
|
if (len != sizeof(*cmd)) {
|
return BLE_ERR_INV_HCI_CMD_PARMS;
|
}
|
|
/* Must be in proper state */
|
if (!ble_ll_whitelist_chg_allowed()) {
|
return BLE_ERR_CMD_DISALLOWED;
|
}
|
|
position = ble_ll_whitelist_search(cmd->addr, cmd->addr_type);
|
if (position) {
|
g_ble_ll_whitelist[position - 1].wl_valid = 0;
|
}
|
|
#if (BLE_USES_HW_WHITELIST == 1)
|
ble_hw_whitelist_rmv(cmd->addr, cmd->addr_type);
|
#endif
|
|
return BLE_ERR_SUCCESS;
|
}
|
|
/**
|
* Enable whitelisting.
|
*
|
* Note: This function has no effect if we are not using HW whitelisting
|
*/
|
void
|
ble_ll_whitelist_enable(void)
|
{
|
#if (BLE_USES_HW_WHITELIST == 1)
|
ble_hw_whitelist_enable();
|
#endif
|
}
|
|
/**
|
* Disable whitelisting.
|
*
|
* Note: This function has no effect if we are not using HW whitelisting
|
*/
|
void
|
ble_ll_whitelist_disable(void)
|
{
|
#if (BLE_USES_HW_WHITELIST == 1)
|
ble_hw_whitelist_disable();
|
#endif
|
}
|