summaryrefslogtreecommitdiff
path: root/mesh/net-keys.c
diff options
context:
space:
mode:
authorBrian Gix <brian.gix@intel.com>2019-12-13 12:15:05 -0800
committerBrian Gix <brian.gix@intel.com>2019-12-16 08:18:20 -0800
commit9955657fa222ba892c52daff910044dca019dadb (patch)
tree695dc54afbdaba83f94e0979ad47992b6671d2b3 /mesh/net-keys.c
parent3ed6e9f90c301536c332b52946347d6b70fb8d3c (diff)
downloadbluez-9955657fa222ba892c52daff910044dca019dadb.tar.gz
mesh: Refactor Secure Network Beaconing
The daemon handles multiple nodes, that may or may not be on the same mesh network. While each node my be seperately configured to beacon or not beacon, there is nothing gained (except redundent traffic) for each node to beacon seperately. Beaconing is therefore centralized with the Network Key the SNB represents, with each *received* beacon delivered to each node. But for SNBs generated, we keep a count of how many nodes want beacons sent for a specific key. If 1 or more, we beacon, if 0 nodes want the beacon sent, then we do not beacon.
Diffstat (limited to 'mesh/net-keys.c')
-rw-r--r--mesh/net-keys.c204
1 files changed, 197 insertions, 7 deletions
diff --git a/mesh/net-keys.c b/mesh/net-keys.c
index 5be7e0b58..65f0808dd 100644
--- a/mesh/net-keys.c
+++ b/mesh/net-keys.c
@@ -23,16 +23,35 @@
#include <ell/ell.h>
+#include "mesh/mesh-defs.h"
+#include "mesh/util.h"
#include "mesh/crypto.h"
+#include "mesh/mesh-io.h"
+#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 {
+ struct l_timeout *timeout;
+ uint32_t ts;
+ uint16_t observe_period;
+ uint16_t observed;
+ uint16_t expected;
+ bool half_period;
+ uint8_t beacon[23];
+};
+
struct net_key {
uint32_t id;
+ struct net_beacon snb;
uint16_t ref_cnt;
+ uint16_t beacon_enables;
uint8_t friend_key;
uint8_t nid;
uint8_t master[16];
@@ -157,6 +176,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_queue_remove(keys, key);
l_free(key);
}
@@ -305,18 +325,188 @@ bool net_key_snb_compose(uint32_t id, uint32_t iv_index, bool kr, bool ivu,
return false;
}
- snb[0] = BEACON_TYPE_SNB;
- snb[1] = 0;
+ snb[0] = MESH_AD_TYPE_BEACON;
+ snb[1] = BEACON_TYPE_SNB;
+ snb[2] = 0;
if (kr)
- snb[1] |= KEY_REFRESH;
+ snb[2] |= KEY_REFRESH;
if (ivu)
- snb[1] |= IV_INDEX_UPDATE;
+ snb[2] |= IV_INDEX_UPDATE;
- memcpy(snb + 2, key->network, 8);
- l_put_be32(iv_index, snb + 10);
- l_put_be64(cmac, snb + 14);
+ memcpy(snb + 3, key->network, 8);
+ l_put_be32(iv_index, snb + 11);
+ l_put_be64(cmac, snb + 15);
return true;
}
+
+static void send_network_beacon(struct net_key *key)
+{
+ struct mesh_io_send_info info = {
+ .type = MESH_IO_TIMING_TYPE_GENERAL,
+ .u.gen.interval = 100,
+ .u.gen.cnt = 1,
+ .u.gen.min_delay = DEFAULT_MIN_DELAY,
+ .u.gen.max_delay = DEFAULT_MAX_DELAY
+ };
+
+ mesh_io_send(NULL, &info, key->snb.beacon, sizeof(key->snb.beacon));
+}
+
+static void snb_timeout(struct l_timeout *timeout, void *user_data)
+{
+ struct net_key *key = user_data;
+ uint32_t interval, scale_factor;
+
+ /* Always send at least one beacon */
+ send_network_beacon(key);
+
+ /* Count our own beacons towards the vicinity total */
+ key->snb.observed++;
+
+ if (!key->snb.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);
+
+
+ interval = (key->snb.observe_period * key->snb.observed)
+ / key->snb.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;
+
+ /* Beaconing must be no *slower* than once every 10 minutes,
+ * and no *faster* than once every 10 seconds, per spec.
+ * Observation period is twice beaconing period.
+ */
+ if (interval < BEACON_INTERVAL_MIN * 2)
+ interval = BEACON_INTERVAL_MIN * 2;
+ else if (interval > BEACON_INTERVAL_MAX * 2)
+ interval = BEACON_INTERVAL_MAX * 2;
+
+ key->snb.observe_period = interval;
+ key->snb.observed = 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;
+ scale_factor = interval / 60;
+ key->snb.expected += scale_factor * 3;
+ }
+
+ interval = key->snb.observe_period / 2;
+ key->snb.ts = get_timestamp_secs();
+ key->snb.half_period = !key->snb.half_period;
+
+ if (key->beacon_enables)
+ l_timeout_modify(timeout, interval);
+ else {
+ l_timeout_remove(timeout);
+ key->snb.timeout = NULL;
+ }
+}
+
+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++;
+}
+
+void net_key_beacon_enable(uint32_t id)
+{
+ struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
+ bool enabled;
+ uint32_t rand_ms;
+
+ if (!key)
+ return;
+
+ enabled = !!key->beacon_enables;
+ key->beacon_enables++;
+
+ /* If already Enabled, do nothing */
+ if (enabled)
+ return;
+
+ /* Randomize first timeout to avoid bursts of beacons */
+ l_getrandom(&rand_ms, sizeof(rand_ms));
+ rand_ms %= (BEACON_INTERVAL_MIN * 1000);
+ rand_ms++;
+
+ /* Enable Periodic Beaconing on this key */
+ key->snb.ts = get_timestamp_secs();
+ 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);
+}
+
+bool net_key_beacon_refresh(uint32_t id, uint32_t iv_index, bool kr, bool ivu)
+{
+ 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;
+
+ if (!net_key_snb_compose(id, iv_index, kr, ivu, beacon))
+ return false;
+
+ if (memcmp(key->snb.beacon, beacon, sizeof(beacon)))
+ memcpy(key->snb.beacon, beacon, sizeof(beacon));
+ else
+ return false;
+
+ l_debug("Setting SNB: IVI: %8.8x, IVU: %d, KR: %d", iv_index, ivu, kr);
+ print_packet("Set SNB Beacon to", beacon, sizeof(beacon));
+
+ /* 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.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);
+
+ return true;
+}
+
+void net_key_beacon_disable(uint32_t id)
+{
+ struct net_key *key = l_queue_find(keys, match_id, L_UINT_TO_PTR(id));
+
+ if (!key || !key->beacon_enables)
+ return;
+
+ key->beacon_enables--;
+
+ if (key->beacon_enables)
+ return;
+
+ /* Disable periodic Beaconing on this key */
+ l_timeout_remove(key->snb.timeout);
+ key->snb.timeout = NULL;
+}