/* * 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 #include #include #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 }