2020-08-28 20:05:21

by Stotland, Inga

[permalink] [raw]
Subject: [PATCH BlueZ 0/2] Mesh replay protection clean up

This patch set moves RPL initialization to the node init sequence
instead of checking for every incoming message whether RPL has
been initialized.

Also, use consistent size check for RPL directory/file paths.


Inga Stotland (2):
mesh: Initialize RPL when creating or loading a node
mesh: Move RPL check from model to net layer

mesh/model.c | 18 ++---
mesh/model.h | 6 +-
mesh/net.c | 198 +++++++++++++++++++++++++++------------------------
mesh/net.h | 5 +-
mesh/node.c | 11 +--
mesh/rpl.c | 29 +++++---
mesh/rpl.h | 3 +-
7 files changed, 143 insertions(+), 127 deletions(-)

--
2.26.2


2020-08-28 20:05:22

by Stotland, Inga

[permalink] [raw]
Subject: [PATCH BlueZ 1/2] mesh: Initialize RPL when creating or loading a node

When either a new node is created or an existing node is loaded from
storage, initialize RPL storage directory.

Additionally, when an existing node configguration is read from storage,
load saved RPL entries into the corresponding RPL lists.
---
mesh/net.c | 14 +++++++-------
mesh/net.h | 1 +
mesh/node.c | 11 +++++++----
mesh/rpl.c | 29 ++++++++++++++++++++---------
mesh/rpl.h | 3 ++-
5 files changed, 37 insertions(+), 21 deletions(-)

diff --git a/mesh/net.c b/mesh/net.c
index 15d8f1c4b..83647d6ad 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -633,6 +633,7 @@ struct mesh_net *mesh_net_new(struct mesh_node *node)
net->frnd_msgs = l_queue_new();
net->destinations = l_queue_new();
net->app_keys = l_queue_new();
+ net->replay_cache = l_queue_new();

if (!nets)
nets = l_queue_new();
@@ -2596,7 +2597,7 @@ static void update_iv_ivu_state(struct mesh_net *net, uint32_t iv_index,
mesh_config_write_iv_index(cfg, iv_index, ivu);

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

node_property_changed(net->node, "IVIndex");
@@ -3480,12 +3481,6 @@ bool net_msg_check_replay_cache(struct mesh_net *net, uint16_t src,
if (!net || !net->node)
return true;

- 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);
- }
-
rpe = l_queue_find(net->replay_cache, match_replay_cache,
L_UINT_TO_PTR(src));

@@ -3688,3 +3683,8 @@ int mesh_net_set_heartbeat_pub(struct mesh_net *net, uint16_t dst,
/* TODO: Save to node config */
return MESH_STATUS_SUCCESS;
}
+
+bool mesh_net_load_rpl(struct mesh_net *net)
+{
+ return rpl_get_list(net->node, net->replay_cache);
+}
diff --git a/mesh/net.h b/mesh/net.h
index 253185e42..725054ce6 100644
--- a/mesh/net.h
+++ b/mesh/net.h
@@ -358,3 +358,4 @@ bool net_msg_check_replay_cache(struct mesh_net *net, uint16_t src,
uint16_t crpl, uint32_t seq, uint32_t iv_index);
void net_msg_add_replay_cache(struct mesh_net *net, uint16_t src, uint32_t seq,
uint32_t iv_index);
+bool mesh_net_load_rpl(struct mesh_net *net);
diff --git a/mesh/node.c b/mesh/node.c
index c564bdb91..f7ac5ff67 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -44,6 +44,7 @@
#include "mesh/dbus.h"
#include "mesh/agent.h"
#include "mesh/manager.h"
+#include "mesh/rpl.h"
#include "mesh/node.h"

#define MESH_NODE_PATH_PREFIX "/node"
@@ -399,7 +400,8 @@ static bool init_storage_dir(struct mesh_node *node)

node->storage_dir = l_strdup(dir_name);

- return true;
+ /* Initialize directory for storing RPL info */
+ return rpl_init(node->storage_dir);
}

static void update_net_settings(struct mesh_node *node)
@@ -470,6 +472,10 @@ static bool init_from_storage(struct mesh_config_node *db_node,

mesh_net_set_iv_index(node->net, db_node->iv_index, db_node->iv_update);

+ /* Initialize directory for storing keyring and RPL info */
+ if (!init_storage_dir(node) || !mesh_net_load_rpl(node->net))
+ goto fail;
+
if (db_node->net_transmit)
mesh_net_transmit_params_set(node->net,
db_node->net_transmit->count,
@@ -497,9 +503,6 @@ static bool init_from_storage(struct mesh_config_node *db_node,

node->cfg = cfg;

- /* Initialize directory for storing keyring info */
- init_storage_dir(node);
-
return true;
fail:
node_remove(node);
diff --git a/mesh/rpl.c b/mesh/rpl.c
index 5a667468e..792c52ad6 100644
--- a/mesh/rpl.c
+++ b/mesh/rpl.c
@@ -51,7 +51,7 @@ bool rpl_put_entry(struct mesh_node *node, uint16_t src, uint32_t iv_index,
DIR *dir;
int fd;

- if (!node || !IS_UNICAST(src))
+ if (!IS_UNICAST(src))
return false;

node_path = node_get_storage_dir(node);
@@ -100,7 +100,7 @@ void rpl_del_entry(struct mesh_node *node, uint16_t src)
struct dirent *entry;
DIR *dir;

- if (!node || !IS_UNICAST(src))
+ if (!IS_UNICAST(src))
return;

node_path = node_get_storage_dir(node);
@@ -205,12 +205,12 @@ bool rpl_get_list(struct mesh_node *node, struct l_queue *rpl_list)
size_t len;
DIR *dir;

- if (!node || !rpl_list)
+ if (!rpl_list)
return false;

node_path = node_get_storage_dir(node);

- len = strlen(node_path) + strlen(rpl_dir) + 14;
+ len = strlen(node_path) + strlen(rpl_dir) + 15;

if (len > PATH_MAX)
return false;
@@ -241,7 +241,7 @@ bool rpl_get_list(struct mesh_node *node, struct l_queue *rpl_list)
return true;
}

-void rpl_init(struct mesh_node *node, uint32_t cur)
+void rpl_update(struct mesh_node *node, uint32_t cur)
{
uint32_t old = cur - 1;
const char *node_path;
@@ -249,12 +249,11 @@ void rpl_init(struct mesh_node *node, uint32_t cur)
char path[PATH_MAX];
DIR *dir;

- if (!node)
- return;
-
node_path = node_get_storage_dir(node);
+ if (!node_path)
+ return;

- if (strlen(node_path) + strlen(rpl_dir) + 10 >= PATH_MAX)
+ if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX)
return;

/* Make sure path exists */
@@ -287,3 +286,15 @@ void rpl_init(struct mesh_node *node, uint32_t cur)

closedir(dir);
}
+
+bool rpl_init(const char *node_path)
+{
+ char path[PATH_MAX];
+
+ if (strlen(node_path) + strlen(rpl_dir) + 15 >= PATH_MAX)
+ return false;
+
+ snprintf(path, PATH_MAX, "%s%s", node_path, rpl_dir);
+ mkdir(path, 0755);
+ return true;
+}
diff --git a/mesh/rpl.h b/mesh/rpl.h
index 17d2e3f05..2bbbdc9b9 100644
--- a/mesh/rpl.h
+++ b/mesh/rpl.h
@@ -27,4 +27,5 @@ 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);
+void rpl_update(struct mesh_node *node, uint32_t iv_index);
+bool rpl_init(const char *node_path);
--
2.26.2

2020-08-28 20:05:22

by Stotland, Inga

[permalink] [raw]
Subject: [PATCH BlueZ 2/2] mesh: Move RPL check from model to net layer

Check whether an incoming message has an RPL entry prior to
handing it over to model layer for processing. If present in RPL
or the RPL queue is full, ignore the incoming message.

If the incoming message is processed successfully, add the message
as a new RPL entry.
---
mesh/model.c | 18 ++---
mesh/model.h | 6 +-
mesh/net.c | 184 +++++++++++++++++++++++++++------------------------
mesh/net.h | 4 --
4 files changed, 106 insertions(+), 106 deletions(-)

diff --git a/mesh/model.c b/mesh/model.c
index 7bbc9cca7..ed2a75215 100644
--- a/mesh/model.c
+++ b/mesh/model.c
@@ -862,9 +862,9 @@ static void send_msg_rcvd(struct mesh_node *node, uint8_t ele_idx,
}

bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
- uint32_t seq, uint32_t iv_index,
- uint16_t net_idx, uint16_t src, uint16_t dst,
- uint8_t key_aid, const uint8_t *data, uint16_t size)
+ uint32_t iv_index, uint16_t net_idx, uint16_t src,
+ uint16_t dst, uint8_t key_aid, const uint8_t *data,
+ uint16_t size)
{
uint8_t *clear_text;
struct mod_forward forward = {
@@ -877,7 +877,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, crpl;
+ uint16_t addr;
struct mesh_virtual *decrypt_virt = NULL;
bool result = false;
bool is_subscription;
@@ -892,12 +892,6 @@ bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
/* Unicast and not addressed to us */
return false;

- /* Don't process if already in RPL */
- crpl = node_get_crpl(node);
-
- if (net_msg_check_replay_cache(net, src, crpl, seq, iv_index))
- return false;
-
clear_text = l_malloc(size);
forward.data = clear_text;

@@ -988,10 +982,6 @@ bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
break;
}

- /* If message has been handled by us, add to RPL */
- if (result)
- net_msg_add_replay_cache(net, src, seq, iv_index);
-
done:
l_free(clear_text);

diff --git a/mesh/model.h b/mesh/model.h
index fe0648d04..2eec761a0 100644
--- a/mesh/model.h
+++ b/mesh/model.h
@@ -117,9 +117,9 @@ bool mesh_model_send(struct mesh_node *node, uint16_t src, uint16_t dst,
int mesh_model_publish(struct mesh_node *node, uint32_t id, uint16_t src,
bool segmented, uint16_t len, const void *data);
bool mesh_model_rx(struct mesh_node *node, bool szmict, uint32_t seq0,
- uint32_t seq, uint32_t iv_index, uint16_t net_idx,
- uint16_t src, uint16_t dst, uint8_t key_aid,
- const uint8_t *data, uint16_t size);
+ uint32_t iv_index, uint16_t net_idx, uint16_t src,
+ uint16_t dst, uint8_t key_aid, const uint8_t *data,
+ uint16_t size);
void mesh_model_app_key_delete(struct mesh_node *node, uint16_t ele_idx,
struct l_queue *models, uint16_t app_idx);
uint16_t mesh_model_opcode_set(uint32_t opcode, uint8_t *buf);
diff --git a/mesh/net.c b/mesh/net.c
index 83647d6ad..6c7311732 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -1621,6 +1621,91 @@ static void outseg_to(struct l_timeout *seg_timeout, void *user_data)
sar->seqZero, sar->last_nak);
}

+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;
+}
+
+static bool msg_check_replay_cache(struct mesh_net *net, 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;
+
+ rpe = l_queue_find(net->replay_cache, match_replay_cache,
+ L_UINT_TO_PTR(src));
+
+ if (rpe) {
+ if (iv_index > rpe->iv_index)
+ return false;
+
+ /* Return true if (iv_index | seq) too low */
+ if (iv_index < rpe->iv_index || seq <= rpe->seq) {
+ l_debug("Ignoring replayed packet");
+ return true;
+ }
+ } else if (l_queue_length(net->replay_cache) >= crpl) {
+ /* SRC not in Replay Cache... see if there is space for it */
+
+ int ret = l_queue_foreach_remove(net->replay_cache,
+ clean_old_iv_index, L_UINT_TO_PTR(iv_index));
+
+ /* Return true if no space could be freed */
+ if (!ret) {
+ l_debug("Replay cache full");
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void msg_add_replay_cache(struct mesh_net *net, uint16_t src,
+ uint32_t seq, uint32_t iv_index)
+{
+ struct mesh_rpl *rpe;
+
+ if (!net || !net->replay_cache)
+ return;
+
+ rpe = l_queue_remove_if(net->replay_cache, match_replay_cache,
+ L_UINT_TO_PTR(src));
+
+ if (!rpe) {
+ rpe = l_new(struct mesh_rpl, 1);
+ rpe->src = src;
+ }
+
+ rpe->seq = seq;
+ rpe->iv_index = iv_index;
+ rpl_put_entry(net->node, src, iv_index, seq);
+
+ /* Optimize so that most recent conversations stay earliest in cache */
+ l_queue_push_head(net->replay_cache, rpe);
+}
+
static bool msg_rxed(struct mesh_net *net, bool frnd, uint32_t iv_index,
uint8_t ttl, uint32_t seq,
uint16_t net_idx,
@@ -1630,6 +1715,7 @@ static bool msg_rxed(struct mesh_net *net, bool frnd, uint32_t iv_index,
const uint8_t *data, uint16_t size)
{
uint32_t seqAuth = seq_auth(seq, seqZero);
+ uint16_t crpl;

/* Sanity check seqAuth */
if (seqAuth > seq)
@@ -1671,8 +1757,19 @@ not_for_friend:
if (dst == PROXIES_ADDRESS && !net->proxy_enable)
return false;

- return mesh_model_rx(net->node, szmic, seqAuth, seq, iv_index, net_idx,
- src, dst, key_aid, data, size);
+ /* Don't process if already in RPL */
+ crpl = node_get_crpl(net->node);
+
+ if (msg_check_replay_cache(net, src, crpl, seq, iv_index))
+ return false;
+
+ if (!mesh_model_rx(net->node, szmic, seqAuth, iv_index, net_idx, src,
+ dst, key_aid, data, size))
+ return false;
+
+ /* If message has been handled by us, add to RPL */
+ msg_add_replay_cache(net, src, seq, iv_index);
+ return true;
}

static uint16_t key_id_to_net_idx(struct mesh_net *net, uint32_t key_id)
@@ -3448,89 +3545,6 @@ 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_check_replay_cache(struct mesh_net *net, 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;
-
- rpe = l_queue_find(net->replay_cache, match_replay_cache,
- L_UINT_TO_PTR(src));
-
- if (rpe) {
- if (iv_index > rpe->iv_index)
- return false;
-
- /* Return true if (iv_index | seq) too low */
- if (iv_index < rpe->iv_index || seq <= rpe->seq) {
- l_debug("Ignoring replayed packet");
- return true;
- }
- } else if (l_queue_length(net->replay_cache) >= crpl) {
- /* SRC not in Replay Cache... see if there is space for it */
-
- int ret = l_queue_foreach_remove(net->replay_cache,
- clean_old_iv_index, L_UINT_TO_PTR(iv_index));
-
- /* Return true if no space could be freed */
- if (!ret)
- return true;
- }
-
- return false;
-}
-
-void net_msg_add_replay_cache(struct mesh_net *net, uint16_t src, uint32_t seq,
- uint32_t iv_index)
-{
- struct mesh_rpl *rpe;
-
- if (!net || !net->replay_cache)
- return;
-
- rpe = l_queue_remove_if(net->replay_cache, match_replay_cache,
- L_UINT_TO_PTR(src));
-
- if (!rpe) {
- rpe = l_new(struct mesh_rpl, 1);
- rpe->src = src;
- }
-
- rpe->seq = seq;
- rpe->iv_index = iv_index;
- rpl_put_entry(net->node, src, iv_index, seq);
-
- /* Optimize so that most recent conversations stay earliest in cache */
- l_queue_push_head(net->replay_cache, rpe);
-}
-
static void hb_sub_timeout_func(struct l_timeout *timeout, void *user_data)
{
struct mesh_net *net = user_data;
diff --git a/mesh/net.h b/mesh/net.h
index 725054ce6..7bec6d0fc 100644
--- a/mesh/net.h
+++ b/mesh/net.h
@@ -354,8 +354,4 @@ 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_check_replay_cache(struct mesh_net *net, uint16_t src,
- uint16_t crpl, uint32_t seq, uint32_t iv_index);
-void net_msg_add_replay_cache(struct mesh_net *net, uint16_t src, uint32_t seq,
- uint32_t iv_index);
bool mesh_net_load_rpl(struct mesh_net *net);
--
2.26.2

2020-08-28 21:54:26

by Gix, Brian

[permalink] [raw]
Subject: Re: [PATCH BlueZ 0/2] Mesh replay protection clean up

Applied
On Fri, 2020-08-28 at 13:04 -0700, Inga Stotland wrote:
> This patch set moves RPL initialization to the node init sequence
> instead of checking for every incoming message whether RPL has
> been initialized.
>
> Also, use consistent size check for RPL directory/file paths.
>
>
> Inga Stotland (2):
> mesh: Initialize RPL when creating or loading a node
> mesh: Move RPL check from model to net layer
>
> mesh/model.c | 18 ++---
> mesh/model.h | 6 +-
> mesh/net.c | 198 +++++++++++++++++++++++++++------------------------
> mesh/net.h | 5 +-
> mesh/node.c | 11 +--
> mesh/rpl.c | 29 +++++---
> mesh/rpl.h | 3 +-
> 7 files changed, 143 insertions(+), 127 deletions(-)
>