blob: 838ea00b717029493347d3edc246d85cad2b5b5a [file] [log] [blame]
/*
* Copyright (c) 2021 Nordic Semiconductor
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "mesh_test.h"
#include "settings_test_backend.h"
#include "mesh/mesh.h"
#include "mesh/net.h"
#include "mesh/rpl.h"
#include "mesh/transport.h"
#define LOG_MODULE_NAME test_rpc
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME, LOG_LEVEL_INF);
#define WAIT_TIME 60 /*seconds*/
#define TEST_DATA_WAITING_TIME 5 /* seconds */
#define TEST_DATA_SIZE 20
static const struct bt_mesh_test_cfg tx_cfg = {
.addr = 0x0001,
.dev_key = { 0x01 },
};
static const struct bt_mesh_test_cfg rx_cfg = {
.addr = 0x0002,
.dev_key = { 0x02 },
};
static uint8_t test_data[TEST_DATA_SIZE];
static uint8_t rx_cnt;
static bool is_tx_succeeded;
static void test_tx_init(void)
{
bt_mesh_test_cfg_set(&tx_cfg, WAIT_TIME);
}
static void test_rx_init(void)
{
bt_mesh_test_cfg_set(&rx_cfg, WAIT_TIME);
}
static void tx_started(uint16_t dur, int err, void *data)
{
if (err) {
FAIL("Couldn't start sending (err: %d)", err);
}
LOG_INF("Sending started");
}
static void tx_ended(int err, void *data)
{
struct k_sem *sem = data;
if (err) {
is_tx_succeeded = false;
LOG_INF("Sending failed (%d)", err);
} else {
is_tx_succeeded = true;
LOG_INF("Sending succeeded");
}
k_sem_give(sem);
}
static void rx_ended(uint8_t *data, size_t len)
{
memset(test_data, rx_cnt++, sizeof(test_data));
if (memcmp(test_data, data, len)) {
FAIL("Unexpected rx data");
}
LOG_INF("Receiving succeeded");
}
static void test_tx_immediate_replay_attack(void)
{
settings_test_backend_clear();
bt_mesh_test_setup();
static const struct bt_mesh_send_cb send_cb = {
.start = tx_started,
.end = tx_ended,
};
struct k_sem sem;
k_sem_init(&sem, 0, 1);
uint32_t seq = bt_mesh.seq;
for (int i = 0; i < 3; i++) {
is_tx_succeeded = false;
memset(test_data, i, sizeof(test_data));
ASSERT_OK(bt_mesh_test_send_ra(rx_cfg.addr, test_data,
sizeof(test_data), &send_cb, &sem));
if (k_sem_take(&sem, K_SECONDS(TEST_DATA_WAITING_TIME))) {
LOG_ERR("Send timed out");
}
ASSERT_TRUE(is_tx_succeeded);
}
bt_mesh.seq = seq;
for (int i = 0; i < 3; i++) {
is_tx_succeeded = true;
memset(test_data, i, sizeof(test_data));
ASSERT_OK(bt_mesh_test_send_ra(rx_cfg.addr, test_data,
sizeof(test_data), &send_cb, &sem));
if (k_sem_take(&sem, K_SECONDS(TEST_DATA_WAITING_TIME))) {
LOG_ERR("Send timed out");
}
ASSERT_TRUE(!is_tx_succeeded);
}
PASS();
}
static void test_rx_immediate_replay_attack(void)
{
settings_test_backend_clear();
bt_mesh_test_setup();
bt_mesh_test_ra_cb_setup(rx_ended);
k_sleep(K_SECONDS(6 * TEST_DATA_WAITING_TIME));
ASSERT_TRUE(rx_cnt == 3, "Device didn't receive expected data");
PASS();
}
static void test_tx_power_replay_attack(void)
{
settings_test_backend_clear();
bt_mesh_test_setup();
static const struct bt_mesh_send_cb send_cb = {
.start = tx_started,
.end = tx_ended,
};
struct k_sem sem;
k_sem_init(&sem, 0, 1);
for (int i = 0; i < 3; i++) {
is_tx_succeeded = true;
memset(test_data, i, sizeof(test_data));
ASSERT_OK(bt_mesh_test_send_ra(rx_cfg.addr, test_data,
sizeof(test_data), &send_cb, &sem));
if (k_sem_take(&sem, K_SECONDS(TEST_DATA_WAITING_TIME))) {
LOG_ERR("Send timed out");
}
ASSERT_TRUE(!is_tx_succeeded);
}
for (int i = 0; i < 3; i++) {
is_tx_succeeded = false;
memset(test_data, i, sizeof(test_data));
ASSERT_OK(bt_mesh_test_send_ra(rx_cfg.addr, test_data,
sizeof(test_data), &send_cb, &sem));
if (k_sem_take(&sem, K_SECONDS(TEST_DATA_WAITING_TIME))) {
LOG_ERR("Send timed out");
}
ASSERT_TRUE(is_tx_succeeded);
}
PASS();
}
static void test_rx_power_replay_attack(void)
{
bt_mesh_test_setup();
bt_mesh_test_ra_cb_setup(rx_ended);
k_sleep(K_SECONDS(6 * TEST_DATA_WAITING_TIME));
ASSERT_TRUE(rx_cnt == 3, "Device didn't receive expected data");
PASS();
}
static void send_end_cb(int err, void *cb_data)
{
struct k_sem *sem = cb_data;
ASSERT_EQUAL(err, 0);
k_sem_give(sem);
}
static bool msg_send(uint16_t src, uint16_t dst)
{
struct bt_mesh_send_cb cb = {
.end = send_end_cb,
};
struct bt_mesh_msg_ctx ctx = {
.net_idx = 0,
.app_idx = 0,
.addr = dst,
.send_rel = false,
.send_ttl = BT_MESH_TTL_DEFAULT,
};
struct bt_mesh_net_tx tx = {
.ctx = &ctx,
.src = src,
};
struct k_sem sem;
int err;
k_sem_init(&sem, 0, 1);
BT_MESH_MODEL_BUF_DEFINE(msg, TEST_MSG_OP_1, 0);
bt_mesh_model_msg_init(&msg, TEST_MSG_OP_1);
err = bt_mesh_trans_send(&tx, &msg, &cb, &sem);
if (err) {
LOG_ERR("Failed to send message (err %d)", err);
return false;
}
err = k_sem_take(&sem, K_SECONDS(10));
if (err) {
LOG_ERR("Send timed out (err %d)", err);
return false;
}
return true;
}
static bool msg_recv(uint16_t expected_addr)
{
struct bt_mesh_test_msg msg;
int err;
err = bt_mesh_test_recv_msg(&msg, K_SECONDS(10));
if (err) {
LOG_ERR("Failed to receive message from %u (err %d)", expected_addr, err);
return false;
}
LOG_DBG("Received msg from %u", msg.ctx.addr);
ASSERT_EQUAL(expected_addr, msg.ctx.addr);
return true;
}
static bool ivi_update_toggle(void)
{
bool res;
bt_mesh_iv_update_test(true);
res = bt_mesh_iv_update();
bt_mesh_iv_update_test(false);
return res;
}
static void test_rx_rpl_frag(void)
{
settings_test_backend_clear();
bt_mesh_test_setup();
k_sleep(K_SECONDS(10));
/* Wait 3 messages from different sources. */
for (int i = 0; i < 3; i++) {
ASSERT_TRUE(msg_recv(100 + i));
}
/* Ask tx node to proceed to next test step. */
ASSERT_TRUE(msg_send(rx_cfg.addr, tx_cfg.addr));
/* Start IVI Update. This will set old_iv for all entries in RPL to 1. */
ASSERT_TRUE(ivi_update_toggle());
/* Receive messages from even nodes with new IVI. RPL entry with odd address will stay
* with old IVI.
*/
ASSERT_TRUE(msg_recv(100));
ASSERT_TRUE(msg_recv(102));
/* Ask tx node to proceed to next test step. */
ASSERT_TRUE(msg_send(rx_cfg.addr, tx_cfg.addr));
/* Complete IVI Update. */
ASSERT_FALSE(ivi_update_toggle());
/* Bump SeqNum in RPL for even addresses. */
ASSERT_TRUE(msg_recv(100));
ASSERT_TRUE(msg_recv(102));
/* Start IVI Update again. */
/* RPL entry with odd address should be removed causing fragmentation in RPL. old_iv flag
* for even entries will be set to 1.
*/
ASSERT_TRUE(ivi_update_toggle());
/* Ask tx node to proceed to next test step. */
ASSERT_TRUE(msg_send(rx_cfg.addr, tx_cfg.addr));
/* Complete IVI Update. */
ASSERT_FALSE(ivi_update_toggle());
/* Odd address entry should have been removed keeping even addresses accessible. */
struct bt_mesh_rpl *rpl = NULL;
struct bt_mesh_net_rx rx = {
.old_iv = 1,
.seq = 0,
.ctx.addr = 100,
.local_match = 1,
};
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl));
rx.ctx.addr = 101;
ASSERT_FALSE(bt_mesh_rpl_check(&rx, &rpl));
rx.ctx.addr = 102;
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl));
/* Let the settings store RPL. */
k_sleep(K_SECONDS(CONFIG_BT_MESH_RPL_STORE_TIMEOUT));
PASS();
}
static void test_tx_rpl_frag(void)
{
settings_test_backend_clear();
bt_mesh_test_setup();
k_sleep(K_SECONDS(10));
/* Send message for 3 different addresses. */
for (size_t i = 0; i < 3; i++) {
ASSERT_TRUE(msg_send(100 + i, rx_cfg.addr));
}
k_sleep(K_SECONDS(3));
/* Wait for the rx node. */
ASSERT_TRUE(msg_recv(rx_cfg.addr));
/* Start IVI Update. */
ASSERT_TRUE(ivi_update_toggle());
/* Send msg from elem 1 and 3 with new IVI. 2nd elem should have old IVI. */
ASSERT_TRUE(msg_send(100, rx_cfg.addr));
ASSERT_TRUE(msg_send(102, rx_cfg.addr));
/* Wait for the rx node. */
ASSERT_TRUE(msg_recv(rx_cfg.addr));
/* Complete IVI Update. */
ASSERT_FALSE(ivi_update_toggle());
/* Send message from even addresses with new IVI keeping odd address with old IVI. */
ASSERT_TRUE(msg_send(100, rx_cfg.addr));
ASSERT_TRUE(msg_send(102, rx_cfg.addr));
/* Start IVI Update again to be in sync with rx node. */
ASSERT_TRUE(ivi_update_toggle());
/* Wait for rx node. */
ASSERT_TRUE(msg_recv(rx_cfg.addr));
/* Complete IVI Update. */
ASSERT_FALSE(ivi_update_toggle());
PASS();
}
static void test_rx_reboot_after_defrag(void)
{
bt_mesh_test_setup();
/* Test that RPL entries are restored correctly after defrag and reboot. */
struct bt_mesh_rpl *rpl = NULL;
struct bt_mesh_net_rx rx = {
.old_iv = 1,
.seq = 0,
.ctx.addr = 100,
.local_match = 1,
};
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl));
rx.ctx.addr = 101;
ASSERT_FALSE(bt_mesh_rpl_check(&rx, &rpl));
rx.ctx.addr = 102;
ASSERT_TRUE(bt_mesh_rpl_check(&rx, &rpl));
PASS();
}
#define TEST_CASE(role, name, description) \
{ \
.test_id = "rpc_" #role "_" #name, \
.test_descr = description, \
.test_post_init_f = test_##role##_init, \
.test_tick_f = bt_mesh_test_timeout, \
.test_main_f = test_##role##_##name, \
}
static const struct bst_test_instance test_rpc[] = {
TEST_CASE(tx, immediate_replay_attack, "RPC: perform replay attack immediately"),
TEST_CASE(tx, power_replay_attack, "RPC: perform replay attack after power cycle"),
TEST_CASE(tx, rpl_frag, "RPC: Send messages after double IVI Update"),
TEST_CASE(rx, immediate_replay_attack, "RPC: device under immediate attack"),
TEST_CASE(rx, power_replay_attack, "RPC: device under power cycle reply attack"),
TEST_CASE(rx, rpl_frag, "RPC: Test RPL fragmentation after double IVI Update"),
TEST_CASE(rx, reboot_after_defrag, "RPC: Test PRL after defrag and reboot"),
BSTEST_END_MARKER
};
struct bst_test_list *test_rpc_install(struct bst_test_list *tests)
{
tests = bst_add_tests(tests, test_rpc);
return tests;
}