blob: c959d4659c338c299adc6fe4573b741eb245ae81 [file] [log] [blame]
/*
* Copyright (c) 2023 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/ztest.h>
#include <zephyr/net/buf.h>
#include <zephyr/bluetooth/mesh.h>
#include "settings.h"
#include "net.h"
#include "rpl.h"
#define EMPTY_ENTRIES_CNT (CONFIG_BT_MESH_CRPL - ARRAY_SIZE(test_vector))
/* Used for cleaning RPL without checking it. */
static bool skip_delete;
/* Test function that should call bt_mesh_rpl_check(). */
static enum {
SETTINGS_SAVE_ONE = 1,
SETTINGS_DELETE
} settings_func;
/* Number of `settings_func` calls before calling bt_mesh_rpl_check() (1 means first call). */
static int settings_func_cnt;
/* Received message context. */
static struct bt_mesh_net_rx recv_msg;
/* We will change test vector during the test as it is convenient to do so. Therefore, we need
* to keep default values separately.
*/
static const struct test_rpl_entry {
char *name;
uint16_t src;
bool old_iv;
uint32_t seq;
} test_vector_default[] = {
{ .name = "bt/mesh/RPL/1", .src = 0x1, .old_iv = false, .seq = 10, },
{ .name = "bt/mesh/RPL/17", .src = 0x17, .old_iv = true, .seq = 32, },
{ .name = "bt/mesh/RPL/7c", .src = 0x7c, .old_iv = false, .seq = 20, },
{ .name = "bt/mesh/RPL/2c", .src = 0x2c, .old_iv = true, .seq = 5, },
{ .name = "bt/mesh/RPL/5a", .src = 0x5a, .old_iv = true, .seq = 12, },
};
static struct test_rpl_entry test_vector[ARRAY_SIZE(test_vector_default)];
/**** Helper functions ****/
static void prepare_rpl_and_start_reset(void)
{
/* Add test vector to RPL. */
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
struct bt_mesh_net_rx msg = {
.local_match = true,
.ctx.addr = test_vector[i].src,
.old_iv = test_vector[i].old_iv,
.seq = test_vector[i].seq,
};
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_RPL_PENDING);
zassert_false(bt_mesh_rpl_check(&msg, NULL));
}
/* settings_save_one() will be triggered for all new entries when
* bt_mesh_rpl_pending_store() is called.
*/
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
ztest_expect_data(settings_save_one, name, test_vector[i].name);
}
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
/* Check that all added entries are in RPL. */
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
struct bt_mesh_net_rx msg = {
.local_match = true,
.ctx.addr = test_vector[i].src,
.old_iv = test_vector[i].old_iv,
.seq = test_vector[i].seq,
};
zassert_true(bt_mesh_rpl_check(&msg, NULL));
}
/* Simulate IVI Update. This should only flip flags. The actual storing will happen
* when bt_mesh_rpl_pending_store() is called.
*/
ztest_expect_value(bt_mesh_settings_store_schedule, flag, BT_MESH_SETTINGS_RPL_PENDING);
bt_mesh_rpl_reset();
}
/* Should be called after the reset operation is finished. */
static void check_entries_from_test_vector(void)
{
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
struct bt_mesh_net_rx msg = {
.local_match = true,
.ctx.addr = test_vector[i].src,
/* Entries with old_iv == true should have been deleted. old_iv in entries
* is flipped, so to check this we can try to add the removed entries
* again. RPL should accept them.
*/
.old_iv = !test_vector[i].old_iv,
.seq = test_vector[i].seq
};
/* Removed entries can now be added again. */
if (test_vector[i].old_iv) {
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_RPL_PENDING);
zassert_false(bt_mesh_rpl_check(&msg, NULL));
} else {
zassert_true(bt_mesh_rpl_check(&msg, NULL));
}
}
}
static void check_empty_entries(int cnt)
{
/* Check that RPL has the specified amount of empty entries. */
for (int i = 0; i < cnt; i++) {
struct bt_mesh_net_rx msg = {
.local_match = true,
.ctx.addr = 0x7fff - i,
.old_iv = false,
.seq = i,
};
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_RPL_PENDING);
zassert_false(bt_mesh_rpl_check(&msg, NULL));
}
/* Check that there are no more empty entries in RPL. */
struct bt_mesh_net_rx msg = {
.local_match = true,
.ctx.addr = 0x1024,
.old_iv = false,
.seq = 1024,
};
zassert_true(bt_mesh_rpl_check(&msg, NULL));
}
static void check_op(int op)
{
if (settings_func == op) {
if (settings_func_cnt > 1) {
settings_func_cnt--;
} else {
ztest_expect_value(bt_mesh_settings_store_schedule, flag,
BT_MESH_SETTINGS_RPL_PENDING);
zassert_false(bt_mesh_rpl_check(&recv_msg, NULL));
settings_func_cnt--;
settings_func = 0;
}
}
}
static void call_rpl_check_on(int func, int cnt, struct test_rpl_entry *entry)
{
settings_func = func;
settings_func_cnt = cnt;
recv_msg.local_match = true;
recv_msg.ctx.addr = entry->src;
recv_msg.seq = entry->seq;
recv_msg.old_iv = entry->old_iv;
}
static void expect_pending_store(void)
{
/* Entries with old_iv == true should be removed, others should be stored. */
for (int i = 0; i < ARRAY_SIZE(test_vector); i++) {
if (test_vector[i].old_iv) {
ztest_expect_value(settings_delete, name, test_vector[i].name);
} else {
ztest_expect_data(settings_save_one, name, test_vector[i].name);
}
}
}
static bool is_rpl_check_called(void)
{
return settings_func_cnt == 0;
}
static void verify_rpl(void)
{
check_entries_from_test_vector();
check_empty_entries(EMPTY_ENTRIES_CNT);
}
static void setup(void *f)
{
/* Restore test vector. */
memcpy(test_vector, test_vector_default, sizeof(test_vector));
/* Clear RPL before every test. */
skip_delete = true;
ztest_expect_value(bt_mesh_settings_store_schedule, flag, BT_MESH_SETTINGS_RPL_PENDING);
bt_mesh_rpl_clear();
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
skip_delete = false;
settings_func = 0;
settings_func_cnt = 0;
}
/**** Mocked functions ****/
void bt_mesh_settings_store_schedule(enum bt_mesh_settings_flag flag)
{
ztest_check_expected_value(flag);
}
void bt_mesh_settings_store_cancel(enum bt_mesh_settings_flag flag)
{
}
int settings_save_one(const char *name, const void *value, size_t val_len)
{
ztest_check_expected_data(name, strlen(name));
check_op(SETTINGS_SAVE_ONE);
return 0;
}
int settings_delete(const char *name)
{
if (skip_delete) {
return 0;
}
ztest_check_expected_data(name, strlen(name));
check_op(SETTINGS_DELETE);
return 0;
}
/**** Tests ****/
ZTEST_SUITE(bt_mesh_rpl_reset, NULL, NULL, setup, NULL, NULL);
/** Test that entries with old_iv == true are removed after the reset operation finished. */
ZTEST(bt_mesh_rpl_reset, test_reset_normal)
{
prepare_rpl_and_start_reset();
expect_pending_store();
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
verify_rpl();
}
/** Test that RPL accepts and stores a valid entry that was just deleted. The entry should be
* stored after the reset operation is finished.
*/
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_delete_same_entry)
{
prepare_rpl_and_start_reset();
expect_pending_store();
/* Take the first entry with old_iv == true and simulate msg reception with same src
* address and correct IVI after the entry was deleted.
*/
struct test_rpl_entry *entry = &test_vector[1];
zassert_true(entry->old_iv);
entry->old_iv = false;
call_rpl_check_on(SETTINGS_DELETE, 1, entry);
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
zassert_true(is_rpl_check_called());
/* Call bt_mesh_rpl_pending_store() to store new entry. */
ztest_expect_data(settings_save_one, name, entry->name);
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
verify_rpl();
}
/** Test that RPL accepts and stores a valid entry that was just stored. The entry should be stored
* after the reset operation is finished.
*/
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_save_same_entry)
{
prepare_rpl_and_start_reset();
expect_pending_store();
/* Take the first entry with old_iv == false and simulate msg reception with same src
* address and correct IVI after the entry was stored.
*/
struct test_rpl_entry *entry = &test_vector[0];
zassert_false(entry->old_iv);
call_rpl_check_on(SETTINGS_SAVE_ONE, 1, entry);
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
zassert_true(is_rpl_check_called());
/* Call bt_mesh_rpl_pending_store() to store new entry. */
ztest_expect_data(settings_save_one, name, entry->name);
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
verify_rpl();
}
/** Test that RPL accepts and stores a valid entry that has not yet been deleted. The entry should
* be stored during the reset operation.
*/
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_delete_other_entry)
{
prepare_rpl_and_start_reset();
/* Take the non-first entry with old_iv == true and simulate msg reception with same src
* address and correct IVI before the entry is deleted.
*
* Should be done before calling ztest_expect_data() because the expectation changes.
*/
struct test_rpl_entry *entry = &test_vector[3];
zassert_true(entry->old_iv);
entry->old_iv = false;
call_rpl_check_on(SETTINGS_DELETE, 1, entry);
expect_pending_store();
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
zassert_true(is_rpl_check_called());
/* The entry should have been deleted in previous bt_mesh_rpl_pending_store() call. Another
* call should not do anything.
*/
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
verify_rpl();
}
/* Test that RPL accepts and stores a valid entry that has not yet been stored. The entry should be
* stored during the reset operation.
*/
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_save_other_entry)
{
prepare_rpl_and_start_reset();
/* Take RPL entry from test vector that has old_iv == false and is not stored yet after
* bt_mesh_reset() call and try to store it again. RPL has such entry with flipped old_iv,
* so this one can be accepted as is.
*
* Should be done before calling ztest_expect_data() because the expectation changes.
*/
struct test_rpl_entry *entry = &test_vector[2];
zassert_false(entry->old_iv);
call_rpl_check_on(SETTINGS_SAVE_ONE, 1, entry);
expect_pending_store();
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
zassert_true(is_rpl_check_called());
/* The entry should have been stored in previous bt_mesh_rpl_pending_store() call. Another
* call should not do anything.
*/
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
verify_rpl();
}
/** Test that RPL accepts and stores a valid entry that has been deleted during the reset operation.
* The entry will be added at the end of RPL, therefore it should be stored during the reset
* operation.
*/
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_delete_deleted_entry)
{
prepare_rpl_and_start_reset();
expect_pending_store();
/* Take the first entry with old_iv == true, wait until bt_mesh_rpl_pending_store() takes
* another entry after that one and simulate msg reception.
*/
struct test_rpl_entry *entry = &test_vector[1];
zassert_true(entry->old_iv);
entry->old_iv = false;
call_rpl_check_on(SETTINGS_DELETE, 2, entry);
/* The entry will be stored during the reset operation as it will be added to the end of
* the RPL.
*/
ztest_expect_data(settings_save_one, name, entry->name);
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
zassert_true(is_rpl_check_called());
/* The new entry should have been stored already. Another bt_mesh_rpl_pending_store() call
* should not do anything.
*/
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
verify_rpl();
}
/** Test that RPL accepts and stores a valid entry that has been stored during the reset operation.
* Since the entry has been already in the list, it should be stored again after the reset
* operation is finished.
*/
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_store_stored_entry)
{
prepare_rpl_and_start_reset();
expect_pending_store();
/* Take the first entry with old_iv == false, wait until bt_mesh_rpl_pending_store() takes
* another entry after that one and simulate msg reception.
*/
struct test_rpl_entry *entry = &test_vector[0];
zassert_false(entry->old_iv);
entry->old_iv = true;
entry->seq++;
call_rpl_check_on(SETTINGS_SAVE_ONE, 2, entry);
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
zassert_true(is_rpl_check_called());
/* The entry was updated after bt_mesh_rpl_pending_store() checked it. So it should be
* stored again.
*/
ztest_expect_data(settings_save_one, name, entry->name);
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
verify_rpl();
}
/** Test that RPL accepts and stores a new entry when the reset operation is not yet finished. */
ZTEST(bt_mesh_rpl_reset, test_rpl_check_on_save_new_entry)
{
prepare_rpl_and_start_reset();
expect_pending_store();
/* Add a new entry to RPL during the reset operation. */
struct test_rpl_entry entry = {
.name = "bt/mesh/RPL/2b",
.src = 43,
.old_iv = false,
.seq = 32,
};
ztest_expect_data(settings_save_one, name, entry.name);
call_rpl_check_on(SETTINGS_SAVE_ONE, 1, &entry);
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
zassert_true(is_rpl_check_called());
/* The entry should have been stored in previous bt_mesh_rpl_pending_store() call. Another
* call should not do anything.
*/
bt_mesh_rpl_pending_store(BT_MESH_ADDR_ALL_NODES);
check_entries_from_test_vector();
/* Check that added entry in the RPL. */
struct bt_mesh_net_rx msg = {
.local_match = true,
.ctx.addr = entry.src,
.old_iv = entry.old_iv,
.seq = entry.seq
};
zassert_true(bt_mesh_rpl_check(&msg, NULL));
check_empty_entries(EMPTY_ENTRIES_CNT - 1);
}