Bluetooth: Mesh: Introduce measures to avoid too frequent flash writes

Both the local sequence number as well as the Replay Protection List
(RPL) are states that may potentially change very often. In order not
to wear out the flash with these updates it makes sense to try to
avoid too frequent writes.

For the local sequence number a simple solution is not to write the
number on every increment. This patch introduces a new Kconfig option
to define after how many increments the sequence number gets written.
When the stack gets initialized it automatically adds the configured
number to the last stored one, thereby guaranteeing that the node
starts off with a number that's larger than the last used one.

The RPL is more problematic, since in principle it needs to be updated
every single time that we receive and process a message. Especially
security sentitive nodes will want this stored immediately to flash.
To give some use-case dependent flexibility, this patch introduces a
new Kconfig option to specify a timeout after which the RPL gets
written to flash.

Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Johan Hedberg 2018-05-07 22:22:30 +03:00 committed by Johan Hedberg
commit be7fe55b82
5 changed files with 98 additions and 1 deletions

View file

@ -379,6 +379,42 @@ config BT_MESH_SHELL
Activate shell module that provides Bluetooth Mesh commands to
the console.
if BT_SETTINGS
config BT_MESH_SEQ_STORE_RATE
int "How often the sequence number gets updated in storage"
range 0 1000000
default 128
help
This value defines how often the local sequence number gets
updated in persistent storage (i.e. flash). E.g. a value of 100
means that the sequence number will be stored to flash on every
100th increment. If the node sends messages very frequently a
higher value makes more sense, whereas if the node sends
infrequently a value as low as 0 (update storage for every
increment) can make sense. When the stack gets initialized it
will add this number to the last stored one, so that it starts
off with a value that's guaranteed to be larger than the last
one used before power off.
config BT_MESH_RPL_STORE_TIMEOUT
int "Minimum frequency that the RPL gets updated in storage"
range 0 1000000
default 5
help
This value defines in seconds how soon the RPL gets written to
persistent storage after a change occurs. If the node receives
messages frequently it may make sense to have this set to a
large value, whereas if the RPL gets updated infrequently a
value as low as 0 (write immediately) may make sense. Note that
if the node operates a security sensitive use case, and there's
a risk of sudden power loss, it may be a security vulnerability
to set this value to anything else than 0 (a power loss before
writing to storage exposes the node to potential message
replay attacks).
endif # BT_SETTINGS
config BT_MESH_DEBUG
bool "Enable debug logs"
depends on BT_DEBUG

View file

@ -200,5 +200,9 @@ int bt_mesh_init(const struct bt_mesh_prov *prov,
bt_mesh_proxy_init();
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_settings_init();
}
return 0;
}

View file

@ -77,6 +77,9 @@ struct bt_mesh_subnet {
struct bt_mesh_rpl {
u16_t src;
bool old_iv;
#if defined(CONFIG_BT_SETTINGS)
bool store;
#endif
u32_t seq;
};

View file

@ -165,6 +165,14 @@ static int seq_set(int argc, char **argv, char *val)
bt_mesh.seq = ((u32_t)seq.val[0] | ((u32_t)seq.val[1] << 8) |
((u32_t)seq.val[2] << 16));
if (CONFIG_BT_MESH_SEQ_STORE_RATE > 0) {
/* Make sure we have a large enough sequence number. We
* subtract 1 so that the first transmission causes a write
* to the settings storage.
*/
bt_mesh.seq += CONFIG_BT_MESH_SEQ_STORE_RATE - 1;
}
BT_DBG("Sequence Number 0x%06x", bt_mesh.seq);
return 0;
@ -548,6 +556,11 @@ void bt_mesh_store_seq(void)
struct seq_val seq;
char *str;
if (CONFIG_BT_MESH_SEQ_STORE_RATE &&
(bt_mesh.seq % CONFIG_BT_MESH_SEQ_STORE_RATE)) {
return;
}
seq.val[0] = bt_mesh.seq;
seq.val[1] = bt_mesh.seq >> 8;
seq.val[2] = bt_mesh.seq >> 16;
@ -562,7 +575,7 @@ void bt_mesh_store_seq(void)
settings_save_one("bt/mesh/Seq", str);
}
void bt_mesh_store_rpl(struct bt_mesh_rpl *entry)
static void store_rpl(struct bt_mesh_rpl *entry)
{
char buf[BT_SETTINGS_SIZE(sizeof(struct rpl_val))];
struct rpl_val rpl;
@ -587,6 +600,38 @@ void bt_mesh_store_rpl(struct bt_mesh_rpl *entry)
settings_save_one(path, str);
}
#if CONFIG_BT_MESH_RPL_STORE_TIMEOUT > 0
static struct k_delayed_work rpl_store;
static void rpl_store_timeout(struct k_work *work)
{
int i;
BT_DBG("");
for (i = 0; i < ARRAY_SIZE(bt_mesh.rpl); i++) {
struct bt_mesh_rpl *rpl = &bt_mesh.rpl[i];
if (rpl->store) {
rpl->store = false;
store_rpl(rpl);
}
}
}
#endif
void bt_mesh_store_rpl(struct bt_mesh_rpl *entry)
{
#if CONFIG_BT_MESH_RPL_STORE_TIMEOUT > 0
entry->store = true;
k_delayed_work_submit(&rpl_store,
K_SECONDS(CONFIG_BT_MESH_RPL_STORE_TIMEOUT));
BT_DBG("Waiting %d seconds", CONFIG_BT_MESH_RPL_STORE_TIMEOUT);
#else
store_rpl(entry);
#endif
}
void bt_mesh_store_subnet(struct bt_mesh_subnet *sub)
{
char buf[BT_SETTINGS_SIZE(sizeof(struct net_key_val))];
@ -637,3 +682,10 @@ void bt_mesh_store_app_key(struct bt_mesh_app_key *app)
BT_DBG("Saving AppKey %s as value %s", path, str);
settings_save_one(path, str);
}
void bt_mesh_settings_init(void)
{
#if CONFIG_BT_MESH_RPL_STORE_TIMEOUT > 0
k_delayed_work_init(&rpl_store, rpl_store_timeout);
#endif
}

View file

@ -10,3 +10,5 @@ void bt_mesh_store_seq(void);
void bt_mesh_store_rpl(struct bt_mesh_rpl *rpl);
void bt_mesh_store_subnet(struct bt_mesh_subnet *sub);
void bt_mesh_store_app_key(struct bt_mesh_app_key *key);
void bt_mesh_settings_init(void);