Version 4: Style guide fixes from Inga (patch 5/5).
Version 3: Fix problem found by Rafał relating to the handling of
sequence numbers of messages being handled internally (loop-backs),
which were being rejected by the Replay Protection List (please see
patch 3/5).
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: Clean-up unneeded Sequence Number increments
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 | 128 +++++++++++++++---
mesh/net.h | 3 +
mesh/rpl.c | 278 ++++++++++++++++++++++++++++++++++++++++
mesh/rpl.h | 30 +++++
mesh/util.c | 25 ++++
mesh/util.h | 1 +
11 files changed, 457 insertions(+), 150 deletions(-)
create mode 100644 mesh/rpl.c
create mode 100644 mesh/rpl.h
--
2.21.1
---
mesh/mesh-config-json.c | 20 +-------------------
mesh/util.c | 25 +++++++++++++++++++++++++
mesh/util.h | 1 +
3 files changed, 27 insertions(+), 19 deletions(-)
diff --git a/mesh/mesh-config-json.c b/mesh/mesh-config-json.c
index 5855149e3..ad2d4d0f8 100644
--- a/mesh/mesh-config-json.c
+++ b/mesh/mesh-config-json.c
@@ -2253,24 +2253,6 @@ bool mesh_config_load_nodes(const char *cfgdir_name, mesh_config_node_func_t cb,
return true;
}
-static int del_fobject(const char *fpath, const struct stat *sb, int typeflag,
- struct FTW *ftwbuf)
-{
- switch (typeflag) {
- case FTW_DP:
- rmdir(fpath);
- l_debug("RMDIR %s", fpath);
- break;
-
- case FTW_SL:
- default:
- remove(fpath);
- l_debug("RM %s", fpath);
- break;
- }
- return 0;
-}
-
void mesh_config_destroy(struct mesh_config *cfg)
{
char *node_dir, *node_name;
@@ -2291,7 +2273,7 @@ void mesh_config_destroy(struct mesh_config *cfg)
if (strcmp(node_name, uuid))
return;
- nftw(node_dir, del_fobject, 5, FTW_DEPTH | FTW_PHYS);
+ del_path(node_dir);
/* Release node config object */
mesh_config_release(cfg);
diff --git a/mesh/util.c b/mesh/util.c
index 986ba4b28..43340f159 100644
--- a/mesh/util.c
+++ b/mesh/util.c
@@ -24,6 +24,7 @@
#define _GNU_SOURCE
#include <dirent.h>
#include <ftw.h>
+#include <unistd.h>
#include <stdio.h>
#include <limits.h>
#include <time.h>
@@ -129,3 +130,27 @@ int create_dir(const char *dir_name)
return 0;
}
+
+static int del_fobject(const char *fpath, const struct stat *sb, int typeflag,
+ struct FTW *ftwbuf)
+{
+ switch (typeflag) {
+ case FTW_DP:
+ rmdir(fpath);
+ l_debug("RMDIR %s", fpath);
+ break;
+
+ case FTW_SL:
+ default:
+ remove(fpath);
+ l_debug("RM %s", fpath);
+ break;
+ }
+ return 0;
+}
+
+
+void del_path(const char *path)
+{
+ nftw(path, del_fobject, 5, FTW_DEPTH | FTW_PHYS);
+}
diff --git a/mesh/util.h b/mesh/util.h
index d1e83b573..092d33041 100644
--- a/mesh/util.h
+++ b/mesh/util.h
@@ -23,3 +23,4 @@ bool str2hex(const char *str, uint16_t in_len, uint8_t *out,
size_t hex2str(uint8_t *in, size_t in_len, char *out, size_t out_len);
void print_packet(const char *label, const void *data, uint16_t size);
int create_dir(const char *dir_name);
+void del_path(const char *path);
--
2.21.1
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
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
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 | 278 ++++++++++++++++++++++++++++++++++++++++++++++++++
mesh/rpl.h | 30 ++++++
4 files changed, 323 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 9567d947e..19f3b87b7 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;
@@ -3771,8 +3769,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);
@@ -3784,6 +3785,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;
}
@@ -3799,6 +3801,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;
}
@@ -3813,6 +3817,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..01fecc276
--- /dev/null
+++ b/mesh/rpl.c
@@ -0,0 +1,278 @@
+/*
+ *
+ * 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);
+ if (sscanf(iv_txt, "%08x", &iv_index) != 1)
+ return;
+
+ 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) {
+ if (sscanf(entry->d_name, "%04hx", &src) == 1 &&
+ sscanf(seq_txt, "%06x", &seq) == 1) {
+
+ 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
Hi Brian,
I have a question about the way RPL is stored.
On Tue, Jan 28, 2020 at 06:32:58PM -0800, Brian Gix wrote:
> Mesh specification requires that Replay Protection be preserved
> across node restarts. This adds that storage in
> <node_uuid>/rpl/<iv_index>/<src>
Wouldn't it be more convinient to keep both iv_index and sequence in a file like this:
<node_uuid>/rpl/<src>
?
You could store them in bytes instead of hex string
and it would eliminate the need for cleaning entries from old IV_index.
Regards,
Rafał Gajda
Silvair
Hi Rafał,
On Wed, 2020-01-29 at 13:17 +0100, Rafal Gajda wrote:
> Hi Brian,
>
> I have a question about the way RPL is stored.
>
> On Tue, Jan 28, 2020 at 06:32:58PM -0800, Brian Gix wrote:
> > Mesh specification requires that Replay Protection be preserved
> > across node restarts. This adds that storage in
> > <node_uuid>/rpl/<iv_index>/<src>
>
> Wouldn't it be more convinient to keep both iv_index and sequence in a file like this:
> <node_uuid>/rpl/<src>
> ?
>
> You could store them in bytes instead of hex string
> and it would eliminate the need for cleaning entries from old IV_index.
We considered this and decided against it for ease of processing, as this method requires fewer file
operations. Cleaning old entries is something that will happen regardless of how the RPL tree looks in the
file system, as we delete entries that are older than (net->iv_index - 1) since we don't receive messages on
that iv_index, there is no possiblility of a Replay attack. And deleting a file system tree is pretty simple.
A SRC address does not get to keep it's spot in the RPL indefinitely... only over the current or prior
iv_index.
Our other considerations included maintaining the integrity of the RPL across power-loss or abort reboots.
However, we do recognize that some platforms may different NVM storage available that can be optimized in
different ways, so we tried to keep the NVM RPL apis as simple as possible to allow others to optimize the
storage as they see fit. For instance, if someone was to port this to an embedded system without a standard
linux file system.
On Wed, Jan 29, 2020 at 03:06:10PM +0000, Gix, Brian wrote:
> Hi Rafał,
>
> On Wed, 2020-01-29 at 13:17 +0100, Rafal Gajda wrote:
> > Hi Brian,
> >
> > I have a question about the way RPL is stored.
> >
> > On Tue, Jan 28, 2020 at 06:32:58PM -0800, Brian Gix wrote:
> > > Mesh specification requires that Replay Protection be preserved
> > > across node restarts. This adds that storage in
> > > <node_uuid>/rpl/<iv_index>/<src>
> >
> > Wouldn't it be more convinient to keep both iv_index and sequence in a file like this:
> > <node_uuid>/rpl/<src>
> > ?
> >
> > You could store them in bytes instead of hex string
> > and it would eliminate the need for cleaning entries from old IV_index.
>
>
> We considered this and decided against it for ease of processing, as this method requires fewer file
> operations. Cleaning old entries is something that will happen regardless of how the RPL tree looks in the
> file system, as we delete entries that are older than (net->iv_index - 1) since we don't receive messages on
> that iv_index, there is no possiblility of a Replay attack. And deleting a file system tree is pretty simple.
> A SRC address does not get to keep it's spot in the RPL indefinitely... only over the current or prior
> iv_index.
>
> Our other considerations included maintaining the integrity of the RPL across power-loss or abort reboots.
>
> However, we do recognize that some platforms may different NVM storage available that can be optimized in
> different ways, so we tried to keep the NVM RPL apis as simple as possible to allow others to optimize the
> storage as they see fit. For instance, if someone was to port this to an embedded system without a standard
> linux file system.
Ok, I understand. Thank you for the explanation.
Rafał Gajda
Silvair