2020-01-25 00:44:29

by Gix, Brian

[permalink] [raw]
Subject: [PATCH BlueZ v2 0/5] mesh: Add NVM storage of Replay Protection List

Version 2: Fix path construction code when first reading RPL from NVM


An oversight led to losing our Replay Protection List with every
re-boot. This patch-set makes a number of Replay Protect List
modifications that culminate in adding rpl.c/h, which stores the latest
iv_index/sequence values for each node that handles an incoming packet.

The first 4 patches, does some maintenance required to handle RPL
according the the Mesh Specification.


Brian Gix (5):
mesh: Relocate tree deletion to util.c/h
mesh: Move Replay Protection to mesh/net.c
mesh: Remove unneeded Sequence Number increment
mesh: Apply Replay Protection to all incoming packets
mesh: Add NVM storage of Replay Protection

Makefile.mesh | 1 +
mesh/appkey.c | 102 ---------------
mesh/appkey.h | 3 -
mesh/mesh-config-json.c | 20 +--
mesh/model.c | 16 +--
mesh/net.c | 112 ++++++++++++++--
mesh/net.h | 3 +
mesh/rpl.c | 277 ++++++++++++++++++++++++++++++++++++++++
mesh/rpl.h | 30 +++++
mesh/util.c | 25 ++++
mesh/util.h | 1 +
11 files changed, 447 insertions(+), 143 deletions(-)
create mode 100644 mesh/rpl.c
create mode 100644 mesh/rpl.h

--
2.21.1


2020-01-25 00:44:29

by Gix, Brian

[permalink] [raw]
Subject: [PATCH BlueZ v2 4/5] mesh: Apply Replay Protection to all incoming packets

Replay Protection was only being applied against Application Keys,
but messages with Device Keys are just as vulnerable, and need to be
checked as well.
---
mesh/model.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/mesh/model.c b/mesh/model.c
index 0018c7cff..92a00496c 100644
--- a/mesh/model.c
+++ b/mesh/model.c
@@ -608,7 +608,7 @@ static bool msg_send(struct mesh_node *node, bool credential, uint16_t src,

iv_index = mesh_net_get_iv_index(net);

- seq_num = mesh_net_get_seq_num(net);
+ seq_num = mesh_net_next_seq_num(net);
if (!mesh_crypto_payload_encrypt(label, msg, out, msg_len, src, dst,
key_aid, seq_num, iv_index, szmic, key)) {
l_error("Failed to Encrypt Payload");
@@ -949,7 +949,7 @@ bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
struct mesh_net *net = node_get_net(node);
uint8_t num_ele;
int decrypt_idx, i, ele_idx;
- uint16_t addr;
+ uint16_t addr, crpl;
struct mesh_virtual *decrypt_virt = NULL;
bool result = false;
bool is_subscription;
@@ -997,14 +997,12 @@ bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,

/* print_packet("Clr Rx (pre-cache-check)", clear_text, size - 4); */

- if (key_aid != APP_AID_DEV) {
- uint16_t crpl = node_get_crpl(node);
+ crpl = node_get_crpl(node);

- if (net_msg_in_replay_cache(net, (uint16_t) decrypt_idx, src,
- crpl, seq, iv_index)) {
- result = true;
- goto done;
- }
+ if (net_msg_in_replay_cache(net, (uint16_t) decrypt_idx, src,
+ crpl, seq, iv_index)) {
+ result = true;
+ goto done;
}

print_packet("Clr Rx", clear_text, size - (szmict ? 8 : 4));
--
2.21.1

2020-01-25 00:45:17

by Gix, Brian

[permalink] [raw]
Subject: [PATCH BlueZ v2 2/5] mesh: Move Replay Protection to mesh/net.c

The specification calls for a flatter Replay Protection List that
applies to all processed messages, regardless of which credentials
were used to secure them. So storage and checking is now centralized
in mesh/net.c
---
mesh/appkey.c | 102 -------------------------------------------------
mesh/appkey.h | 3 --
mesh/model.c | 2 +-
mesh/net.c | 104 +++++++++++++++++++++++++++++++++++++++++++++-----
mesh/net.h | 3 ++
5 files changed, 99 insertions(+), 115 deletions(-)

diff --git a/mesh/appkey.c b/mesh/appkey.c
index 0eb268782..45d604007 100644
--- a/mesh/appkey.c
+++ b/mesh/appkey.c
@@ -35,7 +35,6 @@
#include "mesh/appkey.h"

struct mesh_app_key {
- struct l_queue *replay_cache;
uint16_t net_idx;
uint16_t app_idx;
uint8_t key[16];
@@ -44,12 +43,6 @@ struct mesh_app_key {
uint8_t new_key_aid;
};

-struct mesh_msg {
- uint32_t iv_index;
- uint32_t seq;
- uint16_t src;
-};
-
static bool match_key_index(const void *a, const void *b)
{
const struct mesh_app_key *key = a;
@@ -66,103 +59,11 @@ static bool match_bound_key(const void *a, const void *b)
return key->net_idx == idx;
}

-static bool match_replay_cache(const void *a, const void *b)
-{
- const struct mesh_msg *msg = a;
- uint16_t src = L_PTR_TO_UINT(b);
-
- return src == msg->src;
-}
-
-static bool clean_old_iv_index(void *a, void *b)
-{
- struct mesh_msg *msg = a;
- uint32_t iv_index = L_PTR_TO_UINT(b);
-
- if (iv_index < 2)
- return false;
-
- if (msg->iv_index < iv_index - 1) {
- l_free(msg);
- return true;
- }
-
- return false;
-}
-
-bool appkey_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
- uint16_t src, uint16_t crpl, uint32_t seq,
- uint32_t iv_index)
-{
- struct mesh_app_key *key;
- struct mesh_msg *msg;
- struct l_queue *app_keys;
-
- app_keys = mesh_net_get_app_keys(net);
- if (!app_keys)
- return false;
-
- l_debug("Test Replay src: %4.4x seq: %6.6x iv: %8.8x",
- src, seq, iv_index);
-
- key = l_queue_find(app_keys, match_key_index, L_UINT_TO_PTR(idx));
-
- if (!key)
- return false;
-
- msg = l_queue_find(key->replay_cache, match_replay_cache,
- L_UINT_TO_PTR(src));
-
- if (msg) {
- if (iv_index > msg->iv_index) {
- msg->seq = seq;
- msg->iv_index = iv_index;
- return false;
- }
-
- if (seq < msg->seq) {
- l_debug("Ignoring packet with lower sequence number");
- return true;
- }
-
- if (seq == msg->seq) {
- l_debug("Message already processed (duplicate)");
- return true;
- }
-
- msg->seq = seq;
-
- return false;
- }
-
- l_debug("New Entry for %4.4x", src);
- if (key->replay_cache == NULL)
- key->replay_cache = l_queue_new();
-
- /* Replay Cache is fixed sized */
- if (l_queue_length(key->replay_cache) >= crpl) {
- int ret = l_queue_foreach_remove(key->replay_cache,
- clean_old_iv_index, L_UINT_TO_PTR(iv_index));
-
- if (!ret)
- return true;
- }
-
- msg = l_new(struct mesh_msg, 1);
- msg->src = src;
- msg->seq = seq;
- msg->iv_index = iv_index;
- l_queue_push_head(key->replay_cache, msg);
-
- return false;
-}
-
static struct mesh_app_key *app_key_new(void)
{
struct mesh_app_key *key = l_new(struct mesh_app_key, 1);

key->new_key_aid = 0xFF;
- key->replay_cache = l_queue_new();
return key;
}

@@ -192,7 +93,6 @@ void appkey_key_free(void *data)
if (!key)
return;

- l_queue_destroy(key->replay_cache, l_free);
l_free(key);
}

@@ -403,8 +303,6 @@ int appkey_key_add(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
key->app_idx = app_idx;
l_queue_push_tail(app_keys, key);

- l_queue_clear(key->replay_cache, l_free);
-
return MESH_STATUS_SUCCESS;
}

diff --git a/mesh/appkey.h b/mesh/appkey.h
index b3e548071..23b474a0a 100644
--- a/mesh/appkey.h
+++ b/mesh/appkey.h
@@ -25,9 +25,6 @@ struct mesh_app_key;
bool appkey_key_init(struct mesh_net *net, uint16_t net_idx, uint16_t app_idx,
uint8_t *key_value, uint8_t *new_key_value);
void appkey_key_free(void *data);
-bool appkey_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
- uint16_t src, uint16_t crpl, uint32_t seq,
- uint32_t iv_index);
const uint8_t *appkey_get_key(struct mesh_net *net, uint16_t app_idx,
uint8_t *key_id);
int appkey_get_key_idx(struct mesh_app_key *app_key,
diff --git a/mesh/model.c b/mesh/model.c
index 6d7674ee5..0018c7cff 100644
--- a/mesh/model.c
+++ b/mesh/model.c
@@ -1000,7 +1000,7 @@ bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
if (key_aid != APP_AID_DEV) {
uint16_t crpl = node_get_crpl(node);

- if (appkey_msg_in_replay_cache(net, (uint16_t) decrypt_idx, src,
+ if (net_msg_in_replay_cache(net, (uint16_t) decrypt_idx, src,
crpl, seq, iv_index)) {
result = true;
goto done;
diff --git a/mesh/net.c b/mesh/net.c
index 219217793..71ff2cea0 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -135,6 +135,7 @@ struct mesh_net {

struct l_queue *subnets;
struct l_queue *msg_cache;
+ struct l_queue *replay_cache;
struct l_queue *sar_in;
struct l_queue *sar_out;
struct l_queue *frnd_msgs;
@@ -255,6 +256,12 @@ struct net_beacon_data {
bool processed;
};

+struct mesh_rpl {
+ uint32_t iv_index;
+ uint32_t seq;
+ uint16_t src;
+};
+
#define FAST_CACHE_SIZE 8
static struct l_queue *fast_cache;
static struct l_queue *nets;
@@ -554,13 +561,6 @@ static void mesh_sar_free(void *data)
l_free(sar);
}

-static void mesh_msg_free(void *data)
-{
- struct mesh_msg *msg = data;
-
- l_free(msg);
-}
-
static void subnet_free(void *data)
{
struct mesh_subnet *subnet = data;
@@ -688,7 +688,8 @@ void mesh_net_free(struct mesh_net *net)
return;

l_queue_destroy(net->subnets, subnet_free);
- l_queue_destroy(net->msg_cache, mesh_msg_free);
+ l_queue_destroy(net->msg_cache, l_free);
+ l_queue_destroy(net->replay_cache, l_free);
l_queue_destroy(net->sar_in, mesh_sar_free);
l_queue_destroy(net->sar_out, mesh_sar_free);
l_queue_destroy(net->frnd_msgs, l_free);
@@ -1024,7 +1025,7 @@ int mesh_net_add_key(struct mesh_net *net, uint16_t idx, const uint8_t *value)

void mesh_net_flush_msg_queues(struct mesh_net *net)
{
- l_queue_clear(net->msg_cache, mesh_msg_free);
+ l_queue_clear(net->msg_cache, l_free);
}

uint32_t mesh_net_get_iv_index(struct mesh_net *net)
@@ -3734,3 +3735,88 @@ uint32_t mesh_net_get_instant(struct mesh_net *net)

return net->instant;
}
+
+static bool match_replay_cache(const void *a, const void *b)
+{
+ const struct mesh_rpl *rpe = a;
+ uint16_t src = L_PTR_TO_UINT(b);
+
+ return src == rpe->src;
+}
+
+static bool clean_old_iv_index(void *a, void *b)
+{
+ struct mesh_rpl *rpe = a;
+ uint32_t iv_index = L_PTR_TO_UINT(b);
+
+ if (iv_index < 2)
+ return false;
+
+ if (rpe->iv_index < iv_index - 1) {
+ l_free(rpe);
+ return true;
+ }
+
+ return false;
+}
+
+bool net_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
+ uint16_t src, uint16_t crpl, uint32_t seq,
+ uint32_t iv_index)
+{
+ struct mesh_rpl *rpe;
+
+ /* If anything missing reject this message by returning true */
+ if (!net || !net->node)
+ return true;
+
+ if (!net->replay_cache)
+ net->replay_cache = l_queue_new();
+
+ l_debug("Test Replay src: %4.4x seq: %6.6x iv: %8.8x",
+ src, seq, iv_index);
+
+ rpe = l_queue_find(net->replay_cache, match_replay_cache,
+ L_UINT_TO_PTR(src));
+
+ if (rpe) {
+ if (iv_index > rpe->iv_index) {
+ rpe->seq = seq;
+ rpe->iv_index = iv_index;
+ return false;
+ }
+
+ if (seq < rpe->seq) {
+ l_debug("Ignoring packet with lower sequence number");
+ return true;
+ }
+
+ if (seq == rpe->seq) {
+ l_debug("Message already processed (duplicate)");
+ return true;
+ }
+
+ rpe->seq = seq;
+
+ return false;
+ }
+
+ l_debug("New Entry for %4.4x", src);
+
+ /* Replay Cache is fixed sized */
+ if (l_queue_length(net->replay_cache) >= crpl) {
+ int ret = l_queue_foreach_remove(net->replay_cache,
+ clean_old_iv_index, L_UINT_TO_PTR(iv_index));
+
+ if (!ret)
+ return true;
+ }
+
+ rpe = l_new(struct mesh_rpl, 1);
+ rpe->src = src;
+ rpe->seq = seq;
+ rpe->iv_index = iv_index;
+ l_queue_push_head(net->replay_cache, rpe);
+
+ return false;
+}
diff --git a/mesh/net.h b/mesh/net.h
index 023b61e71..ff0a9bb2b 100644
--- a/mesh/net.h
+++ b/mesh/net.h
@@ -379,3 +379,6 @@ void mesh_net_set_prov(struct mesh_net *net, struct mesh_prov *prov);
uint32_t mesh_net_get_instant(struct mesh_net *net);
struct l_queue *mesh_net_get_friends(struct mesh_net *net);
struct l_queue *mesh_net_get_negotiations(struct mesh_net *net);
+bool net_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
+ uint16_t src, uint16_t crpl, uint32_t seq,
+ uint32_t iv_index);
--
2.21.1

2020-01-25 00:45:17

by Gix, Brian

[permalink] [raw]
Subject: [PATCH BlueZ v2 5/5] mesh: Add NVM storage of Replay Protection

Mesh specification requires that Replay Protection be preserved
across node restarts. This adds that storage in
<node_uuid>/rpl/<iv_index>/<src>

Realtime access remains in an l_queue structure, and stored as
messages are processed.
---
Makefile.mesh | 1 +
mesh/net.c | 21 ++--
mesh/rpl.c | 277 ++++++++++++++++++++++++++++++++++++++++++++++++++
mesh/rpl.h | 30 ++++++
4 files changed, 322 insertions(+), 7 deletions(-)
create mode 100644 mesh/rpl.c
create mode 100644 mesh/rpl.h

diff --git a/Makefile.mesh b/Makefile.mesh
index 401122029..10573b304 100644
--- a/Makefile.mesh
+++ b/Makefile.mesh
@@ -32,6 +32,7 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \
mesh/manager.h mesh/manager.c \
mesh/pb-adv.h mesh/pb-adv.c \
mesh/keyring.h mesh/keyring.c \
+ mesh/rpl.h mesh/rpl.c \
mesh/mesh-defs.h
pkglibexec_PROGRAMS += mesh/bluetooth-meshd

diff --git a/mesh/net.c b/mesh/net.c
index ff43176a3..09a4c6834 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -36,6 +36,7 @@
#include "mesh/mesh-config.h"
#include "mesh/model.h"
#include "mesh/appkey.h"
+#include "mesh/rpl.h"

#define abs_diff(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))

@@ -256,12 +257,6 @@ struct net_beacon_data {
bool processed;
};

-struct mesh_rpl {
- uint32_t iv_index;
- uint32_t seq;
- uint16_t src;
-};
-
#define FAST_CACHE_SIZE 8
static struct l_queue *fast_cache;
static struct l_queue *nets;
@@ -2714,6 +2709,9 @@ static void update_iv_ivu_state(struct mesh_net *net, uint32_t iv_index,
struct mesh_config *cfg = node_config_get(net->node);

mesh_config_write_iv_index(cfg, iv_index, ivu);
+
+ /* Cleanup Replay Protection List NVM */
+ rpl_init(net->node, iv_index);
}

net->iv_index = iv_index;
@@ -3769,8 +3767,11 @@ bool net_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
if (!net || !net->node)
return true;

- if (!net->replay_cache)
+ if (!net->replay_cache) {
net->replay_cache = l_queue_new();
+ rpl_init(net->node, net->iv_index);
+ rpl_get_list(net->node, net->replay_cache);
+ }

l_debug("Test Replay src: %4.4x seq: %6.6x iv: %8.8x",
src, seq, iv_index);
@@ -3782,6 +3783,7 @@ bool net_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
if (iv_index > rpe->iv_index) {
rpe->seq = seq;
rpe->iv_index = iv_index;
+ rpl_put_entry(net->node, src, iv_index, seq);
return false;
}

@@ -3797,6 +3799,8 @@ bool net_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,

rpe->seq = seq;

+ rpl_put_entry(net->node, src, iv_index, seq);
+
return false;
}

@@ -3811,6 +3815,9 @@ bool net_msg_in_replay_cache(struct mesh_net *net, uint16_t idx,
return true;
}

+ if (!rpl_put_entry(net->node, src, iv_index, seq))
+ return true;
+
rpe = l_new(struct mesh_rpl, 1);
rpe->src = src;
rpe->seq = seq;
diff --git a/mesh/rpl.c b/mesh/rpl.c
new file mode 100644
index 000000000..27cce4e53
--- /dev/null
+++ b/mesh/rpl.c
@@ -0,0 +1,277 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2020 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include <sys/stat.h>
+
+#include <ell/ell.h>
+
+#include "mesh/mesh-defs.h"
+
+#include "mesh/node.h"
+#include "mesh/net.h"
+#include "mesh/util.h"
+#include "mesh/rpl.h"
+
+const char *rpl_dir = "/rpl";
+
+bool rpl_put_entry(struct mesh_node *node, uint16_t src, uint32_t iv_index,
+ uint32_t seq)
+{
+ const char *node_path;
+ char src_file[PATH_MAX];
+ char seq_txt[7];
+ bool result = false;
+ int fd;
+
+ if (!node || !IS_UNICAST(src))
+ return false;
+
+ node_path = node_get_storage_dir(node);
+
+ if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX)
+ return false;
+
+ snprintf(src_file, PATH_MAX, "%s%s/%8.8x/%4.4x", node_path, rpl_dir,
+ iv_index, src);
+
+ fd = open(src_file, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+ if (fd >= 0) {
+ snprintf(seq_txt, 7, "%6.6x", seq);
+ if (write(fd, seq_txt, 6) == 6)
+ result = true;
+
+ close(fd);
+ }
+
+ if (!result)
+ return false;
+
+ /* Delete RPL entry from old iv_index (if it exists) */
+ iv_index--;
+ snprintf(src_file, PATH_MAX, "%s%s/%8.8x/%4.4x", node_path, rpl_dir,
+ iv_index, src);
+ remove(src_file);
+
+
+ return result;
+}
+
+void rpl_del_entry(struct mesh_node *node, uint16_t src)
+{
+ const char *node_path;
+ char rpl_path[PATH_MAX];
+ struct dirent *entry;
+ DIR *dir;
+
+ if (!node || !IS_UNICAST(src))
+ return;
+
+ node_path = node_get_storage_dir(node);
+
+ if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX)
+ return;
+
+ snprintf(rpl_path, PATH_MAX, "%s%s", node_path, rpl_dir);
+ dir = opendir(rpl_path);
+
+ if (!dir)
+ return;
+
+ /* Remove all instances of src address */
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type == DT_DIR) {
+ snprintf(rpl_path, PATH_MAX, "%s/%4.4x",
+ entry->d_name, src);
+ remove(rpl_path);
+ }
+ }
+
+ closedir(dir);
+}
+
+static bool match_src(const void *a, const void *b)
+{
+ const struct mesh_rpl *rpl = a;
+ uint16_t src = L_PTR_TO_UINT(b);
+
+ return rpl->src == src;
+}
+
+static void get_entries(const char *iv_path, struct l_queue *rpl_list)
+{
+ struct mesh_rpl *rpl;
+ struct dirent *entry;
+ DIR *dir;
+ int fd;
+ const char *iv_txt;
+ char src_path[PATH_MAX];
+ char seq_txt[7];
+ uint32_t iv_index, seq;
+ uint16_t src;
+
+ dir = opendir(iv_path);
+
+ if (!dir)
+ return;
+
+ iv_txt = basename(iv_path);
+ sscanf(iv_txt, "%08x", &iv_index);
+
+ memset(seq_txt, 0, sizeof(seq_txt));
+
+ while ((entry = readdir(dir)) != NULL) {
+ /* RPL sequences are stored in src files under iv_index */
+ if (entry->d_type == DT_REG) {
+ snprintf(src_path, PATH_MAX, "%s/%s", iv_path,
+ entry->d_name);
+ fd = open(src_path, O_RDONLY);
+
+ if (fd < 0)
+ continue;
+
+ if(read(fd, seq_txt, 6) == 6) {
+ sscanf(entry->d_name, "%04hx", &src);
+ sscanf(seq_txt, "%06x", &seq);
+
+ rpl = l_queue_find(rpl_list, match_src,
+ L_UINT_TO_PTR(src));
+
+ if (rpl) {
+ /* Replace older entries */
+ if (rpl->iv_index < iv_index) {
+ rpl->iv_index = iv_index;
+ rpl->seq = seq;
+ }
+ } else if (seq <= SEQ_MASK && IS_UNICAST(src)) {
+ rpl = l_new(struct mesh_rpl, 1);
+ rpl->src = src;
+ rpl->iv_index = iv_index;
+ rpl->seq = seq;
+
+ l_queue_push_head(rpl_list, rpl);
+ }
+ }
+
+ close(fd);
+ }
+ }
+
+ closedir(dir);
+}
+
+bool rpl_get_list(struct mesh_node *node, struct l_queue *rpl_list)
+{
+ const char *node_path;
+ struct dirent *entry;
+ char *rpl_path;
+ size_t len;
+ DIR *dir;
+
+ if (!node || !rpl_list)
+ return false;
+
+ node_path = node_get_storage_dir(node);
+
+ len = strlen(node_path) + strlen(rpl_dir) + 1;
+
+ if (len + 14 > PATH_MAX)
+ return false;
+
+ rpl_path = l_malloc(len);
+ snprintf(rpl_path, PATH_MAX, "%s%s", node_path, rpl_dir);
+
+ dir = opendir(rpl_path);
+
+ if (!dir) {
+ l_error("Failed to read RPL dir: %s", rpl_path);
+ l_free(rpl_path);
+ return false;
+ }
+
+ while ((entry = readdir(dir)) != NULL) {
+ /* RPL sequences are stored in files under iv_indexs */
+ if (entry->d_type == DT_DIR && entry->d_name[0] != '.') {
+ snprintf(rpl_path, PATH_MAX, "%s%s/%s",
+ node_path, rpl_dir, entry->d_name);
+ get_entries(rpl_path, rpl_list);
+ }
+ }
+
+ l_free(rpl_path);
+ closedir(dir);
+
+ return true;
+}
+
+void rpl_init(struct mesh_node *node, uint32_t cur)
+{
+ uint32_t old = cur - 1;
+ const char *node_path;
+ struct dirent *entry;
+ char rpl_path[PATH_MAX];
+ DIR *dir;
+
+ if (!node)
+ return;
+
+ node_path = node_get_storage_dir(node);
+
+ if (strlen(node_path) + strlen(rpl_dir) + 10 >= PATH_MAX)
+ return;
+
+ /* Make sure rpl_path exists */
+ snprintf(rpl_path, PATH_MAX, "%s%s", node_path, rpl_dir);
+ mkdir(rpl_path, 0755);
+
+ /* Cleanup any stale trees */
+ dir = opendir(rpl_path);
+ if (!dir)
+ return;
+
+ while ((entry = readdir(dir)) != NULL) {
+ if (entry->d_type == DT_DIR) {
+ const char *iv_txt = basename(entry->d_name);
+ uint32_t iv_index = 0;
+
+ /* Delete all invalid iv_index trees */
+ sscanf(iv_txt, "%08x", &iv_index);
+ if (iv_index != cur && iv_index != old)
+ del_path(entry->d_name);
+ }
+ }
+
+ closedir(dir);
+
+ /* Make sure all currently considered iv_index directories exist */
+ snprintf(rpl_path, PATH_MAX, "%s%s/%8.8x", node_path, rpl_dir, old);
+ mkdir(rpl_path, 0755);
+ snprintf(rpl_path, PATH_MAX, "%s%s/%8.8x", node_path, rpl_dir, cur);
+ mkdir(rpl_path, 0755);
+}
diff --git a/mesh/rpl.h b/mesh/rpl.h
new file mode 100644
index 000000000..17d2e3f05
--- /dev/null
+++ b/mesh/rpl.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2020 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ */
+
+struct mesh_rpl {
+ uint32_t iv_index;
+ uint32_t seq;
+ uint16_t src;
+};
+
+bool rpl_put_entry(struct mesh_node *node, uint16_t src, uint32_t iv_index,
+ uint32_t seq);
+void rpl_del_entry(struct mesh_node *node, uint16_t src);
+bool rpl_get_list(struct mesh_node *node, struct l_queue *rpl_list);
+void rpl_init(struct mesh_node *node, uint32_t iv_index);
--
2.21.1

2020-01-27 21:27:47

by Rafał Gajda

[permalink] [raw]
Subject: Re: [PATCH BlueZ v2 0/5] mesh: Add NVM storage of Replay Protection List

Hi,

sob., 25 sty 2020 o 01:44 Brian Gix <[email protected]> napisał(a):
>
> Version 2: Fix path construction code when first reading RPL from NVM
>
>
> An oversight led to losing our Replay Protection List with every
> re-boot. This patch-set makes a number of Replay Protect List
> modifications that culminate in adding rpl.c/h, which stores the latest
> iv_index/sequence values for each node that handles an incoming packet.
>
> The first 4 patches, does some maintenance required to handle RPL
> according the the Mesh Specification.

While testing this patch set I've come across the following problem:

After importing and attaching new node to the daemon I tried to
configure new application key by sending dev key message
ConfigOpcode.APPKEY_ADD to self.
This previously resulted in DevKeyMessageReceived reply.

16:40:24.045 MESHD.stderr INFO 2020-01-27 16:40:24.045
mesh.c:622 attach_call mesh/mesh.c:attach_call()
Attach
16:40:24.045 DBUS.in DEBUG /
org.freedesktop.DBus.ObjectManager::GetManagedObjects()
16:40:24.046 MESHD.stderr INFO 2020-01-27 16:40:24.046
node.c:1311 get_app_properties
mesh/node.c:get_app_properties() path /com/silvair/application
16:40:24.046 MESHD.stderr INFO 2020-01-27 16:40:24.046
node.c:1158 get_element_properties
mesh/node.c:get_element_properties() path
/com/silvair/application/element0
16:40:24.048 MESHD.stderr INFO 2020-01-27 16:40:24.046
node.c:1797 build_element_config
mesh/node.c:build_element_config() Element 0
16:40:24.048 DBUS.out DEBUG /org/bluez/mesh
org.bluez.mesh.Network1::Attach('/', 1141961045074800157) =
('/org/bluez/mesh/node83bf3b46810345a28bcce70d8d0b46d3', [[0, []]])
16:40:24.054 DBUS.out DEBUG
/org/bluez/mesh/node83bf3b46810345a28bcce70d8d0b46d3
org.freedesktop.DBus.Properties::Get('org.bluez.mesh.Node1',
'Addresses') = (<dbus_next.signature.Variant object at
0x7f11eaece150>,)
16:40:24.054 APPLICATION INFO Attached to node
/org/bluez/mesh/node83bf3b46810345a28bcce70d8d0b46d3, address: 05f2,
configuration: {0: {}}
16:40:24.056 MESHD.stderr INFO 2020-01-27 16:40:24.055
keyring.c:209 keyring_put_remote_dev_key
mesh/keyring.c:keyring_put_remote_dev_key() Put Dev Key
/tmp/pytest-of-ragajda/pytest-74/test_rpl_persistence_after_res0/config/83bf3b46810345a28bcce70d8d0b46d3/dev_keys/05f2
16:40:24.057 DBUS.out DEBUG
/org/bluez/mesh/node83bf3b46810345a28bcce70d8d0b46d3
org.bluez.mesh.Management1::ImportRemoteNode(1522, 1,
b'\x9dm\xd0\xe9n\xb2]\xc1\x9a@\xed\x99\x14\xf8\xf0?') = ()
16:40:24.060 APPLICATION.Element0.ConfigClient DEBUG Sending:
/com/silvair/application/element0 -> 05f2 [remote True, net_index 0]
ConfigOpcode.APPKEY_ADD Container(app_key_index=0, net_key_index=0,
app_key=b'c\x96GqsO\xbdv\xe3\xb4\x05\x19\xd1\xd9JH')
16:40:24.060 APPLICATION.Element0.ConfigClient DEBUG Sending:
/com/silvair/application/element0 - > 0001 [remote 1522, net_index 0]
0000000063964771734fbd76e3b40519d1d94a48
16:40:24.063 MESHD.stderr INFO 2020-01-27 16:40:24.062
node.c:1891 dev_key_send_call mesh/node.c:dev_key_send_call()
DevKeySend
16:40:24.064 MESHD.stderr INFO 2020-01-27 16:40:24.063
model.c:604 msg_send mesh/model.c:msg_send() (6fff)
0x7ffffffa9fa0
16:40:24.064 MESHD.stderr INFO 2020-01-27 16:40:24.063
model.c:605 msg_send mesh/model.c:msg_send()
net_idx 0
16:40:24.064 MESHD.stderr INFO 2020-01-27 16:40:24.063
model.c:957 mesh_model_rx mesh/model.c:mesh_model_rx()
iv_index 00000000 key_aid = 00
16:40:24.064 MESHD.stderr INFO 2020-01-27 16:40:24.063
util.c:46 print_packet mesh/util.c:print_packet()
39624.063 Clr Rx: 0000000063964771734fbd76e3b40519d1d94a48
16:40:24.065 MESHD.stderr INFO 2020-01-27 16:40:24.063
model.c:370 forward_model mesh/model.c:forward_model()
model ffff0000 with idx 7fff
16:40:24.065 MESHD.stderr INFO 2020-01-27 16:40:24.063
cfgmod-server.c:783 cfg_srv_pkt
mesh/cfgmod-server.c:cfg_srv_pkt() CONFIG-SRV-opcode 0x0 size 19 idx
000
16:40:24.065 MESHD.stderr INFO 2020-01-27 16:40:24.065
cfgmod-server.c:1036 cfg_srv_pkt
mesh/cfgmod-server.c:cfg_srv_pkt() Add/Update AppKey success: Net_Idx
000, App_Idx 000
16:40:24.065 MESHD.stderr INFO 2020-01-27 16:40:24.065
model.c:604 msg_send mesh/model.c:msg_send() (7fff)
0x5564327d323c
16:40:24.066 MESHD.stderr INFO 2020-01-27 16:40:24.065
model.c:605 msg_send mesh/model.c:msg_send()
net_idx 0
16:40:24.066 MESHD.stderr INFO 2020-01-27 16:40:24.065
model.c:957 mesh_model_rx mesh/model.c:mesh_model_rx()
iv_index 00000000 key_aid = 00
16:40:24.066 MESHD.stderr INFO 2020-01-27 16:40:24.065
util.c:46 print_packet mesh/util.c:print_packet()
39624.065 Clr Rx: 800300000000
16:40:24.066 MESHD.stderr INFO 2020-01-27 16:40:24.065
cfgmod-server.c:783 cfg_srv_pkt
mesh/cfgmod-server.c:cfg_srv_pkt() CONFIG-SRV-opcode 0x8003 size 4 idx
000
16:40:24.067 MESHD.stderr INFO 2020-01-27 16:40:24.065
model.c:871 send_dev_key_msg_rcvd
mesh/model.c:send_dev_key_msg_rcvd() Send "DevKeyMessageReceived"
16:40:24.067 MESHD.stderr INFO 2020-01-27 16:40:24.065
mesh-config-json.c:2111 mesh_config_write_seq_number
mesh/mesh-config-json.c:mesh_config_write_seq_number() Seq Cache: 1 ->
14286

After applying the patch set the same procedure results in:

16:41:04.102 MESHD.stderr INFO mesh/mesh.c:attach_call() Attach
16:41:04.103 DBUS.in DEBUG /
org.freedesktop.DBus.ObjectManager::GetManagedObjects()
16:41:04.103 MESHD.stderr INFO
mesh/node.c:get_app_properties() path /com/silvair/application
16:41:04.104 MESHD.stderr INFO
mesh/node.c:get_element_properties() path
/com/silvair/application/element0
16:41:04.105 MESHD.stderr INFO
mesh/node.c:build_element_config() Element 0
16:41:04.105 DBUS.out DEBUG /org/bluez/mesh
org.bluez.mesh.Network1::Attach('/', 11095264700132944744) =
('/org/bluez/mesh/node83bf3b46810345a28bcce70d8d0b46d3', [[0, []]])
16:41:04.110 DBUS.out DEBUG
/org/bluez/mesh/node83bf3b46810345a28bcce70d8d0b46d3
org.freedesktop.DBus.Properties::Get('org.bluez.mesh.Node1',
'Addresses') = (<dbus_next.signature.Variant object at
0x7f27613a2990>,)
16:41:04.110 APPLICATION INFO Attached to node
/org/bluez/mesh/node83bf3b46810345a28bcce70d8d0b46d3, address: 05f2,
configuration: {0: {}}
16:41:04.111 MESHD.stderr INFO
mesh/keyring.c:keyring_put_remote_dev_key() Put Dev Key
/tmp/pytest-of-ragajda/pytest-75/test_rpl_persistence_after_res0/config/83bf3b46810345a28bcce70d8d0b46d3/dev_keys/05f2
16:41:04.112 DBUS.out DEBUG
/org/bluez/mesh/node83bf3b46810345a28bcce70d8d0b46d3
org.bluez.mesh.Management1::ImportRemoteNode(1522, 1,
b'\x9dm\xd0\xe9n\xb2]\xc1\x9a@\xed\x99\x14\xf8\xf0?') = ()
16:41:04.113 APPLICATION.Element0.ConfigClient DEBUG Sending:
/com/silvair/application/element0 -> 05f2 [remote True, net_index 0]
ConfigOpcode.APPKEY_ADD Container(app_key_index=0, net_key_index=0,
app_key=b'c\x96GqsO\xbdv\xe3\xb4\x05\x19\xd1\xd9JH')
16:41:04.113 APPLICATION.Element0.ConfigClient DEBUG Sending:
/com/silvair/application/element0 - > 0001 [remote 1522, net_index 0]
0000000063964771734fbd76e3b40519d1d94a48
16:41:04.113 MESHD.stderr INFO
mesh/node.c:dev_key_send_call() DevKeySend
16:41:04.114 MESHD.stderr INFO mesh/model.c:msg_send() (6fff)
0x7ffffce69290
16:41:04.114 MESHD.stderr INFO mesh/model.c:msg_send() net_idx 0
16:41:04.114 MESHD.stderr INFO
mesh/mesh-config-json.c:mesh_config_write_seq_number() Seq Cache: 1 ->
25001
16:41:04.114 MESHD.stderr INFO mesh/model.c:mesh_model_rx()
iv_index 00000000 key_aid = 00
16:41:04.115 MESHD.stderr INFO
mesh/net.c:net_msg_in_replay_cache() Test Replay src: 05f2 seq: 000001
iv: 00000000
16:41:04.115 MESHD.stderr INFO
mesh/net.c:net_msg_in_replay_cache() New Entry for 05f2
16:41:04.115 MESHD.stderr INFO mesh/util.c:print_packet()
39664.115 Clr Rx: 0000000063964771734fbd76e3b40519d1d94a48
16:41:04.115 MESHD.stderr INFO mesh/model.c:forward_model()
model ffff0000 with idx 7fff
16:41:04.115 MESHD.stderr INFO
mesh/cfgmod-server.c:cfg_srv_pkt() CONFIG-SRV-opcode 0x0 size 19 idx
000
16:41:04.115 MESHD.stderr INFO
mesh/cfgmod-server.c:cfg_srv_pkt() Add/Update AppKey success: Net_Idx
000, App_Idx 000
16:41:04.115 MESHD.stderr INFO mesh/model.c:msg_send() (7fff)
0x55639ab9c8ec
16:41:04.115 MESHD.stderr INFO mesh/model.c:msg_send() net_idx 0
16:41:04.116 MESHD.stderr INFO mesh/model.c:mesh_model_rx()
iv_index 00000000 key_aid = 00
16:41:04.116 MESHD.stderr INFO
mesh/net.c:net_msg_in_replay_cache() Test Replay src: 05f2 seq: 000001
iv: 00000000
16:41:04.116 MESHD.stderr INFO
mesh/net.c:net_msg_in_replay_cache() Message already processed
(duplicate)

It looks like sequence number is correctly incremented when calling
DevKeySend but it isn't incremented at all when ConfigModel sends the
reply back.

--

Rafał Gajda
Silvair Sp. z o.o.

2020-01-28 11:27:16

by Rafał Gajda

[permalink] [raw]
Subject: Re: [PATCH BlueZ v2 0/5] mesh: Add NVM storage of Replay Protection List

Hi,

pon., 27 sty 2020 o 22:27 Rafał Gajda <[email protected]> napisał(a):
>
> It looks like sequence number is correctly incremented when calling
> DevKeySend but it isn't incremented at all when ConfigModel sends the
> reply back.

I think the culprit is in mesh/net.c in mesh_net_app_send() line 3160:

> /* First enqueue to any Friends and internal models */
> result = msg_rxed(net, false, iv_index, ttl,
> seq + seg_max,
> net_idx,
> src, dst,
> key_aid,szmic, seq & SEQ_ZERO_MASK,
> msg, msg_len);
>
> /* If addressed to a unicast address and successfully enqueued,
> * or delivered to one of our Unicast addresses we are done
> */
> if ((result && IS_UNICAST(dst)) || src == dst ||
> (dst >= net->src_addr && dst <= net->last_addr)) {
> /* Adjust our seq_num for "virtual" delivery */
> net->seq_num += seg_max;
> return true;
> }

When sending the message we first deliver it to internal models and
only after that we increment the msg sequence number.
If we do "net->seq_num += seg_max" before calling msg_rxed() then
messages are delivered correctly.

--

Rafał Gajda
Silvair Sp. z o.o.