Diff between 3ed6e9f90c301536c332b52946347d6b70fb8d3c and 9955657fa222ba892c52daff910044dca019dadb

Changed Files

File Additions Deletions Status
mesh/mesh-io.c +3 -0 modified
mesh/net-keys.c +197 -7 modified
mesh/net-keys.h +8 -0 modified
mesh/net.c +115 -235 modified
mesh/net.h +3 -0 modified

Full Patch

diff --git a/mesh/mesh-io.c b/mesh/mesh-io.c
index 95a99b6..1ab173d 100644
--- a/mesh/mesh-io.c
+++ b/mesh/mesh-io.c
@@ -171,6 +171,9 @@ bool mesh_io_send(struct mesh_io *io, struct mesh_io_send_info *info,
 {
 	io = l_queue_find(io_list, match_by_io, io);
 
+	if (!io)
+		io = l_queue_peek_head(io_list);
+
 	if (io && io->api && io->api->send)
 		return io->api->send(io, info, data, len);
 
diff --git a/mesh/net-keys.c b/mesh/net-keys.c
index 5be7e0b..65f0808 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;
+}
diff --git a/mesh/net-keys.h b/mesh/net-keys.h
index d54c521..3c2c4d0 100644
--- a/mesh/net-keys.h
+++ b/mesh/net-keys.h
@@ -17,6 +17,10 @@
  *
  */
 
+#define BEACON_TYPE_SNB		0x01
+#define KEY_REFRESH		0x01
+#define IV_INDEX_UPDATE		0x02
+
 bool net_key_confirm(uint32_t id, const uint8_t master[16]);
 bool net_key_retrieve(uint32_t id, uint8_t *master);
 uint32_t net_key_add(const uint8_t master[16]);
@@ -31,3 +35,7 @@ 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);
diff --git a/mesh/net.c b/mesh/net.c
index f662d8a..e708b2a 100644
--- a/mesh/net.c
+++ b/mesh/net.c
@@ -53,15 +53,9 @@
 #define SEG_TO	2
 #define MSG_TO	60
 
-#define DEFAULT_MIN_DELAY		0
-#define DEFAULT_MAX_DELAY		25
-
 #define DEFAULT_TRANSMIT_COUNT		1
 #define DEFAULT_TRANSMIT_INTERVAL	100
 
-#define BEACON_INTERVAL_MIN	10
-#define BEACON_INTERVAL_MAX	600
-
 #define SAR_KEY(src, seq0)	((((uint32_t)(seq0)) << 16) | (src))
 
 enum _relay_advice {
@@ -90,23 +84,12 @@ struct net_key {
 	uint8_t network_id[8];
 };
 
-struct mesh_beacon {
-	struct l_timeout *timeout;
-	uint32_t ts;
-	uint16_t observe_period;
-	uint16_t observed;
-	uint16_t expected;
-	uint8_t half_period;
-	uint8_t beacon[23];
-};
-
 struct mesh_subnet {
 	struct mesh_net *net;
 	uint16_t idx;
 	uint32_t net_key_tx;
 	uint32_t net_key_cur;
 	uint32_t net_key_upd;
-	struct mesh_beacon snb;
 	uint8_t key_refresh;
 	uint8_t kr_phase;
 };
@@ -260,8 +243,11 @@ struct net_queue_data {
 };
 
 struct net_beacon_data {
-	const uint8_t *data;
-	uint16_t len;
+	uint32_t key_id;
+	uint32_t ivi;
+	bool ivu;
+	bool kr;
+	bool processed;
 };
 
 #define FAST_CACHE_SIZE 8
@@ -570,9 +556,6 @@ static void subnet_free(void *data)
 	l_free(subnet);
 }
 
-static void lpn_process_beacon(void *user_data, const void *data, uint8_t size,
-								int8_t rssi);
-
 static struct mesh_subnet *subnet_new(struct mesh_net *net, uint16_t idx)
 {
 	struct mesh_subnet *subnet;
@@ -583,93 +566,71 @@ static struct mesh_subnet *subnet_new(struct mesh_net *net, uint16_t idx)
 
 	subnet->net = net;
 	subnet->idx = idx;
-	subnet->snb.beacon[0] = MESH_AD_TYPE_BEACON;
 	return subnet;
 }
 
-static bool create_secure_beacon(struct mesh_net *net,
-					struct mesh_subnet *subnet,
-					uint8_t *beacon_data)
+static void enable_beacon(void *a, void *b)
 {
-	bool kr = (subnet->kr_phase == KEY_REFRESH_PHASE_TWO);
+	struct mesh_subnet *subnet = a;
+	struct mesh_net *net = b;
 
-	return net_key_snb_compose(subnet->net_key_tx, net->iv_index,
-					kr, net->iv_update, beacon_data);
+	if (net->beacon_enable)
+		net_key_beacon_enable(subnet->net_key_tx);
+	else
+		net_key_beacon_disable(subnet->net_key_tx);
 }
 
-static void send_network_beacon(struct mesh_subnet *subnet,
-							struct mesh_net *net)
+static void enqueue_update(void *a, void *b);
+
+static void queue_friend_update(struct mesh_net *net)
 {
-	struct mesh_io_send_info info = {
-		.type = MESH_IO_TIMING_TYPE_GENERAL,
-		.u.gen.interval = net->tx_interval,
-		.u.gen.cnt = 1,
-		.u.gen.min_delay = DEFAULT_MIN_DELAY,
-		.u.gen.max_delay = DEFAULT_MAX_DELAY
-	};
+	struct mesh_subnet *subnet;
+	struct mesh_friend *frnd;
+	uint8_t flags = 0;
 
-	l_info("Send SNB on network %3.3x", subnet->idx);
-	mesh_io_send(net->io, &info, subnet->snb.beacon,
-						sizeof(subnet->snb.beacon));
-}
+	if (l_queue_length(net->friends)) {
+		struct mesh_friend_msg update = {
+			.src = net->src_addr,
+			.iv_index = mesh_net_get_iv_index(net),
+			.last_len = 7,
+			.ctl = true,
+		};
 
-static void network_beacon_timeout(struct l_timeout *timeout, void *user_data)
-{
-	struct mesh_subnet *subnet = user_data;
-	uint32_t interval;
+		frnd = l_queue_peek_head(net->friends);
+		subnet = l_queue_find(net->subnets, match_key_index,
+						L_UINT_TO_PTR(frnd->net_idx));
 
-	send_network_beacon(subnet, subnet->net);
+		if (!subnet)
+			return;
 
-	if (!subnet->snb.half_period) {
-		l_debug("beacon TO period %d, observed %d, expected %d",
-						subnet->snb.observe_period,
-						subnet->snb.observed,
-						subnet->snb.expected);
-		interval = subnet->snb.observe_period *
-			(subnet->snb.observed + 1) / subnet->snb.expected;
-		subnet->snb.observe_period = interval * 2;
-		subnet->snb.expected = subnet->snb.observe_period / 10;
-		subnet->snb.observed = 0;
-	} else
-		interval = subnet->snb.observe_period / 2;
+		if (subnet->kr_phase == KEY_REFRESH_PHASE_TWO)
+			flags |= KEY_REFRESH;
 
-	if (interval < BEACON_INTERVAL_MIN)
-		interval = BEACON_INTERVAL_MIN;
+		if (net->iv_update)
+			flags |= IV_INDEX_UPDATE;
 
-	if (interval > BEACON_INTERVAL_MAX)
-		interval = BEACON_INTERVAL_MAX;
+		update.u.one[0].hdr = NET_OP_FRND_UPDATE << OPCODE_HDR_SHIFT;
+		update.u.one[0].seq = mesh_net_next_seq_num(net);
+		update.u.one[0].data[0] = NET_OP_FRND_UPDATE;
+		update.u.one[0].data[1] = flags;
+		l_put_be32(net->iv_index, update.u.one[0].data + 2);
+		update.u.one[0].data[6] = 0x01; /* More Data */
+		/* print_packet("Frnd-Beacon-SRC",
+		 *			beacon_data, sizeof(beacon_data));
+		 */
+		/* print_packet("Frnd-Update", update.u.one[0].data, 6); */
 
-	subnet->snb.ts = get_timestamp_secs();
-	subnet->snb.half_period ^= 1;
-	l_timeout_modify(timeout, interval);
+		l_queue_foreach(net->friends, enqueue_update, &update);
+	}
 }
 
-static void start_network_beacon(void *a, void *b)
+static void refresh_beacon(void *a, void *b)
 {
 	struct mesh_subnet *subnet = a;
 	struct mesh_net *net = b;
 
-	if (!net->beacon_enable) {
-		if (subnet->snb.timeout)
-			l_timeout_remove(subnet->snb.timeout);
-		subnet->snb.timeout = NULL;
-		return;
-	}
-
-	/* If timeout is active, let it run it's course */
-	if (subnet->snb.timeout)
-		return;
-
-	send_network_beacon(subnet, subnet->net);
-
-	subnet->snb.ts = get_timestamp_secs();
-	subnet->snb.expected = 2;
-	subnet->snb.observed = 0;
-	subnet->snb.half_period = 1;
-	subnet->snb.observe_period = BEACON_INTERVAL_MIN * 2;
-
-	subnet->snb.timeout = l_timeout_create(BEACON_INTERVAL_MIN,
-				network_beacon_timeout, subnet, NULL);
+	net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
+		!!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update);
 }
 
 struct mesh_net *mesh_net_new(struct mesh_node *node)
@@ -1005,10 +966,11 @@ static struct mesh_subnet *add_key(struct mesh_net *net, uint16_t idx,
 		return NULL;
 	}
 
-	if (!create_secure_beacon(net, subnet, subnet->snb.beacon + 1)) {
-		subnet_free(subnet);
-		return NULL;
-	}
+	net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
+						false, net->iv_update);
+
+	if (net->beacon_enable)
+		net_key_beacon_enable(subnet->net_key_tx);
 
 	l_queue_push_tail(net->subnets, subnet);
 
@@ -1043,9 +1005,6 @@ int mesh_net_add_key(struct mesh_net *net, uint16_t idx, const uint8_t *value)
 		return MESH_STATUS_STORAGE_FAIL;
 	}
 
-	if (net->io)
-		start_network_beacon(subnet, net);
-
 	return MESH_STATUS_SUCCESS;
 }
 
@@ -1072,11 +1031,11 @@ void mesh_net_get_snb_state(struct mesh_net *net, uint8_t *flags,
 		return;
 
 	*iv_index = net->iv_index;
-	*flags = (net->iv_upd_state == IV_UPD_UPDATING) ? 0x02 : 0x00;
+	*flags = net->iv_update ? IV_INDEX_UPDATE : 0x00;
 
 	subnet = get_primary_subnet(net);
 	if (subnet)
-		*flags |= subnet->key_refresh ? 0x01 : 0x00;
+		*flags |= subnet->key_refresh ? KEY_REFRESH : 0x00;
 }
 
 bool mesh_net_get_key(struct mesh_net *net, bool new_key, uint16_t idx,
@@ -2117,14 +2076,6 @@ static bool ctl_received(struct mesh_net *net, uint16_t key_id,
 						L_UINT_TO_PTR(src)));
 		break;
 
-	case NET_OP_FRND_UPDATE:
-		if (ttl)
-			return false;
-
-		print_packet("Rx-NET_OP_FRND_UPDATE", pkt, len);
-		lpn_process_beacon(net, pkt, len, 0);
-		break;
-
 	case NET_OP_FRND_REQUEST:
 		if (!net->friend_enable)
 			return false;
@@ -2539,50 +2490,6 @@ static void net_msg_recv(void *user_data, struct mesh_io_recv_info *info,
 	}
 }
 
-static void set_network_beacon(void *a, void *b)
-{
-	struct mesh_subnet *subnet = a;
-	struct mesh_net *net = b;
-	uint8_t beacon_data[22];
-
-	if (!create_secure_beacon(net, subnet, beacon_data))
-		return;
-
-	if (memcmp(&subnet->snb.beacon[1], beacon_data,
-						sizeof(beacon_data)) == 0)
-		return;
-
-	memcpy(&subnet->snb.beacon[1], beacon_data, sizeof(beacon_data));
-
-	if (net->beacon_enable && !net->friend_addr) {
-		print_packet("Set My Beacon to",
-			beacon_data, sizeof(beacon_data));
-		start_network_beacon(subnet, net);
-	}
-
-	if (l_queue_length(net->friends)) {
-		struct mesh_friend_msg update = {
-			.src = net->src_addr,
-			.iv_index = mesh_net_get_iv_index(net),
-			.last_len = 7,
-			.ctl = true,
-		};
-
-		update.u.one[0].hdr = NET_OP_FRND_UPDATE << OPCODE_HDR_SHIFT;
-		update.u.one[0].seq = mesh_net_next_seq_num(net);
-		update.u.one[0].data[0] = NET_OP_FRND_UPDATE;
-		update.u.one[0].data[1] = beacon_data[3];
-		l_put_be32(net->iv_index, update.u.one[0].data + 2);
-		update.u.one[0].data[6] = 0x01; /* More Data */
-		/* print_packet("Frnd-Beacon-SRC",
-		 *			beacon_data, sizeof(beacon_data));
-		 */
-		/* print_packet("Frnd-Update", update.u.one[0].data, 6); */
-
-		l_queue_foreach(net->friends, enqueue_update, &update);
-	}
-}
-
 static void iv_upd_to(struct l_timeout *upd_timeout, void *user_data)
 {
 	struct mesh_net *net = user_data;
@@ -2604,7 +2511,8 @@ static void iv_upd_to(struct l_timeout *upd_timeout, void *user_data)
 		net->iv_update = false;
 		mesh_config_write_iv_index(node_config_get(net->node),
 							net->iv_index, false);
-		l_queue_foreach(net->subnets, set_network_beacon, net);
+		l_queue_foreach(net->subnets, refresh_beacon, net);
+		queue_friend_update(net);
 		mesh_net_flush_msg_queues(net);
 		break;
 
@@ -2646,7 +2554,8 @@ static int key_refresh_phase_two(struct mesh_net *net, uint16_t idx)
 	 * it hears beacons from all the nodes
 	 */
 	subnet->kr_phase = KEY_REFRESH_PHASE_TWO;
-	set_network_beacon(subnet, net);
+	refresh_beacon(subnet, net);
+	queue_friend_update(net);
 
 	l_queue_foreach(net->friends, frnd_kr_phase2, net);
 
@@ -2679,7 +2588,8 @@ static int key_refresh_finish(struct mesh_net *net, uint16_t idx)
 	subnet->net_key_upd = 0;
 	subnet->key_refresh = 0;
 	subnet->kr_phase = KEY_REFRESH_PHASE_NONE;
-	set_network_beacon(subnet, net);
+	refresh_beacon(subnet, net);
+	queue_friend_update(net);
 
 	l_queue_foreach(net->friends, frnd_kr_phase3, net);
 
@@ -2771,106 +2681,73 @@ static void update_iv_ivu_state(struct mesh_net *net, uint32_t iv_index,
 static void process_beacon(void *net_ptr, void *user_data)
 {
 	struct mesh_net *net = net_ptr;
-	const uint8_t *buf = *(uint8_t **)user_data;
+	struct net_beacon_data *beacon_data = user_data;
 	uint32_t ivi;
 	bool ivu, kr, local_kr;
 	struct mesh_subnet *subnet;
-	uint32_t key_id;
 
-	ivi = l_get_be32(buf + 10);
+	ivi = beacon_data->ivi;
 
 	/* Ignore out-of-range IV_Index for this network */
 	if ((net->iv_index + IV_IDX_DIFF_RANGE < ivi) || (ivi < net->iv_index))
 		return;
 
-	/* Ignore Network IDs unknown to this mesh universe */
-	key_id = net_key_network_id(buf + 2);
-	if (!key_id)
-		return;
-
+	/* Ignore beacons not in this universe */
 	subnet = l_queue_find(net->subnets, match_key_id,
-							L_UINT_TO_PTR(key_id));
+					L_UINT_TO_PTR(beacon_data->key_id));
+
 	if (!subnet)
 		return;
 
 	/* Get IVU and KR boolean bits from beacon */
-	ivu = !!(buf[1] & 0x02);
-	kr = !!(buf[1] & 0x01);
-	local_kr = !!(subnet->kr_phase != KEY_REFRESH_PHASE_TWO);
-
-	if (net->iv_upd_state != IV_UPD_INIT) {
-		/* Ignore beacons that indicate *no change* */
-		if (!memcmp(&subnet->snb.beacon[1], buf, 22)) {
-			subnet->snb.observed++;
-			return;
-		}
-	}
-
-	/* Validate beacon before accepting */
-	if (!net_key_snb_check(key_id, ivi, kr, ivu, l_get_be64(buf + 14))) {
-		l_error("mesh_crypto_beacon verify failed");
-		return;
-	}
+	ivu = beacon_data->ivu;
+	kr = beacon_data->kr;
+	local_kr = !!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO);
 
 	/* We have officially *seen* this beacon now */
-	subnet->snb.observed++;
-
-	update_iv_ivu_state(net, ivi, ivu);
-	update_kr_state(subnet, kr, key_id);
-
-	if (ivi != net->iv_index || ivu != net->iv_update || kr != local_kr)
-		set_network_beacon(subnet, net);
-}
-
-static void lpn_process_beacon(void *user_data, const void *data,
-						uint8_t size, int8_t rssi)
-{
-	struct mesh_net *net = user_data;
-	const uint8_t *buf = data;
-	uint32_t ivi;
-	bool ivu, kr, local_kr;
-	struct mesh_subnet *subnet;
-	bool kr_transition = false;
-
-	/* print_packet("lpn: Secure Net Beacon RXed", data, size); */
-	kr = !!(buf[0] & 0x01);
-	ivu = !!(buf[0] & 0x02);
-	ivi = l_get_be32(buf + 1);
-
-	l_debug("KR: %d -- IVU: %d -- IVI: %8.8x", kr, ivu, ivi);
-
-	/* TODO: figure out actual network index (i.e., friendship subnet) */
-	subnet = get_primary_subnet(net);
-	if (!subnet)
-		return;
-
-	local_kr = (subnet->kr_phase == KEY_REFRESH_PHASE_TWO);
+	beacon_data->processed = true;
 
-	/* Don't bother going further if nothing has changed */
-	if (local_kr == kr && ivi == net->iv_index && ivu == net->iv_update &&
-					net->iv_upd_state != IV_UPD_INIT)
+	if (ivi == net->iv_index && ivu == net->iv_update && kr == local_kr)
 		return;
 
 	update_iv_ivu_state(net, ivi, ivu);
-
-	if (kr)
-		update_kr_state(subnet, kr_transition, subnet->net_key_upd);
-	else
-		update_kr_state(subnet, kr_transition, subnet->net_key_cur);
+	update_kr_state(subnet, kr, beacon_data->key_id);
+	net_key_beacon_refresh(beacon_data->key_id, net->iv_index,
+		!!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update);
 }
 
 static void beacon_recv(void *user_data, struct mesh_io_recv_info *info,
 					const uint8_t *data, uint16_t len)
 {
-	const uint8_t *ptr = data + 1;
+	struct net_beacon_data beacon_data = {
+		.processed = false,
+	};
 
 	if (len != 23 || data[1] != 0x01)
 		return;
 
-	l_debug("KR: %d -- IVU: %d -- IV: %8.8x",
-			data[2] & 1, !!(data[2] & 2), l_get_be32(data + 11));
+	/* Ignore Network IDs unknown to this daemon */
+	beacon_data.key_id = net_key_network_id(data + 3);
+	if (!beacon_data.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);
 
-	l_queue_foreach(nets, process_beacon, &ptr);
+	/* Validate beacon before accepting */
+	if (!net_key_snb_check(beacon_data.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.key_id);
 }
 
 bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable)
@@ -2878,16 +2755,16 @@ bool mesh_net_set_beacon_mode(struct mesh_net *net, bool enable)
 	if (!net || !IS_UNASSIGNED(net->friend_addr))
 		return false;
 
-	if (net->beacon_enable != enable) {
-		uint8_t type = MESH_AD_TYPE_BEACON;
+	if (net->beacon_enable == enable)
+		return true;
 
-		net->beacon_enable = enable;
+	net->beacon_enable = enable;
 
-		if (!enable)
-			mesh_io_send_cancel(net->io, &type, 1);
+	if (enable)
+		l_queue_foreach(net->subnets, refresh_beacon, net);
 
-		l_queue_foreach(net->subnets, start_network_beacon, net);
-	}
+	l_queue_foreach(net->subnets, enable_beacon, net);
+	queue_friend_update(net);
 
 	return true;
 }
@@ -2923,14 +2800,18 @@ bool mesh_net_set_key(struct mesh_net *net, uint16_t idx, const uint8_t *key,
 	if (phase == KEY_REFRESH_PHASE_TWO) {
 		subnet->key_refresh = 1;
 		subnet->net_key_tx = subnet->net_key_upd;
+		if (net->beacon_enable) {
+			/* Switch beaconing key */
+			net_key_beacon_disable(subnet->net_key_cur);
+			net_key_beacon_enable(subnet->net_key_upd);
+		}
 	}
 
 	subnet->kr_phase = phase;
 
-	set_network_beacon(subnet, net);
+	net_key_beacon_refresh(subnet->net_key_tx, net->iv_index,
+		!!(subnet->kr_phase == KEY_REFRESH_PHASE_TWO), net->iv_update);
 
-	if (net->io)
-		start_network_beacon(subnet, net);
 
 	return true;
 }
@@ -2960,7 +2841,6 @@ bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)
 							beacon_recv, NULL);
 		mesh_io_register_recv_cb(io, MESH_IO_FILTER_NET,
 							net_msg_recv, NULL);
-		l_queue_foreach(net->subnets, start_network_beacon, net);
 	}
 
 	if (l_queue_find(nets, is_this_net, net))
@@ -3007,7 +2887,8 @@ bool mesh_net_iv_index_update(struct mesh_net *net)
 	net->iv_upd_state = IV_UPD_UPDATING;
 	net->iv_index++;
 	net->iv_update = true;
-	l_queue_foreach(net->subnets, set_network_beacon, net);
+	l_queue_foreach(net->subnets, refresh_beacon, net);
+	queue_friend_update(net);
 	net->iv_update_timeout = l_timeout_create(
 			IV_IDX_UPD_MIN,
 			iv_upd_to, net, NULL);
@@ -3059,7 +2940,6 @@ bool mesh_net_set_friend(struct mesh_net *net, uint16_t friend_addr)
 
 	net->friend_addr = friend_addr;
 
-	set_network_beacon(get_primary_subnet(net), net);
 	return true;
 }
 
diff --git a/mesh/net.h b/mesh/net.h
index fe4e0b6..90ca832 100644
--- a/mesh/net.h
+++ b/mesh/net.h
@@ -117,6 +117,9 @@ struct mesh_node;
 #define FRND_OPCODE(x) \
 		((x) >= NET_OP_FRND_POLL && (x) <= NET_OP_FRND_CLEAR_CONFIRM)
 
+#define DEFAULT_MIN_DELAY		0
+#define DEFAULT_MAX_DELAY		25
+
 struct mesh_net_addr_range {
 	uint16_t low;
 	uint16_t high;