summaryrefslogtreecommitdiff
path: root/mesh
diff options
context:
space:
mode:
Diffstat (limited to 'mesh')
-rw-r--r--mesh/net-keys.c501
-rw-r--r--mesh/net-keys.h10
-rw-r--r--mesh/net.c172
-rw-r--r--mesh/net.h6
-rw-r--r--mesh/node.c4
5 files changed, 519 insertions, 174 deletions
diff --git a/mesh/net-keys.c b/mesh/net-keys.c
index ee7bbf0c0..0ba051d79 100644
--- a/mesh/net-keys.c
+++ b/mesh/net-keys.c
@@ -21,39 +21,55 @@
#include "mesh/net.h"
#include "mesh/net-keys.h"
-#define BEACON_TYPE_SNB 0x01
-#define KEY_REFRESH 0x01
-#define IV_INDEX_UPDATE 0x02
-
#define BEACON_INTERVAL_MIN 10
#define BEACON_INTERVAL_MAX 600
-struct net_beacon {
+/* This allows daemon to skip decryption on recently seen beacons */
+#define BEACON_CACHE_MAX 10
+
+struct beacon_rx {
+ uint8_t data[28];
+ uint32_t id;
+ uint32_t ivi;
+ bool kr;
+ bool ivu;
+};
+
+struct beacon_observe {
struct l_timeout *timeout;
uint32_t ts;
- uint16_t observe_period;
- uint16_t observed;
+ uint16_t period;
+ uint16_t seen;
uint16_t expected;
bool half_period;
- uint8_t beacon[23];
};
struct net_key {
uint32_t id;
- struct net_beacon snb;
+ struct l_timeout *mpb_to;
+ uint8_t *mpb;
+ uint8_t *snb;
+ struct beacon_observe observe;
+ uint32_t ivi;
uint16_t ref_cnt;
- uint16_t beacon_enables;
+ uint16_t mpb_enables;
+ uint16_t snb_enables;
+ uint8_t mpb_refresh;
uint8_t friend_key;
uint8_t nid;
uint8_t flooding[16];
- uint8_t encrypt[16];
- uint8_t privacy[16];
- uint8_t beacon[16];
- uint8_t network[8];
+ uint8_t enc_key[16];
+ uint8_t prv_key[16];
+ uint8_t snb_key[16];
+ uint8_t pvt_key[16];
+ uint8_t net_id[8];
+ bool kr;
+ bool ivu;
};
-static struct l_queue *keys = NULL;
-static uint32_t last_flooding_id = 0;
+static struct l_queue *beacons;
+static struct l_queue *keys;
+static uint32_t last_flooding_id;
/* To avoid re-decrypting same packet for multiple nodes, cache and check */
static uint8_t cache_pkt[29];
@@ -81,9 +97,9 @@ static bool match_id(const void *a, const void *b)
static bool match_network(const void *a, const void *b)
{
const struct net_key *key = a;
- const uint8_t *network = b;
+ const uint8_t *net_id = b;
- return memcmp(key->network, network, sizeof(key->network)) == 0;
+ return memcmp(key->net_id, net_id, sizeof(key->net_id)) == 0;
}
/* Key added from Provisioning, NetKey Add or NetKey update */
@@ -101,19 +117,26 @@ uint32_t net_key_add(const uint8_t flooding[16])
if (!keys)
keys = l_queue_new();
+ if (!beacons)
+ beacons = l_queue_new();
+
key = l_new(struct net_key, 1);
memcpy(key->flooding, flooding, 16);
key->ref_cnt++;
- result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->encrypt,
- key->privacy);
+ result = mesh_crypto_k2(flooding, p, sizeof(p), &key->nid, key->enc_key,
+ key->prv_key);
if (!result)
goto fail;
- result = mesh_crypto_k3(flooding, key->network);
+ result = mesh_crypto_k3(flooding, key->net_id);
if (!result)
goto fail;
- result = mesh_crypto_nkbk(flooding, key->beacon);
+ result = mesh_crypto_nkbk(flooding, key->snb_key);
+ if (!result)
+ goto fail;
+
+ result = mesh_crypto_nkpk(flooding, key->pvt_key);
if (!result)
goto fail;
@@ -146,7 +169,7 @@ uint32_t net_key_frnd_add(uint32_t flooding_id, uint16_t lpn, uint16_t frnd,
l_put_be16(fn_cnt, p + 7);
result = mesh_crypto_k2(key->flooding, p, sizeof(p), &frnd_key->nid,
- frnd_key->encrypt, frnd_key->privacy);
+ frnd_key->enc_key, frnd_key->prv_key);
if (!result) {
l_free(frnd_key);
@@ -167,7 +190,7 @@ void net_key_unref(uint32_t id)
if (key && key->ref_cnt) {
if (--key->ref_cnt == 0) {
- l_timeout_remove(key->snb.timeout);
+ l_timeout_remove(key->observe.timeout);
l_queue_remove(keys, key);
l_free(key);
}
@@ -206,7 +229,7 @@ static void decrypt_net_pkt(void *a, void *b)
result = mesh_crypto_packet_decode(cache_pkt, cache_len, false,
cache_plain, cache_iv_index,
- key->encrypt, key->privacy);
+ key->enc_key, key->prv_key);
if (result) {
cache_id = key->id;
@@ -254,8 +277,8 @@ bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len)
if (!key)
return false;
- result = mesh_crypto_packet_encode(pkt, len, iv_index, key->encrypt,
- key->privacy);
+ result = mesh_crypto_packet_encode(pkt, len, iv_index, key->enc_key,
+ key->prv_key);
if (!result)
return false;
@@ -265,9 +288,9 @@ bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len)
return result;
}
-uint32_t net_key_network_id(const uint8_t network[8])
+uint32_t net_key_network_id(const uint8_t net_id[8])
{
- struct net_key *key = l_queue_find(keys, match_network, network);
+ struct net_key *key = l_queue_find(keys, match_network, net_id);
if (!key)
return 0;
@@ -275,6 +298,55 @@ uint32_t net_key_network_id(const uint8_t network[8])
return key->id;
}
+struct auth_check {
+ const uint8_t *data;
+ uint32_t id;
+ uint32_t ivi;
+ bool ivu;
+ bool kr;
+};
+
+static void check_auth(void *a, void *b)
+{
+ struct net_key *key = a;
+ struct auth_check *auth = b;
+ uint8_t out[5];
+
+
+ /* Stop checking if already found */
+ if (auth->id)
+ return;
+
+ if (mesh_crypto_aes_ccm_decrypt(auth->data + 1, key->pvt_key, NULL, 0,
+ auth->data + 14, 13,
+ out, NULL, 8)) {
+ auth->id = key->id;
+ auth->ivi = l_get_be32(out + 1);
+ auth->ivu = !!(out[0] & 0x02);
+ auth->kr = !!(out[0] & 0x01);
+ }
+}
+
+static uint32_t private_beacon_check(const void *beacon, uint32_t *ivi,
+ bool *ivu, bool *kr)
+{
+ struct auth_check auth = {
+ .data = beacon,
+ .id = 0,
+ };
+
+ auth.id = 0;
+ l_queue_foreach(keys, check_auth, &auth);
+
+ if (auth.id) {
+ *ivi = auth.ivi;
+ *ivu = auth.ivu;
+ *kr = auth.kr;
+ }
+
+ return auth.id;
+}
+
bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
uint64_t cmac)
{
@@ -285,7 +357,7 @@ bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
return false;
/* Any behavioral changes must pass CMAC test */
- if (!mesh_crypto_beacon_cmac(key->beacon, key->network, iv_index, kr,
+ if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, iv_index, kr,
ivu, &cmac_check)) {
l_error("mesh_crypto_beacon_cmac failed");
return false;
@@ -300,39 +372,142 @@ bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
return true;
}
-bool net_key_snb_compose(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
- uint8_t *snb)
+static bool mpb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu)
+{
+ uint8_t b_data[5 + 8];
+ uint8_t random[13];
+
+ if (!key)
+ return false;
+
+ b_data[0] = 0;
+ l_put_be32(ivi, b_data + 1);
+
+ if (kr)
+ b_data[0] |= KEY_REFRESH;
+
+ if (ivu)
+ b_data[0] |= IV_INDEX_UPDATE;
+
+ l_getrandom(random, sizeof(random));
+ if (!mesh_crypto_aes_ccm_encrypt(random, key->pvt_key, NULL, 0,
+ b_data, 5, b_data, NULL, 8))
+ return false;
+
+ key->mpb[0] = MESH_AD_TYPE_BEACON;
+ key->mpb[1] = BEACON_TYPE_MPB;
+ memcpy(key->mpb + 2, random, 13);
+ memcpy(key->mpb + 15, b_data, 13);
+
+ return true;
+}
+
+static bool snb_compose(struct net_key *key, uint32_t ivi, bool kr, bool ivu)
{
- struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
uint64_t cmac;
if (!key)
return false;
/* Any behavioral changes must pass CMAC test */
- if (!mesh_crypto_beacon_cmac(key->beacon, key->network, iv_index, kr,
+ if (!mesh_crypto_beacon_cmac(key->snb_key, key->net_id, ivi, kr,
ivu, &cmac)) {
l_error("mesh_crypto_beacon_cmac failed");
return false;
}
- snb[0] = MESH_AD_TYPE_BEACON;
- snb[1] = BEACON_TYPE_SNB;
- snb[2] = 0;
+ key->snb[0] = MESH_AD_TYPE_BEACON;
+ key->snb[1] = BEACON_TYPE_SNB;
+ key->snb[2] = 0;
if (kr)
- snb[2] |= KEY_REFRESH;
+ key->snb[2] |= KEY_REFRESH;
if (ivu)
- snb[2] |= IV_INDEX_UPDATE;
+ key->snb[2] |= IV_INDEX_UPDATE;
- memcpy(snb + 3, key->network, 8);
- l_put_be32(iv_index, snb + 11);
- l_put_be64(cmac, snb + 15);
+ memcpy(key->snb + 3, key->net_id, 8);
+ l_put_be32(ivi, key->snb + 11);
+ l_put_be64(cmac, key->snb + 15);
return true;
}
+static bool match_beacon(const void *a, const void *b)
+{
+ const struct beacon_rx *cached = a;
+ const uint8_t *incoming = b;
+
+ if (incoming[0] == BEACON_TYPE_MPB)
+ return !memcmp(cached->data, incoming, 27);
+
+ if (incoming[0] == BEACON_TYPE_SNB)
+ return !memcmp(cached->data, incoming, 22);
+
+ return false;
+}
+
+uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi,
+ bool *ivu, bool *kr)
+{
+ struct net_key *key;
+ struct beacon_rx *beacon;
+ uint32_t b_id, b_ivi;
+ bool b_ivu, b_kr;
+
+ if (data[1] == BEACON_TYPE_SNB && len != 23)
+ return 0;
+
+ if (data[1] == BEACON_TYPE_MPB && len != 28)
+ return 0;
+
+ beacon = l_queue_remove_if(beacons, match_beacon, data + 1);
+
+ if (beacon)
+ goto accept;
+
+ /* Validate beacon data */
+ if (data[1] == BEACON_TYPE_SNB) {
+ key = l_queue_find(keys, match_network, data + 3);
+
+ if (!key)
+ return 0;
+
+ b_id = key->id;
+ b_ivu = !!(data[2] & 0x02);
+ b_kr = !!(data[2] & 0x01);
+ b_ivi = l_get_be32(data + 11);
+
+ if (!net_key_snb_check(b_id, b_ivi, b_kr, b_ivu,
+ l_get_be64(data + 15)))
+ return 0;
+
+ } else if (data[1] == BEACON_TYPE_MPB) {
+ b_id = private_beacon_check(data + 1, &b_ivi, &b_ivu, &b_kr);
+
+ if (!b_id)
+ return 0;
+
+ } else
+ return 0;
+
+ beacon = l_new(struct beacon_rx, 1);
+ memcpy(beacon->data, data + 1, len - 1);
+ beacon->id = b_id;
+ beacon->ivi = b_ivi;
+ beacon->ivu = b_ivu;
+ beacon->kr = b_kr;
+
+accept:
+ *ivi = beacon->ivi;
+ *ivu = beacon->ivu;
+ *kr = beacon->kr;
+
+ l_queue_push_head(beacons, beacon);
+
+ return beacon->id;
+}
+
static void send_network_beacon(struct net_key *key)
{
struct mesh_io_send_info info = {
@@ -343,10 +518,26 @@ static void send_network_beacon(struct net_key *key)
.u.gen.max_delay = DEFAULT_MAX_DELAY
};
- mesh_io_send(NULL, &info, key->snb.beacon, sizeof(key->snb.beacon));
+ if (key->mpb_enables) {
+ /* If Interval steps == 0, refresh key every time */
+ if (!key->mpb_refresh || !key->mpb || !key->mpb[0])
+ net_key_beacon_refresh(key->id, key->ivi, key->kr,
+ key->ivu, true);
+
+ mesh_io_send(NULL, &info, key->mpb, 28);
+ }
+
+ if (key->snb_enables) {
+ if (!key->snb || !key->snb[0]) {
+ net_key_beacon_refresh(key->id, key->ivi, key->kr,
+ key->ivu, true);
+ }
+
+ mesh_io_send(NULL, &info, key->snb, 23);
+ }
}
-static void snb_timeout(struct l_timeout *timeout, void *user_data)
+static void beacon_timeout(struct l_timeout *timeout, void *user_data)
{
struct net_key *key = user_data;
uint32_t interval, scale_factor;
@@ -355,29 +546,29 @@ static void snb_timeout(struct l_timeout *timeout, void *user_data)
send_network_beacon(key);
/* Count our own beacons towards the vicinity total */
- key->snb.observed++;
+ key->observe.seen++;
- if (!key->snb.half_period) {
+ if (!key->observe.half_period) {
l_debug("beacon %d for %d nodes, period %d, obs %d, exp %d",
- key->id,
- key->beacon_enables,
- key->snb.observe_period,
- key->snb.observed,
- key->snb.expected);
+ key->id,
+ key->snb_enables + key->mpb_enables,
+ key->observe.period,
+ key->observe.seen,
+ key->observe.expected);
- interval = (key->snb.observe_period * key->snb.observed)
- / key->snb.expected;
+ interval = (key->observe.period * key->observe.seen)
+ / key->observe.expected;
/* Limit Increases and Decreases by 10 seconds Up and
* 20 seconds down each step, to avoid going nearly silent
* in highly populated environments.
*/
- if (interval - 10 > key->snb.observe_period)
- interval = key->snb.observe_period + 10;
- else if (interval + 20 < key->snb.observe_period)
- interval = key->snb.observe_period - 20;
+ if (interval - 10 > key->observe.period)
+ interval = key->observe.period + 10;
+ else if (interval + 20 < key->observe.period)
+ interval = key->observe.period - 20;
/* Beaconing must be no *slower* than once every 10 minutes,
* and no *faster* than once every 10 seconds, per spec.
@@ -388,26 +579,26 @@ static void snb_timeout(struct l_timeout *timeout, void *user_data)
else if (interval > BEACON_INTERVAL_MAX * 2)
interval = BEACON_INTERVAL_MAX * 2;
- key->snb.observe_period = interval;
- key->snb.observed = 0;
+ key->observe.period = interval;
+ key->observe.seen = 0;
/* To prevent "over slowing" of the beaconing frequency,
* require more significant "over observing" the slower
* our own beaconing frequency.
*/
- key->snb.expected = interval / 10;
+ key->observe.expected = interval / 10;
scale_factor = interval / 60;
- key->snb.expected += scale_factor * 3;
+ key->observe.expected += scale_factor * 3;
}
- interval = key->snb.observe_period / 2;
- key->snb.half_period = !key->snb.half_period;
+ interval = key->observe.period / 2;
+ key->observe.half_period = !key->observe.half_period;
- if (key->beacon_enables)
+ if (key->mpb_enables || key->snb_enables)
l_timeout_modify(timeout, interval);
else {
l_timeout_remove(timeout);
- key->snb.timeout = NULL;
+ key->observe.timeout = NULL;
}
}
@@ -416,8 +607,8 @@ void net_key_beacon_seen(uint32_t id)
struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
if (key) {
- key->snb.observed++;
- key->snb.ts = get_timestamp_secs();
+ key->observe.seen++;
+ key->observe.ts = get_timestamp_secs();
}
}
@@ -426,12 +617,83 @@ uint32_t net_key_beacon_last_seen(uint32_t id)
struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
if (key)
- return key->snb.ts;
+ return key->observe.ts;
return 0;
}
-void net_key_beacon_enable(uint32_t id)
+bool net_key_beacon_refresh(uint32_t id, uint32_t ivi, bool kr, bool ivu,
+ bool force)
+{
+ struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
+ bool refresh = force;
+ uint32_t rand_ms;
+
+ if (!key)
+ return false;
+
+ if (key->snb_enables && !key->snb) {
+ key->snb = l_new(uint8_t, 23);
+ refresh = true;
+ }
+
+ if (key->mpb_enables && !key->mpb) {
+ key->mpb = l_new(uint8_t, 28);
+ refresh = true;
+ }
+
+ if (key->ivi != ivi || key->ivu != ivu || key->kr != kr)
+ refresh = true;
+
+ if (!refresh)
+ return true;
+
+ if (key->mpb) {
+ if (!mpb_compose(key, ivi, kr, ivu))
+ return false;
+
+ print_packet("Set MPB to", key->mpb, 28);
+ }
+
+ if (key->snb) {
+ if (!snb_compose(key, ivi, kr, ivu))
+ return false;
+
+ print_packet("Set SNB to", key->snb, 23);
+ }
+
+ l_debug("Set Beacon: IVI: %8.8x, IVU: %d, KR: %d", ivi, ivu, kr);
+
+ /* Propagate changes to all local nodes */
+ net_local_beacon(id, ivi, ivu, kr);
+
+ /* Send one new SNB soon, after all nodes have seen it */
+ l_getrandom(&rand_ms, sizeof(rand_ms));
+ rand_ms %= 1000;
+ key->observe.expected++;
+
+ if (key->observe.timeout)
+ l_timeout_modify_ms(key->observe.timeout, 500 + rand_ms);
+ else
+ key->observe.timeout = l_timeout_create_ms(500 + rand_ms,
+ beacon_timeout, key, NULL);
+
+ return true;
+}
+
+static void mpb_timeout(struct l_timeout *timeout, void *user_data)
+{
+ struct net_key *key = user_data;
+
+ if (key->mpb_refresh) {
+ l_debug("Refresh in %d seconds", key->mpb_refresh * 10);
+ l_timeout_modify(timeout, key->mpb_refresh * 10);
+ }
+
+ net_key_beacon_refresh(key->id, key->ivi, key->kr, key->ivu, true);
+}
+
+void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count)
{
struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
bool enabled;
@@ -440,8 +702,19 @@ void net_key_beacon_enable(uint32_t id)
if (!key)
return;
- enabled = !!key->beacon_enables;
- key->beacon_enables++;
+ enabled = !!key->snb_enables || !!key->mpb_enables;
+
+ if (mpb) {
+ key->mpb_enables++;
+ key->mpb_refresh = refresh_count;
+ l_timeout_remove(key->mpb_to);
+ if (refresh_count)
+ key->mpb_to = l_timeout_create(refresh_count * 10,
+ mpb_timeout, key, NULL);
+ else
+ key->mpb_to = NULL;
+ } else
+ key->snb_enables++;
/* If already Enabled, do nothing */
if (enabled)
@@ -453,70 +726,68 @@ void net_key_beacon_enable(uint32_t id)
rand_ms++;
/* Enable Periodic Beaconing on this key */
- key->snb.observe_period = BEACON_INTERVAL_MIN * 2;
- key->snb.expected = 2;
- key->snb.observed = 0;
- key->snb.half_period = true;
- l_timeout_remove(key->snb.timeout);
- key->snb.timeout = l_timeout_create_ms(rand_ms, snb_timeout, key, NULL);
+ key->observe.period = BEACON_INTERVAL_MIN * 2;
+ key->observe.expected = 2;
+ key->observe.seen = 0;
+ key->observe.half_period = true;
+ l_timeout_remove(key->observe.timeout);
+ key->observe.timeout = l_timeout_create_ms(rand_ms, beacon_timeout,
+ key, NULL);
}
-bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu)
+void net_key_beacon_disable(uint32_t id, bool mpb)
{
struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
- uint8_t beacon[23];
- uint32_t rand_ms;
if (!key)
- return false;
+ return;
- if (!net_key_snb_compose(id, iv_index, kr, ivu, beacon))
- return false;
+ if (mpb) {
+ if (!key->mpb_enables)
+ return;
- if (memcmp(key->snb.beacon, beacon, sizeof(beacon)))
- memcpy(key->snb.beacon, beacon, sizeof(beacon));
- else
- return false;
+ key->mpb_enables--;
- l_debug("Setting SNB: IVI: %8.8x, IVU: %d, KR: %d", iv_index, ivu, kr);
- print_packet("Set SNB Beacon to", beacon, sizeof(beacon));
+ if (!key->mpb_enables) {
+ l_free(key->mpb);
+ key->mpb = NULL;
+ l_timeout_remove(key->mpb_to);
+ key->mpb_to = NULL;
+ }
+ } else {
+ if (!key->snb_enables)
+ return;
- /* Propagate changes to all local nodes */
- net_local_beacon(id, beacon);
+ key->snb_enables--;
- /* Send one new SNB soon, after all nodes have seen it */
- l_getrandom(&rand_ms, sizeof(rand_ms));
- rand_ms %= 1000;
- key->snb.expected++;
+ if (!key->snb_enables) {
+ l_free(key->snb);
+ key->snb = NULL;
+ }
+ }
- if (key->snb.timeout)
- l_timeout_modify_ms(key->snb.timeout, 500 + rand_ms);
- else
- key->snb.timeout = l_timeout_create_ms(500 + rand_ms,
- snb_timeout, key, NULL);
+ if (key->snb_enables || key->mpb_enables)
+ return;
- return true;
+ /* Disable periodic Beaconing on this key */
+ l_timeout_remove(key->observe.timeout);
+ key->observe.timeout = NULL;
}
-void net_key_beacon_disable(uint32_t id)
+static void free_key(void *data)
{
- struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
-
- if (!key || !key->beacon_enables)
- return;
-
- key->beacon_enables--;
+ struct net_key *key = data;
- if (key->beacon_enables)
- return;
-
- /* Disable periodic Beaconing on this key */
- l_timeout_remove(key->snb.timeout);
- key->snb.timeout = NULL;
+ l_timeout_remove(key->mpb_to);
+ l_free(key->snb);
+ l_free(key->mpb);
+ l_free(key);
}
void net_key_cleanup(void)
{
- l_queue_destroy(keys, l_free);
+ l_queue_destroy(keys, free_key);
keys = NULL;
+ l_queue_destroy(beacons, l_free);
+ beacons = NULL;
}
diff --git a/mesh/net-keys.h b/mesh/net-keys.h
index 420618f71..a3909448c 100644
--- a/mesh/net-keys.h
+++ b/mesh/net-keys.h
@@ -9,6 +9,7 @@
*/
#define BEACON_TYPE_SNB 0x01
+#define BEACON_TYPE_MPB 0x02
#define KEY_REFRESH 0x01
#define IV_INDEX_UPDATE 0x02
@@ -23,12 +24,15 @@ uint32_t net_key_decrypt(uint32_t iv_index, const uint8_t *pkt, size_t len,
uint8_t **plain, size_t *plain_len);
bool net_key_encrypt(uint32_t id, uint32_t iv_index, uint8_t *pkt, size_t len);
uint32_t net_key_network_id(const uint8_t network[8]);
+uint32_t net_key_beacon(const uint8_t *data, uint16_t len, uint32_t *ivi,
+ bool *ivu, bool *kr);
bool net_key_snb_check(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
uint64_t cmac);
bool net_key_snb_compose(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
uint8_t *snb);
void net_key_beacon_seen(uint32_t id);
-void net_key_beacon_enable(uint32_t id);
-bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu);
-void net_key_beacon_disable(uint32_t id);
+bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
+ bool force);
+void net_key_beacon_enable(uint32_t id, bool mpb, uint8_t refresh_count);
+void net_key_beacon_disable(uint32_t id, bool mpb);
uint32_t net_key_beacon_last_seen(uint32_t id);
diff --git a/mesh/net.c b/mesh/net.c
index 1d27289bf..81f1e57ee 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -102,7 +102,8 @@ struct mesh_net {
unsigned int sar_id_next;
bool friend_enable;
- bool beacon_enable;
+ bool snb_enable;
+ bool mpb_enable;
bool proxy_enable;
bool friend_seq;
struct l_timeout *iv_update_timeout;
@@ -119,6 +120,7 @@ struct mesh_net {
uint8_t chan; /* Channel of recent Rx */
uint8_t default_ttl;
uint8_t tid;
+ uint8_t mpb_period;
struct {
bool enable;
@@ -217,6 +219,7 @@ struct net_beacon_data {
bool ivu;
bool kr;
bool processed;
+ bool local;
};
static struct l_queue *fast_cache;
@@ -526,6 +529,13 @@ static void mesh_sar_free(void *data)
static void subnet_free(void *data)
{
struct mesh_subnet *subnet = data;
+ struct mesh_net *net = subnet->net;
+
+ if (net->snb_enable)
+ net_key_beacon_disable(subnet->net_key_tx, false);
+
+ if (net->mpb_enable)
+ net_key_beacon_disable(subnet->net_key_tx, true);
net_key_unref(subnet->net_key_cur);
net_key_unref(subnet->net_key_upd);
@@ -545,15 +555,27 @@ static struct mesh_subnet *subnet_new(struct mesh_net *net, uint16_t idx)
return subnet;
}
-static void enable_beacon(void *a, void *b)
+static void enable_snb(void *a, void *b)
{
struct mesh_subnet *subnet = a;
struct mesh_net *net = b;
- if (net->beacon_enable)
- net_key_beacon_enable(subnet->net_key_tx);
+ if (net->snb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, false, 0);
else
- net_key_beacon_disable(subnet->net_key_tx);
+ net_key_beacon_disable(subnet->net_key_tx, false);
+}
+
+static void enable_mpb(void *a, void *b)
+{
+ struct mesh_subnet *subnet = a;
+ struct mesh_net *net = b;
+
+ if (net->mpb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, true,
+ net->mpb_period);
+ else
+ net_key_beacon_disable(subnet->net_key_tx, true);
}
static void enqueue_update(void *a, void *b);
@@ -602,7 +624,8 @@ static void refresh_beacon(void *a, void *b)
struct mesh_net *net = b;
net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
- !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update);
+ !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update,
+ false);
}
struct mesh_net *mesh_net_new(struct mesh_node *node)
@@ -826,7 +849,7 @@ int mesh_net_del_key(struct mesh_net *net, uint16_t idx)
if (idx == net->hb_pub.net_idx)
net->hb_pub.dst = UNASSIGNED_ADDRESS;
- /* TODO: cancel beacon_enable on this subnet */
+ /* TODO: cancel snb_enable on this subnet */
l_queue_remove(net->subnets, subnet);
subnet_free(subnet);
@@ -853,10 +876,14 @@ static struct mesh_subnet *add_key(struct mesh_net *net, uint16_t idx,
}
net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
- false, net->iv_update);
+ false, net->iv_update, false);
- if (net->beacon_enable)
- net_key_beacon_enable(subnet->net_key_tx);
+ if (net->snb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, false, 0);
+
+ if (net->mpb_enable)
+ net_key_beacon_enable(subnet->net_key_tx, true,
+ net->mpb_period);
l_queue_push_tail(net->subnets, subnet);
@@ -2794,83 +2821,111 @@ static void process_beacon(void *net_ptr, void *user_data)
beacon_data->processed = true;
/*
- * Ignore the beacon if it doesn't change anything, unless we're
- * doing IV Recovery
+ * Ignore local beacons and beacons that don't change anything,
+ * unless we're doing IV Recovery
*/
- if (net->iv_upd_state == IV_UPD_INIT || ivi != net->iv_index ||
+ if (!beacon_data->local) {
+ if (net->iv_upd_state == IV_UPD_INIT || ivi != net->iv_index ||
ivu != net->iv_update)
- updated |= update_iv_ivu_state(net, ivi, ivu);
+ updated |= update_iv_ivu_state(net, ivi, ivu);
+
+ if (kr != local_kr)
+ updated |= update_kr_state(subnet, kr,
+ beacon_data->net_key_id);
- if (kr != local_kr || beacon_data->net_key_id != subnet->net_key_cur)
- updated |= update_kr_state(subnet, kr, beacon_data->net_key_id);
- if (updated)
- net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
+ if (updated)
+ net_key_beacon_refresh(beacon_data->net_key_id,
+ net->iv_index,
!!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO),
- net->iv_update);
+ net->iv_update, false);
+ }
}
static void beacon_recv(void *user_data, struct mesh_io_recv_info *info,
const uint8_t *data, uint16_t len)
{
struct net_beacon_data beacon_data = {
+ .local = false,
.processed = false,
};
- if (len != 23 || data[1] != 0x01)
- return;
+ beacon_data.net_key_id = net_key_beacon(data, len, &beacon_data.ivi,
+ &beacon_data.ivu, &beacon_data.kr);
- /* Ignore Network IDs unknown to this daemon */
- beacon_data.net_key_id = net_key_network_id(data + 3);
if (!beacon_data.net_key_id)
return;
- /* Get data bits from beacon */
- beacon_data.ivu = !!(data[2] & 0x02);
- beacon_data.kr = !!(data[2] & 0x01);
- beacon_data.ivi = l_get_be32(data + 11);
-
- /* Validate beacon before accepting */
- if (!net_key_snb_check(beacon_data.net_key_id, beacon_data.ivi,
- beacon_data.kr, beacon_data.ivu,
- l_get_be64(data + 15))) {
- l_error("mesh_crypto_beacon verify failed");
- return;
- }
-
l_queue_foreach(nets, process_beacon, &beacon_data);
if (beacon_data.processed)
net_key_beacon_seen(beacon_data.net_key_id);
}
-void net_local_beacon(uint32_t net_key_id, uint8_t *beacon)
+void net_local_beacon(uint32_t net_key_id, uint32_t ivi, bool ivu, bool kr)
{
struct net_beacon_data beacon_data = {
+ .local = true,
+ .processed = false,
.net_key_id = net_key_id,
- .ivu = !!(beacon[2] & 0x02),
- .kr = !!(beacon[2] & 0x01),
- .ivi = l_get_be32(beacon + 11),
+ .ivu = ivu,
+ .kr = kr,
+ .ivi = ivi,
};
/* Deliver locally generated beacons to all nodes */
l_queue_foreach(nets, process_beacon, &beacon_data);
}
-bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable)
+bool mesh_net_set_snb_mode(struct mesh_net *net, bool enable)
{
if (!net)
return false;
- if (net->beacon_enable == enable)
+ if (net->snb_enable == enable)
return true;
- net->beacon_enable = enable;
+ net->snb_enable = enable;
if (enable)
l_queue_foreach(net->subnets, refresh_beacon, net);
- l_queue_foreach(net->subnets, enable_beacon, net);
+ l_queue_foreach(net->subnets, enable_snb, net);
+ queue_friend_update(net);
+
+ return true;
+}
+
+bool mesh_net_set_mpb_mode(struct mesh_net *net, bool enable, uint8_t period,
+ bool initialize)
+{
+ uint8_t old_period;
+ bool old_enable;
+
+ if (!net)
+ return false;
+
+ old_enable = net->mpb_enable;
+ old_period = net->mpb_period;
+
+ if (enable)
+ net->mpb_period = period;
+
+ if (old_enable == enable && old_period == net->mpb_period)
+ return true;
+
+ if (enable && !initialize) {
+ /* If enable with different period, disable and re-enable */
+ net->mpb_enable = false;
+ l_queue_foreach(net->subnets, enable_mpb, net);
+ }
+
+ net->mpb_enable = enable;
+
+ if (enable)
+ l_queue_foreach(net->subnets, refresh_beacon, net);
+
+ l_queue_foreach(net->subnets, enable_mpb, net);
queue_friend_update(net);
return true;
@@ -2908,17 +2963,25 @@ bool mesh_net_set_key(struct mesh_net *net, uint16_t idx, const uint8_t *key,
subnet->key_refresh = 1;
subnet->net_key_tx = subnet->net_key_upd;
- if (net->beacon_enable) {
+ if (net->snb_enable) {
/* Switch beaconing key */
- net_key_beacon_disable(subnet->net_key_cur);
- net_key_beacon_enable(subnet->net_key_upd);
+ net_key_beacon_disable(subnet->net_key_cur, false);
+ net_key_beacon_enable(subnet->net_key_upd, false, 0);
+ }
+
+ if (net->mpb_enable) {
+ /* Switch beaconing key */
+ net_key_beacon_disable(subnet->net_key_cur, true);
+ net_key_beacon_enable(subnet->net_key_upd, true,
+ net->mpb_period);
}
}
subnet->kr_phase = phase;
net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
- !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update);
+ !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update,
+ false);
return true;
@@ -2933,8 +2996,9 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)
first = l_queue_isempty(nets);
if (first) {
- uint8_t snb[] = {MESH_AD_TYPE_BEACON, 0x01};
- uint8_t pkt[] = {MESH_AD_TYPE_NETWORK};
+ const uint8_t snb[] = {MESH_AD_TYPE_BEACON, 1};
+ const uint8_t mpb[] = {MESH_AD_TYPE_BEACON, 2};
+ const uint8_t pkt[] = {MESH_AD_TYPE_NETWORK};
if (!nets)
nets = l_queue_new();
@@ -2944,6 +3008,8 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)
mesh_io_register_recv_cb(io, snb, sizeof(snb),
beacon_recv, NULL);
+ mesh_io_register_recv_cb(io, mpb, sizeof(mpb),
+ beacon_recv, NULL);
mesh_io_register_recv_cb(io, pkt, sizeof(pkt),
net_msg_recv, NULL);
}
@@ -2960,8 +3026,9 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)
struct mesh_io *mesh_net_detach(struct mesh_net *net)
{
- uint8_t snb[] = {MESH_AD_TYPE_BEACON, 0x01};
- uint8_t pkt[] = {MESH_AD_TYPE_NETWORK};
+ const uint8_t snb[] = {MESH_AD_TYPE_BEACON, 1};
+ const uint8_t mpb[] = {MESH_AD_TYPE_BEACON, 2};
+ const uint8_t pkt[] = {MESH_AD_TYPE_NETWORK};
struct mesh_io *io;
uint8_t type = 0;
@@ -2975,6 +3042,7 @@ struct mesh_io *mesh_net_detach(struct mesh_net *net)
/* Only deregister io if this is the last network detached.*/
if (l_queue_length(nets) < 2) {
mesh_io_deregister_recv_cb(io, snb, sizeof(snb));
+ mesh_io_deregister_recv_cb(io, mpb, sizeof(mpb));
mesh_io_deregister_recv_cb(io, pkt, sizeof(pkt));
}
diff --git a/mesh/net.h b/mesh/net.h
index 0bacbbbbf..d385ba16e 100644
--- a/mesh/net.h
+++ b/mesh/net.h
@@ -236,8 +236,10 @@ void mesh_net_set_frnd_seq(struct mesh_net *net, bool seq);
uint16_t mesh_net_get_address(struct mesh_net *net);
bool mesh_net_register_unicast(struct mesh_net *net,
uint16_t unicast, uint8_t num_ele);
-void net_local_beacon(uint32_t net_key_id, uint8_t *beacon);
-bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable);
+void net_local_beacon(uint32_t key_id, uint32_t ivi, bool ivu, bool kr);
+bool mesh_net_set_snb_mode(struct mesh_net *net, bool enable);
+bool mesh_net_set_mpb_mode(struct mesh_net *net, bool enabla, uint8_t period,
+ bool init);
bool mesh_net_set_proxy_mode(struct mesh_net *net, bool enable);
bool mesh_net_set_relay_mode(struct mesh_net *net, bool enable, uint8_t cnt,
uint8_t interval);
diff --git a/mesh/node.c b/mesh/node.c
index 5150a085a..a2a330518 100644
--- a/mesh/node.c
+++ b/mesh/node.c
@@ -414,7 +414,7 @@ static void update_net_settings(struct mesh_node *node)
mesh_net_set_relay_mode(net, node->relay.mode == MESH_MODE_ENABLED,
node->relay.cnt, node->relay.interval);
- mesh_net_set_beacon_mode(net, node->beacon == MESH_MODE_ENABLED);
+ mesh_net_set_snb_mode(net, node->beacon == MESH_MODE_ENABLED);
}
static bool init_from_storage(struct mesh_config_node *db_node,
@@ -825,7 +825,7 @@ bool node_beacon_mode_set(struct mesh_node *node, bool enable)
if (res) {
node->beacon = beacon;
- mesh_net_set_beacon_mode(node->net, enable);
+ mesh_net_set_snb_mode(node->net, enable);
}
return res;