diff --git a/nimble/host/include/host/ble_hs.h b/nimble/host/include/host/ble_hs.h index 792f56631..fc7626683 100644 --- a/nimble/host/include/host/ble_hs.h +++ b/nimble/host/include/host/ble_hs.h @@ -72,6 +72,7 @@ struct os_event; #define BLE_HS_EENCRYPT 25 #define BLE_HS_EENCRYPT_KEY_SZ 26 #define BLE_HS_ESTORE_CAP 27 +#define BLE_HS_ESTORE_FAIL 28 #define BLE_HS_ERR_ATT_BASE 0x100 /* 256 */ #define BLE_HS_ATT_ERR(x) ((x) ? BLE_HS_ERR_ATT_BASE + (x) : 0) diff --git a/nimble/host/store/config/include/store/config/ble_store_config.h b/nimble/host/store/config/include/store/config/ble_store_config.h new file mode 100644 index 000000000..1b3f8e6ce --- /dev/null +++ b/nimble/host/store/config/include/store/config/ble_store_config.h @@ -0,0 +1,39 @@ +/* + * 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. + */ + +#ifndef H_BLE_STORE_CONFIG_ +#define H_BLE_STORE_CONFIG_ + +#ifdef __cplusplus +extern "C" { +#endif + +union ble_store_key; +union ble_store_value; + +int ble_store_config_read(int obj_type, const union ble_store_key *key, + union ble_store_value *value); +int ble_store_config_write(int obj_type, const union ble_store_value *val); +int ble_store_config_delete(int obj_type, const union ble_store_key *key); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/nimble/host/store/config/pkg.yml b/nimble/host/store/config/pkg.yml new file mode 100644 index 000000000..1ba363a1f --- /dev/null +++ b/nimble/host/store/config/pkg.yml @@ -0,0 +1,38 @@ +# +# 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. +# + +pkg.name: net/nimble/host/store/config +pkg.description: sys/config-based persistence layer for the NimBLE host. +pkg.author: "Apache Mynewt " +pkg.homepage: "http://mynewt.apache.org/" +pkg.keywords: + - ble + - bluetooth + - nimble + - persistence + +pkg.deps: + - encoding/base64 + - net/nimble/host + +pkg.deps.BLE_STORE_CONFIG_PERSIST: + - sys/config + +pkg.init: + ble_store_config_init: 500 diff --git a/nimble/host/store/config/src/ble_store_config.c b/nimble/host/store/config/src/ble_store_config.c new file mode 100644 index 000000000..ce60d1fb7 --- /dev/null +++ b/nimble/host/store/config/src/ble_store_config.c @@ -0,0 +1,533 @@ +/* + * 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 "sysinit/sysinit.h" +#include "syscfg/syscfg.h" +#include "host/ble_hs.h" +#include "config/config.h" +#include "base64/base64.h" +#include "store/config/ble_store_config.h" +#include "ble_store_config_priv.h" + +struct ble_store_value_sec + ble_store_config_our_secs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; +int ble_store_config_num_our_secs; + +struct ble_store_value_sec + ble_store_config_peer_secs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; +int ble_store_config_num_peer_secs; + +struct ble_store_value_cccd + ble_store_config_cccds[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)]; +int ble_store_config_num_cccds; + +/***************************************************************************** + * $sec * + *****************************************************************************/ + +static void +ble_store_config_print_value_sec(const struct ble_store_value_sec *sec) +{ + if (sec->ltk_present) { + BLE_HS_LOG(DEBUG, "ediv=%u rand=%llu authenticated=%d ltk=", + sec->ediv, sec->rand_num, sec->authenticated); + ble_hs_log_flat_buf(sec->ltk, 16); + BLE_HS_LOG(DEBUG, " "); + } + if (sec->irk_present) { + BLE_HS_LOG(DEBUG, "irk="); + ble_hs_log_flat_buf(sec->irk, 16); + BLE_HS_LOG(DEBUG, " "); + } + if (sec->csrk_present) { + BLE_HS_LOG(DEBUG, "csrk="); + ble_hs_log_flat_buf(sec->csrk, 16); + BLE_HS_LOG(DEBUG, " "); + } + + BLE_HS_LOG(DEBUG, "\n"); +} + +static void +ble_store_config_print_key_sec(const struct ble_store_key_sec *key_sec) +{ + if (ble_addr_cmp(&key_sec->peer_addr, BLE_ADDR_ANY)) { + BLE_HS_LOG(DEBUG, "peer_addr_type=%d peer_addr=", + key_sec->peer_addr.type); + ble_hs_log_flat_buf(key_sec->peer_addr.val, 6); + BLE_HS_LOG(DEBUG, " "); + } + if (key_sec->ediv_rand_present) { + BLE_HS_LOG(DEBUG, "ediv=0x%02x rand=0x%llx ", + key_sec->ediv, key_sec->rand_num); + } +} + +static int +ble_store_config_find_sec(const struct ble_store_key_sec *key_sec, + const struct ble_store_value_sec *value_secs, + int num_value_secs) +{ + const struct ble_store_value_sec *cur; + int skipped; + int i; + + skipped = 0; + + for (i = 0; i < num_value_secs; i++) { + cur = value_secs + i; + + if (ble_addr_cmp(&key_sec->peer_addr, BLE_ADDR_ANY)) { + if (ble_addr_cmp(&cur->peer_addr, &key_sec->peer_addr)) { + continue; + } + } + + if (key_sec->ediv_rand_present) { + if (cur->ediv != key_sec->ediv) { + continue; + } + + if (cur->rand_num != key_sec->rand_num) { + continue; + } + } + + if (key_sec->idx > skipped) { + skipped++; + continue; + } + + return i; + } + + return -1; +} + +static int +ble_store_config_read_our_sec(const struct ble_store_key_sec *key_sec, + struct ble_store_value_sec *value_sec) +{ + int idx; + + idx = ble_store_config_find_sec(key_sec, ble_store_config_our_secs, + ble_store_config_num_our_secs); + if (idx == -1) { + return BLE_HS_ENOENT; + } + + *value_sec = ble_store_config_our_secs[idx]; + return 0; +} + + +static int +ble_store_config_write_our_sec(const struct ble_store_value_sec *value_sec) +{ + struct ble_store_key_sec key_sec; + int idx; + int rc; + + BLE_HS_LOG(DEBUG, "persisting our sec; "); + ble_store_config_print_value_sec(value_sec); + + ble_store_key_from_value_sec(&key_sec, value_sec); + idx = ble_store_config_find_sec(&key_sec, ble_store_config_our_secs, + ble_store_config_num_our_secs); + if (idx == -1) { + if (ble_store_config_num_our_secs >= MYNEWT_VAL(BLE_STORE_MAX_BONDS)) { + BLE_HS_LOG(DEBUG, "error persisting our sec; too many entries " + "(%d)\n", ble_store_config_num_our_secs); + return BLE_HS_ESTORE_CAP; + } + + idx = ble_store_config_num_our_secs; + ble_store_config_num_our_secs++; + } + + ble_store_config_our_secs[idx] = *value_sec; + + rc = ble_store_config_persist_our_secs(); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_store_config_delete_obj(void *values, int value_size, int idx, + int *num_values) +{ + uint8_t *dst; + uint8_t *src; + int move_count; + + (*num_values)--; + if (idx < *num_values) { + dst = values; + dst += idx * value_size; + src = dst + value_size; + + move_count = *num_values - idx; + memmove(dst, src, move_count * value_size); + } + + return 0; +} + +static int +ble_store_config_delete_sec(const struct ble_store_key_sec *key_sec, + struct ble_store_value_sec *value_secs, + int *num_value_secs) +{ + int idx; + int rc; + + idx = ble_store_config_find_sec(key_sec, value_secs, *num_value_secs); + if (idx == -1) { + return BLE_HS_ENOENT; + } + + rc = ble_store_config_delete_obj(value_secs, sizeof *value_secs, idx, + num_value_secs); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_store_config_delete_our_sec(const struct ble_store_key_sec *key_sec) +{ + int rc; + + rc = ble_store_config_delete_sec(key_sec, ble_store_config_our_secs, + &ble_store_config_num_our_secs); + if (rc != 0) { + return rc; + } + + rc = ble_store_config_persist_our_secs(); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_store_config_delete_peer_sec(const struct ble_store_key_sec *key_sec) +{ + int rc; + + rc = ble_store_config_delete_sec(key_sec, ble_store_config_peer_secs, + &ble_store_config_num_peer_secs); + if (rc != 0) { + return rc; + } + + rc = ble_store_config_persist_peer_secs(); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_store_config_read_peer_sec(const struct ble_store_key_sec *key_sec, + struct ble_store_value_sec *value_sec) +{ + int idx; + + idx = ble_store_config_find_sec(key_sec, ble_store_config_peer_secs, + ble_store_config_num_peer_secs); + if (idx == -1) { + return BLE_HS_ENOENT; + } + + *value_sec = ble_store_config_peer_secs[idx]; + return 0; +} + +static int +ble_store_config_write_peer_sec(const struct ble_store_value_sec *value_sec) +{ + struct ble_store_key_sec key_sec; + int idx; + int rc; + + BLE_HS_LOG(DEBUG, "persisting peer sec; "); + ble_store_config_print_value_sec(value_sec); + + ble_store_key_from_value_sec(&key_sec, value_sec); + idx = ble_store_config_find_sec(&key_sec, ble_store_config_peer_secs, + ble_store_config_num_peer_secs); + if (idx == -1) { + if (ble_store_config_num_peer_secs >= MYNEWT_VAL(BLE_STORE_MAX_BONDS)) { + BLE_HS_LOG(DEBUG, "error persisting peer sec; too many entries " + "(%d)\n", ble_store_config_num_peer_secs); + return BLE_HS_ESTORE_CAP; + } + + idx = ble_store_config_num_peer_secs; + ble_store_config_num_peer_secs++; + } + + ble_store_config_peer_secs[idx] = *value_sec; + + rc = ble_store_config_persist_peer_secs(); + if (rc != 0) { + return rc; + } + + return 0; +} + +/***************************************************************************** + * $cccd * + *****************************************************************************/ + +static int +ble_store_config_find_cccd(const struct ble_store_key_cccd *key) +{ + struct ble_store_value_cccd *cccd; + int skipped; + int i; + + skipped = 0; + for (i = 0; i < ble_store_config_num_cccds; i++) { + cccd = ble_store_config_cccds + i; + + if (ble_addr_cmp(&key->peer_addr, BLE_ADDR_ANY)) { + if (ble_addr_cmp(&cccd->peer_addr, &key->peer_addr)) { + continue; + } + } + + if (key->chr_val_handle != 0) { + if (cccd->chr_val_handle != key->chr_val_handle) { + continue; + } + } + + if (key->idx > skipped) { + skipped++; + continue; + } + + return i; + } + + return -1; +} + +static int +ble_store_config_delete_cccd(const struct ble_store_key_cccd *key_cccd) +{ + int idx; + int rc; + + idx = ble_store_config_find_cccd(key_cccd); + if (idx == -1) { + return BLE_HS_ENOENT; + } + + rc = ble_store_config_delete_obj(ble_store_config_cccds, + sizeof *ble_store_config_cccds, + idx, + &ble_store_config_num_cccds); + if (rc != 0) { + return rc; + } + + rc = ble_store_config_persist_cccds(); + if (rc != 0) { + return rc; + } + + return 0; +} + +static int +ble_store_config_read_cccd(const struct ble_store_key_cccd *key_cccd, + struct ble_store_value_cccd *value_cccd) +{ + int idx; + + idx = ble_store_config_find_cccd(key_cccd); + if (idx == -1) { + return BLE_HS_ENOENT; + } + + *value_cccd = ble_store_config_cccds[idx]; + return 0; +} + +static int +ble_store_config_write_cccd(const struct ble_store_value_cccd *value_cccd) +{ + struct ble_store_key_cccd key_cccd; + int idx; + int rc; + + ble_store_key_from_value_cccd(&key_cccd, value_cccd); + idx = ble_store_config_find_cccd(&key_cccd); + if (idx == -1) { + if (ble_store_config_num_cccds >= MYNEWT_VAL(BLE_STORE_MAX_CCCDS)) { + BLE_HS_LOG(DEBUG, "error persisting cccd; too many entries (%d)\n", + ble_store_config_num_cccds); + return BLE_HS_ESTORE_CAP; + } + + idx = ble_store_config_num_cccds; + ble_store_config_num_cccds++; + } + + ble_store_config_cccds[idx] = *value_cccd; + + rc = ble_store_config_persist_cccds(); + if (rc != 0) { + return rc; + } + + return 0; +} + +/***************************************************************************** + * $api * + *****************************************************************************/ + +/** + * Searches the database for an object matching the specified criteria. + * + * @return 0 if a key was found; else BLE_HS_ENOENT. + */ +int +ble_store_config_read(int obj_type, const union ble_store_key *key, + union ble_store_value *value) +{ + int rc; + + switch (obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + /* An encryption procedure (bonding) is being attempted. The nimble + * stack is asking us to look in our key database for a long-term key + * corresponding to the specified ediv and random number. + * + * Perform a key lookup and populate the context object with the + * result. The nimble stack will use this key if this function returns + * success. + */ + BLE_HS_LOG(DEBUG, "looking up peer sec; "); + ble_store_config_print_key_sec(&key->sec); + BLE_HS_LOG(DEBUG, "\n"); + rc = ble_store_config_read_peer_sec(&key->sec, &value->sec); + return rc; + + case BLE_STORE_OBJ_TYPE_OUR_SEC: + BLE_HS_LOG(DEBUG, "looking up our sec; "); + ble_store_config_print_key_sec(&key->sec); + BLE_HS_LOG(DEBUG, "\n"); + rc = ble_store_config_read_our_sec(&key->sec, &value->sec); + return rc; + + case BLE_STORE_OBJ_TYPE_CCCD: + rc = ble_store_config_read_cccd(&key->cccd, &value->cccd); + return rc; + + default: + return BLE_HS_ENOTSUP; + } +} + +/** + * Adds the specified object to the database. + * + * @return 0 on success; + * BLE_HS_ESTORE_CAP if the database is full. + */ +int +ble_store_config_write(int obj_type, const union ble_store_value *val) +{ + int rc; + + switch (obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + rc = ble_store_config_write_peer_sec(&val->sec); + return rc; + + case BLE_STORE_OBJ_TYPE_OUR_SEC: + rc = ble_store_config_write_our_sec(&val->sec); + return rc; + + case BLE_STORE_OBJ_TYPE_CCCD: + rc = ble_store_config_write_cccd(&val->cccd); + return rc; + + default: + return BLE_HS_ENOTSUP; + } +} + +int +ble_store_config_delete(int obj_type, const union ble_store_key *key) +{ + int rc; + + switch (obj_type) { + case BLE_STORE_OBJ_TYPE_PEER_SEC: + rc = ble_store_config_delete_peer_sec(&key->sec); + return rc; + + case BLE_STORE_OBJ_TYPE_OUR_SEC: + rc = ble_store_config_delete_our_sec(&key->sec); + return rc; + + case BLE_STORE_OBJ_TYPE_CCCD: + rc = ble_store_config_delete_cccd(&key->cccd); + return rc; + + default: + return BLE_HS_ENOTSUP; + } +} + +void +ble_store_config_init(void) +{ + /* Ensure this function only gets called by sysinit. */ + SYSINIT_ASSERT_ACTIVE(); + + ble_hs_cfg.store_read_cb = ble_store_config_read; + ble_hs_cfg.store_write_cb = ble_store_config_write; + ble_hs_cfg.store_delete_cb = ble_store_config_delete; + + /* Re-initialize BSS values in case of unit tests. */ + ble_store_config_num_our_secs = 0; + ble_store_config_num_peer_secs = 0; + ble_store_config_num_cccds = 0; + + ble_store_config_conf_init(); +} diff --git a/nimble/host/store/config/src/ble_store_config_conf.c b/nimble/host/store/config/src/ble_store_config_conf.c new file mode 100644 index 000000000..e74127ae9 --- /dev/null +++ b/nimble/host/store/config/src/ble_store_config_conf.c @@ -0,0 +1,231 @@ +/* + * 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 "syscfg/syscfg.h" + +#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) + +#include +#include + +#include "sysinit/sysinit.h" +#include "host/ble_hs.h" +#include "config/config.h" +#include "base64/base64.h" +#include "store/config/ble_store_config.h" +#include "ble_store_config_priv.h" + +static int +ble_store_config_conf_set(int argc, char **argv, char *val); +static int +ble_store_config_conf_export(void (*func)(char *name, char *val), + enum conf_export_tgt tgt); + +static struct conf_handler ble_store_config_conf_handler = { + .ch_name = "ble_hs", + .ch_get = NULL, + .ch_set = ble_store_config_conf_set, + .ch_commit = NULL, + .ch_export = ble_store_config_conf_export +}; + +#define BLE_STORE_CONFIG_SEC_ENCODE_SZ \ + BASE64_ENCODE_SIZE(sizeof (struct ble_store_value_sec)) + +#define BLE_STORE_CONFIG_SEC_SET_ENCODE_SZ \ + (MYNEWT_VAL(BLE_STORE_MAX_BONDS) * BLE_STORE_CONFIG_SEC_ENCODE_SZ + 1) + +#define BLE_STORE_CONFIG_CCCD_ENCODE_SZ \ + BASE64_ENCODE_SIZE(sizeof (struct ble_store_value_cccd)) + +#define BLE_STORE_CONFIG_CCCD_SET_ENCODE_SZ \ + (MYNEWT_VAL(BLE_STORE_MAX_CCCDS) * BLE_STORE_CONFIG_CCCD_ENCODE_SZ + 1) + +static void +ble_store_config_serialize_arr(const void *arr, int obj_sz, int num_objs, + char *out_buf, int buf_sz) +{ + int arr_size; + + arr_size = obj_sz * num_objs; + assert(arr_size <= buf_sz); + + base64_encode(arr, arr_size, out_buf, 1); +} + +static int +ble_store_config_deserialize_arr(const char *enc, + void *out_arr, + int obj_sz, + int *out_num_objs) +{ + int len; + + len = base64_decode(enc, out_arr); + if (len < 0) { + return OS_EINVAL; + } + + *out_num_objs = len / obj_sz; + return 0; +} + +static int +ble_store_config_conf_set(int argc, char **argv, char *val) +{ + int rc; + + if (argc == 1) { + if (strcmp(argv[0], "our_sec") == 0) { + rc = ble_store_config_deserialize_arr( + val, + ble_store_config_our_secs, + sizeof *ble_store_config_our_secs, + &ble_store_config_num_our_secs); + return rc; + } else if (strcmp(argv[0], "peer_sec") == 0) { + rc = ble_store_config_deserialize_arr( + val, + ble_store_config_peer_secs, + sizeof *ble_store_config_peer_secs, + &ble_store_config_num_peer_secs); + return rc; + } else if (strcmp(argv[0], "cccd") == 0) { + rc = ble_store_config_deserialize_arr( + val, + ble_store_config_cccds, + sizeof *ble_store_config_cccds, + &ble_store_config_num_cccds); + return rc; + } + } + return OS_ENOENT; +} + +static int +ble_store_config_conf_export(void (*func)(char *name, char *val), + enum conf_export_tgt tgt) +{ + union { + char sec[BLE_STORE_CONFIG_SEC_SET_ENCODE_SZ]; + char cccd[BLE_STORE_CONFIG_CCCD_SET_ENCODE_SZ]; + } buf; + + ble_store_config_serialize_arr(ble_store_config_our_secs, + sizeof *ble_store_config_our_secs, + ble_store_config_num_our_secs, + buf.sec, + sizeof buf.sec); + func("ble_hs/our_sec", buf.sec); + + ble_store_config_serialize_arr(ble_store_config_peer_secs, + sizeof *ble_store_config_peer_secs, + ble_store_config_num_peer_secs, + buf.sec, + sizeof buf.sec); + func("ble_hs/peer_sec", buf.sec); + + ble_store_config_serialize_arr(ble_store_config_cccds, + sizeof *ble_store_config_cccds, + ble_store_config_num_cccds, + buf.cccd, + sizeof buf.cccd); + func("ble_hs/cccd", buf.cccd); + + return 0; +} + +static int +ble_store_config_persist_sec_set(const char *setting_name, + const struct ble_store_value_sec *secs, + int num_secs) +{ + char buf[BLE_STORE_CONFIG_SEC_SET_ENCODE_SZ]; + int rc; + + ble_store_config_serialize_arr(secs, sizeof *secs, num_secs, + buf, sizeof buf); + rc = conf_save_one(setting_name, buf); + if (rc != 0) { + return BLE_HS_ESTORE_FAIL; + } + + return 0; +} + +int +ble_store_config_persist_our_secs(void) +{ + int rc; + + rc = ble_store_config_persist_sec_set("ble_hs/our_sec", + ble_store_config_our_secs, + ble_store_config_num_our_secs); + if (rc != 0) { + return rc; + } + + return 0; +} + +int +ble_store_config_persist_peer_secs(void) +{ + int rc; + + rc = ble_store_config_persist_sec_set("ble_hs/peer_sec", + ble_store_config_peer_secs, + ble_store_config_num_peer_secs); + if (rc != 0) { + return rc; + } + + return 0; +} + +int +ble_store_config_persist_cccds(void) +{ + char buf[BLE_STORE_CONFIG_CCCD_SET_ENCODE_SZ]; + int rc; + + ble_store_config_serialize_arr(ble_store_config_cccds, + sizeof *ble_store_config_cccds, + ble_store_config_num_cccds, + buf, + sizeof buf); + rc = conf_save_one("ble_hs/cccd", buf); + if (rc != 0) { + return BLE_HS_ESTORE_FAIL; + } + + return 0; +} + +void +ble_store_config_conf_init(void) +{ + int rc; + + rc = conf_register(&ble_store_config_conf_handler); + SYSINIT_PANIC_ASSERT_MSG(rc == 0, + "Failed to register ble_store_config conf"); +} + +#endif /* MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) */ diff --git a/nimble/host/store/config/src/ble_store_config_priv.h b/nimble/host/store/config/src/ble_store_config_priv.h new file mode 100644 index 000000000..bae90e976 --- /dev/null +++ b/nimble/host/store/config/src/ble_store_config_priv.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef H_BLE_STORE_CONFIG_PRIV_ +#define H_BLE_STORE_CONFIG_PRIV_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern struct ble_store_value_sec + ble_store_config_our_secs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; +extern int ble_store_config_num_our_secs; + +extern struct ble_store_value_sec + ble_store_config_peer_secs[MYNEWT_VAL(BLE_STORE_MAX_BONDS)]; +extern int ble_store_config_num_peer_secs; + +extern struct ble_store_value_cccd + ble_store_config_cccds[MYNEWT_VAL(BLE_STORE_MAX_CCCDS)]; +extern int ble_store_config_num_cccds; + +#if MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) + +int ble_store_config_persist_our_secs(void); +int ble_store_config_persist_peer_secs(void); +int ble_store_config_persist_cccds(void); +void ble_store_config_conf_init(void); + +#else + +static inline int ble_store_config_persist_our_secs(void) { return 0; } +static inline int ble_store_config_persist_peer_secs(void) { return 0; } +static inline int ble_store_config_persist_cccds(void) { return 0; } +static inline void ble_store_config_conf_init(void) { } + +#endif /* MYNEWT_VAL(BLE_STORE_CONFIG_PERSIST) */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/nimble/host/store/config/syscfg.yml b/nimble/host/store/config/syscfg.yml new file mode 100644 index 000000000..15ba29957 --- /dev/null +++ b/nimble/host/store/config/syscfg.yml @@ -0,0 +1,25 @@ +# 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. +# + +# Package: net/nimble/host + +syscfg.defs: + BLE_STORE_CONFIG_PERSIST: + description: > + Whether to save data to sys/config, or just keep it in RAM. + value: 1 diff --git a/nimble/host/store/ram/pkg.yml b/nimble/host/store/ram/pkg.yml index 276afa605..dc4fb82b8 100644 --- a/nimble/host/store/ram/pkg.yml +++ b/nimble/host/store/ram/pkg.yml @@ -18,7 +18,10 @@ # pkg.name: net/nimble/host/store/ram -pkg.description: RAM-based persistence layer for the NimBLE host. +pkg.description: > + DEPRECATED; for a RAM-only BLE store, use store/config and set + BLE_STORE_CONFIG_PERSIST to 0. RAM-based persistence layer for the NimBLE + host. pkg.author: "Apache Mynewt " pkg.homepage: "http://mynewt.apache.org/" pkg.keywords: diff --git a/nimble/host/syscfg.yml b/nimble/host/syscfg.yml index 6fe858cbf..110d87e43 100644 --- a/nimble/host/syscfg.yml +++ b/nimble/host/syscfg.yml @@ -322,10 +322,15 @@ syscfg.defs: # Store settings. BLE_STORE_MAX_BONDS: description: > - Number of bonds to persist before recycling old ones. - value: 4 + Maximum number of bonds that can be persisted. Note: increasing + this value may also require increasing the capacity of the + underlying storage mechanism. + value: 3 BLE_STORE_MAX_CCCDS: description: > - Number of client characteristic configuration descriptors to - persist before recycling old ones. - value: 16 + Maximum number of client characteristic configuration descriptors + that can be persisted. Note: increasing this value may also + require increasing the capacity of the underlying storage + mechanism. + + value: 8 diff --git a/nimble/host/test/pkg.yml b/nimble/host/test/pkg.yml index cd77a34ea..a7087a90d 100644 --- a/nimble/host/test/pkg.yml +++ b/nimble/host/test/pkg.yml @@ -25,7 +25,7 @@ pkg.keywords: pkg.deps: - test/testutil - net/nimble/host - - net/nimble/host/store/ram + - net/nimble/host/store/config pkg.deps.SELFTEST: - sys/console/stub diff --git a/nimble/host/test/src/ble_hs_test_util.c b/nimble/host/test/src/ble_hs_test_util.c index cb59f5075..fd22c7ec9 100644 --- a/nimble/host/test/src/ble_hs_test_util.c +++ b/nimble/host/test/src/ble_hs_test_util.c @@ -26,7 +26,7 @@ #include "nimble/ble_hci_trans.h" #include "host/ble_hs_adv.h" #include "host/ble_hs_id.h" -#include "store/ram/ble_store_ram.h" +#include "store/config/ble_store_config.h" #include "transport/ram/ble_hci_ram.h" #include "ble_hs_test_util.h" @@ -2328,7 +2328,7 @@ ble_hs_test_util_store_read(int obj_type, const union ble_store_key *key, ble_sm_test_store_obj_type = obj_type; ble_sm_test_store_key = *key; - rc = ble_store_ram_read(obj_type, key, value); + rc = ble_store_config_read(obj_type, key, value); ble_sm_test_store_value = *value; return rc; @@ -2341,7 +2341,7 @@ ble_hs_test_util_store_write(int obj_type, const union ble_store_value *value) ble_sm_test_store_obj_type = obj_type; - rc = ble_store_ram_write(obj_type, value); + rc = ble_store_config_write(obj_type, value); ble_sm_test_store_value = *value; return rc; @@ -2355,7 +2355,7 @@ ble_hs_test_util_store_delete(int obj_type, const union ble_store_key *key) ble_sm_test_store_obj_type = obj_type; ble_sm_test_store_key = *key; - rc = ble_store_ram_delete(obj_type, key); + rc = ble_store_config_delete(obj_type, key); return rc; } diff --git a/nimble/host/test/src/ble_sm_test_util.c b/nimble/host/test/src/ble_sm_test_util.c index c81ff6ffc..1a23fdc0b 100644 --- a/nimble/host/test/src/ble_sm_test_util.c +++ b/nimble/host/test/src/ble_sm_test_util.c @@ -26,7 +26,6 @@ #include "host/ble_sm.h" #include "host/ble_hs_test.h" #include "host/ble_hs_id.h" -#include "store/ram/ble_store_ram.h" #include "ble_hs_test_util.h" #include "ble_sm_test_util.h" diff --git a/nimble/host/test/src/ble_store_test.c b/nimble/host/test/src/ble_store_test.c index 1a73d614b..6665900ce 100644 --- a/nimble/host/test/src/ble_store_test.c +++ b/nimble/host/test/src/ble_store_test.c @@ -115,7 +115,7 @@ ble_store_test_util_overflow_sec(int is_our_sec) TEST_CASE(ble_store_test_peers) { - struct ble_store_value_sec secs[4] = { + struct ble_store_value_sec secs[3] = { { .peer_addr = { BLE_ADDR_PUBLIC, { 1, 2, 3, 4, 5, 6 } }, .ltk_present = 1, @@ -129,12 +129,8 @@ TEST_CASE(ble_store_test_peers) .peer_addr = { BLE_ADDR_PUBLIC, { 2, 3, 4, 5, 6, 7 } }, .ltk_present = 1, }, - { - .peer_addr = { BLE_ADDR_RANDOM, { 3, 4, 5, 6, 7, 8 } }, - .ltk_present = 1, - }, }; - ble_addr_t peer_addrs[4]; + ble_addr_t peer_addrs[3]; int num_addrs; int rc; int i; @@ -188,6 +184,9 @@ TEST_CASE(ble_store_test_delete_peer) .chr_val_handle = 5, }, }; + union ble_store_value value; + union ble_store_key key; + int count; int rc; int i; @@ -212,6 +211,35 @@ TEST_CASE(ble_store_test_delete_peer) /* Ensure all traces of first peer have been removed. */ ble_store_test_util_verify_peer_deleted(&secs[0].peer_addr); + /* Ensure second peer data is still intact. */ + ble_store_key_from_value_sec(&key.sec, secs + 1); + + rc = ble_store_util_count(BLE_STORE_OBJ_TYPE_OUR_SEC, &count); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT(count == 1); + + rc = ble_store_read_our_sec(&key.sec, &value.sec); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT(memcmp(&value.sec, secs + 1, sizeof value.sec) == 0); + + rc = ble_store_util_count(BLE_STORE_OBJ_TYPE_PEER_SEC, &count); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT(count == 1); + + rc = ble_store_read_peer_sec(&key.sec, &value.sec); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT(memcmp(&value.sec, secs + 1, sizeof value.sec) == 0); + + ble_store_key_from_value_cccd(&key.cccd, cccds + 2); + + rc = ble_store_util_count(BLE_STORE_OBJ_TYPE_CCCD, &count); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT(count == 1); + + rc = ble_store_read_cccd(&key.cccd, &value.cccd); + TEST_ASSERT_FATAL(rc == 0); + TEST_ASSERT(memcmp(&value.cccd, cccds + 2, sizeof value.cccd) == 0); + /* Delete second peer. */ rc = ble_store_util_delete_peer(&secs[1].peer_addr); TEST_ASSERT_FATAL(rc == 0); @@ -272,15 +300,15 @@ TEST_CASE(ble_store_test_count) /* Write some test data. */ - for (i = 0; i < 4; i++) { + for (i = 0; i < 3; i++) { rc = ble_store_write_our_sec(secs + i); TEST_ASSERT_FATAL(rc == 0); } - for (i = 0; i < 3; i++) { + for (i = 0; i < 2; i++) { rc = ble_store_write_peer_sec(secs + i); TEST_ASSERT_FATAL(rc == 0); } - for (i = 0; i < 2; i++) { + for (i = 0; i < 1; i++) { rc = ble_store_write_cccd(cccds + i); TEST_ASSERT_FATAL(rc == 0); } @@ -288,15 +316,15 @@ TEST_CASE(ble_store_test_count) /*** Verify counts after populating store. */ rc = ble_store_util_count(BLE_STORE_OBJ_TYPE_OUR_SEC, &count); TEST_ASSERT_FATAL(rc == 0); - TEST_ASSERT(count == 4); + TEST_ASSERT(count == 3); rc = ble_store_util_count(BLE_STORE_OBJ_TYPE_PEER_SEC, &count); TEST_ASSERT_FATAL(rc == 0); - TEST_ASSERT(count == 3); + TEST_ASSERT(count == 2); rc = ble_store_util_count(BLE_STORE_OBJ_TYPE_CCCD, &count); TEST_ASSERT_FATAL(rc == 0); - TEST_ASSERT(count == 2); + TEST_ASSERT(count == 1); } TEST_CASE(ble_store_test_overflow) diff --git a/nimble/host/test/syscfg.yml b/nimble/host/test/syscfg.yml index 971d78622..9cca387ad 100644 --- a/nimble/host/test/syscfg.yml +++ b/nimble/host/test/syscfg.yml @@ -28,3 +28,4 @@ syscfg.vals: BLE_SM_SC: 1 MSYS_1_BLOCK_COUNT: 100 BLE_L2CAP_COC_MAX_NUM: 1 + CONFIG_FCB: 1