[PATCH BlueZ v3 06/14] meshd: Upper and Lower mesh transport

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



---
 meshd/src/net.c | 3639 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 3639 insertions(+)
 create mode 100644 meshd/src/net.c

diff --git a/meshd/src/net.c b/meshd/src/net.c
new file mode 100644
index 000000000..12f11b2b2
--- /dev/null
+++ b/meshd/src/net.c
@@ -0,0 +1,3639 @@
+/*
+ *
+ *  BlueZ - Bluetooth protocol stack for Linux
+ *
+ *  Copyright (C) 2018  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
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <ell/ell.h>
+
+#include "meshd/common/mesh-defs.h"
+#include "meshd/common/util.h"
+
+#include "meshd/src/display.h"
+#include "meshd/src/crypto.h"
+#include "meshd/src/mesh.h"
+#include "meshd/src/node.h"
+#include "meshd/src/net.h"
+#include "meshd/src/mesh-io.h"
+#include "meshd/src/friend.h"
+#include "meshd/src/storage.h"
+#include "meshd/src/model.h"
+#include "meshd/src/appkey.h"
+#include "meshd/src/prov.h"
+#include "meshd/src/provision.h"
+
+#define abs_diff(a, b) ((a) > (b) ? (a) - (b) : (b) - (a))
+
+#define IV_IDX_DIFF_RANGE	42
+
+/* #define IV_IDX_UPD_MIN	(60)		1 minute for Testing */
+#define IV_IDX_UPD_MIN	(60 * 60 * 96)	/* 96 Hours - per Spec */
+#define IV_IDX_UPD_HOLD	(IV_IDX_UPD_MIN/2)
+#define IV_IDX_UPD_MAX	(IV_IDX_UPD_MIN + IV_IDX_UPD_HOLD)
+
+#define iv_is_updating(net) ((net)->iv_upd_state == IV_UPD_UPDATING)
+
+#define IV_UPDATE_SEQ_TRIGGER 0x800000  /* Half of Seq-Nums expended */
+
+#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_TYPE_SNB		0x01
+
+#define BEACON_INTERVAL_MIN	10
+#define BEACON_INTERVAL_MAX	600
+
+#define SAR_KEY(src, seq0)	((((uint32_t)(seq0)) << 16) | (src))
+
+enum _iv_upd_state {
+	/* Allows acceptance of any iv_index secure net beacon */
+	IV_UPD_INIT,
+	/* Normal, can transition, accept current or old */
+	IV_UPD_NORMAL,
+	/* Updating proc running, we use old, accept old or new */
+	IV_UPD_UPDATING,
+	/* Normal, can *not* transition, accept current or old iv_index */
+	IV_UPD_NORMAL_HOLD,
+};
+
+struct net_key {
+	struct mesh_key_set key_set;
+	unsigned int beacon_id;
+	uint8_t key[16];
+	uint8_t beacon_key[16];
+	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;
+	struct net_key *tx;
+	struct net_key current;
+	struct net_key updated;
+	struct mesh_beacon snb;
+	uint8_t key_refresh;
+	uint8_t kr_phase;
+};
+
+struct mesh_net {
+	int ref_count;
+	struct mesh_io *io;
+	struct mesh_node *local_node;
+	struct mesh_prov *prov;
+	void *jconfig_local;
+	const char *cfg_file;
+	struct l_queue *app_keys;
+	unsigned int pkt_id;
+	unsigned int bea_id;
+	unsigned int beacon_id;
+	unsigned int key_id_next;
+	unsigned int sar_id_next;
+
+	bool friend_enable;
+	bool beacon_enable;
+	bool proxy_enable;
+	bool provisioner;
+	bool provisioned;
+	bool friend_seq;
+	struct l_timeout *iv_update_timeout;
+	enum _iv_upd_state iv_upd_state;
+
+	bool iv_update;
+	uint32_t instant; /* Controller Instant of recent Rx */
+	uint32_t iv_index;
+	uint32_t seq_num;
+	uint32_t cached_seq_num;
+	uint16_t crpl;
+	uint16_t src_addr;
+	uint16_t last_addr;
+	uint16_t friend_addr;
+	uint16_t tx_interval;
+	uint16_t tx_cnt;
+	uint8_t chan; /* Channel of recent Rx */
+	uint8_t default_ttl;
+	uint8_t tid;
+	uint8_t window_accuracy;
+
+	struct {
+		bool enable;
+		uint16_t interval;
+		uint8_t count;
+	} relay;
+
+	struct mesh_net_heartbeat heartbeat;
+
+	struct l_queue *subnets;
+	struct l_queue *msg_cache;
+	struct l_queue *sar_in;
+	struct l_queue *sar_out;
+	struct l_queue *frnd_msgs;
+	struct l_queue *friends;
+	struct l_queue *destinations;
+	struct l_queue *fast_cache;
+	struct l_queue *key_sets;
+
+	uint8_t prov_priv_key[32];
+
+	/* Unprovisioned Identity */
+	char id_name[20];
+	uint8_t id_uuid[16];
+
+	/* Provisioner: unicast address range */
+	struct mesh_net_addr_range prov_uni_addr;
+
+	/* Test Data */
+	uint8_t prov_rand[16];
+	uint8_t test_bd_addr[6];
+	struct mesh_net_prov_caps prov_caps;
+	bool test_mode;
+};
+
+struct mesh_msg {
+	uint16_t src;
+	uint32_t seq;
+	uint32_t mic;
+};
+
+struct mesh_sar {
+	unsigned int id;
+	struct l_timeout *seg_timeout;
+	struct l_timeout *msg_timeout;
+	mesh_net_status_func_t status_func;
+	void *user_data;
+	uint32_t flags;
+	uint32_t last_nak;
+	uint32_t iv_index;
+	uint32_t seqAuth;
+	uint16_t seqZero;
+	uint16_t app_idx;
+	uint16_t src;
+	uint16_t remote;
+	uint16_t len;
+	bool szmic;
+	bool frnd;
+	bool frnd_cred;
+	uint8_t ttl;
+	uint8_t last_seg;
+	uint8_t key_id;
+	uint8_t buf[4]; /* Large enough for ACK-Flags and MIC */
+};
+
+struct mesh_destination {
+	uint16_t dst;
+	uint16_t ref_cnt;
+};
+
+struct msg_rx {
+	const uint8_t *data;
+	uint32_t iv_index;
+	uint32_t seq;
+	uint16_t src;
+	uint16_t dst;
+	uint16_t size;
+	uint8_t tc;
+	bool done;
+	bool szmic;
+	union {
+		struct {
+			uint16_t app_idx;
+			uint8_t key_id;
+		} m;
+		struct {
+			uint16_t seq0;
+		} a;
+		struct {
+			uint8_t opcode;
+		} c;
+	} u;
+};
+
+struct net_decode {
+	struct mesh_net *net;
+	struct mesh_friend *frnd;
+	struct mesh_key_set *key_set;
+	uint8_t *packet;
+	uint32_t iv_index;
+	uint8_t size;
+	uint8_t nid;
+	bool proxy;
+};
+
+static inline struct mesh_subnet *get_primary_subnet(struct mesh_net *net)
+{
+	return l_queue_peek_head(net->subnets);
+}
+
+static bool match_key_index(const void *a, const void *b)
+{
+	const struct mesh_subnet *subnet = a;
+	uint16_t idx = L_PTR_TO_UINT(b);
+
+	return subnet->idx == idx;
+}
+
+static bool match_key_set(const void *a, const void *b)
+{
+	const struct mesh_subnet *subnet = a;
+	const struct mesh_key_set *key_set = b;
+
+	return (key_set == &subnet->current.key_set) ||
+					(key_set == &subnet->updated.key_set);
+}
+
+static bool match_network_id(const void *a, const void *b)
+{
+	const struct mesh_subnet *subnet = a;
+	const uint8_t *network_id = b;
+
+	return ((memcmp(subnet->current.network_id, network_id, 8) == 0) ||
+		(memcmp(subnet->updated.network_id, network_id, 8) == 0));
+}
+
+static void idle_mesh_heartbeat_send(void *net)
+{
+	mesh_net_heartbeat_send(net);
+}
+
+static void trigger_heartbeat(struct mesh_net *net, uint16_t feature,
+								bool in_use)
+{
+	struct mesh_net_heartbeat *hb = &net->heartbeat;
+
+	l_info("%s: %4.4x --> %d", __func__, feature, in_use);
+	if (in_use) {
+		if (net->heartbeat.features & feature)
+			return; /* no change */
+
+		hb->features |= feature;
+	} else {
+		if (!(hb->features & feature))
+			return; /* no change */
+
+		hb->features &= ~feature;
+	}
+
+	if (!(hb->pub_features & feature))
+		return; /* not interested in this feature */
+
+	l_idle_oneshot(idle_mesh_heartbeat_send, net, NULL);
+
+}
+
+static bool match_by_friend(const void *a, const void *b)
+{
+	const struct mesh_friend *frnd = a;
+	uint16_t dst = L_PTR_TO_UINT(b);
+
+	return frnd->dst == dst;
+}
+
+static void free_friend_internals(struct mesh_friend *frnd)
+{
+	if (frnd->pkt_cache)
+		l_queue_destroy(frnd->pkt_cache, l_free);
+
+	if (frnd->grp_list)
+		l_free(frnd->grp_list);
+
+	frnd->pkt_cache = NULL;
+	frnd->grp_list = NULL;
+	mesh_net_remove_keyset(frnd->net, &frnd->key_set);
+	mesh_net_remove_keyset(frnd->net, &frnd->new_key_set);
+}
+
+static void frnd_kr_phase1(void *a, void *b)
+{
+	struct mesh_friend *frnd = a;
+	const uint8_t *key = b;
+	uint8_t p[9] = {0x01};
+
+	l_put_be16(frnd->dst, p + 1);
+	l_put_be16(frnd->net->src_addr, p + 3);
+	l_put_be16(frnd->lp_cnt, p + 5);
+	l_put_be16(frnd->fn_cnt, p + 7);
+
+	mesh_crypto_k2(key, p, sizeof(p), &frnd->new_key_set.nid,
+				frnd->new_key_set.enc_key,
+				frnd->new_key_set.privacy_key);
+
+	mesh_net_add_keyset(frnd->net, &frnd->new_key_set);
+	l_info("Add New KeySet %2.2x for %4.4x",
+					frnd->new_key_set.nid, frnd->dst);
+	l_info("Outgoing with %2.2x", frnd->key_set.nid);
+}
+
+static void frnd_kr_phase2(void *a, void *b)
+{
+	struct mesh_friend *frnd = a;
+
+	/*
+	 * I think that a Friend should use Old Key as long as possible
+	 * Because a Friend Node will enter Phase 3 before it's LPN.
+	 * Alternatively, the FN could keep the Old Friend Keys until it
+	 * receives it's first Poll using the new keys (?)
+	 */
+
+	l_info("Use Both KeySet %2.2x && %2.2x for %4.4x",
+			frnd->key_set.nid, frnd->new_key_set.nid, frnd->dst);
+}
+
+static void frnd_kr_phase3(void *a, void *b)
+{
+	struct mesh_friend *frnd = a;
+	struct mesh_net *net = b;
+
+	l_info("Replace KeySet %2.2x with %2.2x for %4.4x",
+			frnd->key_set.nid, frnd->new_key_set.nid, frnd->dst);
+	frnd->key_set = frnd->new_key_set;
+	mesh_net_remove_keyset(net, &frnd->new_key_set);
+	frnd->new_key_set.nid = 0xff;
+}
+
+/* TODO: add net key idx? For now, use primary net key */
+struct mesh_friend *mesh_friend_new(struct mesh_net *net, uint16_t dst,
+					uint8_t ele_cnt, uint8_t frd,
+					uint8_t frw, uint32_t fpt,
+					uint16_t fn_cnt, uint16_t lp_cnt)
+{
+	struct mesh_subnet *subnet;
+	uint8_t p[9] = {0x01};
+	struct mesh_friend *frnd = l_queue_find(net->friends,
+					match_by_friend, L_UINT_TO_PTR(dst));
+
+	if (frnd) {
+		/* Kill all timers and empty cache for this friend */
+		free_friend_internals(frnd);
+		l_timeout_remove(frnd->timeout);
+		frnd->timeout = NULL;
+	} else {
+		frnd = l_new(struct mesh_friend, 1);
+		l_queue_push_head(net->friends, frnd);
+	}
+
+	/* add _k2 */
+	frnd->net = net;
+	frnd->dst = dst;
+	frnd->frd = frd;
+	frnd->frw = frw;
+	frnd->fn_cnt = fn_cnt;
+	frnd->lp_cnt = lp_cnt;
+	frnd->poll_timeout = fpt;
+	frnd->ele_cnt = ele_cnt;
+	frnd->pkt_cache = l_queue_new();
+	frnd->new_key_set.nid = NET_NID_INVALID;
+
+	l_put_be16(dst, p + 1);
+	l_put_be16(net->src_addr, p + 3);
+	l_put_be16(lp_cnt, p + 5);
+	l_put_be16(fn_cnt, p + 7);
+
+	subnet = get_primary_subnet(net);
+	/* TODO: the primary key must be present, do we need to add check?. */
+
+	mesh_crypto_k2(subnet->current.key, p, sizeof(p),
+				&frnd->key_set.nid,
+				frnd->key_set.enc_key,
+				frnd->key_set.privacy_key);
+
+	frnd->key_set.frnd = true;
+	mesh_net_add_keyset(net, &frnd->key_set);
+
+	if (subnet->updated.key_set.nid == NET_NID_INVALID)
+		return frnd;
+
+	mesh_crypto_k2(subnet->updated.key, p, sizeof(p),
+				&frnd->new_key_set.nid,
+				frnd->new_key_set.enc_key,
+				frnd->new_key_set.privacy_key);
+	frnd->new_key_set.frnd = true;
+	mesh_net_add_keyset(net, &frnd->new_key_set);
+
+	return frnd;
+}
+
+void mesh_friend_free(void *data)
+{
+	struct mesh_friend *frnd = data;
+
+	free_friend_internals(frnd);
+	l_timeout_remove(frnd->timeout);
+	l_free(frnd);
+}
+
+bool mesh_friend_clear(struct mesh_net *net, struct mesh_friend *frnd)
+{
+	bool removed = l_queue_remove(net->friends, frnd);
+
+	free_friend_internals(frnd);
+
+	return removed;
+}
+
+bool mesh_net_add_keyset(struct mesh_net *net, struct mesh_key_set *key_set)
+{
+	if (!net)
+		return false;
+
+	l_info("Add KEY_SET %2.2x (%d) %p",
+					key_set->nid, key_set->frnd, key_set);
+	return l_queue_push_tail(net->key_sets, key_set);
+}
+
+bool mesh_net_remove_keyset(struct mesh_net *net, struct mesh_key_set *key_set)
+{
+	if (!net || !net->key_sets)
+		return false;
+
+	l_info("DEL KEY_SET %2.2x (%d) %p",
+					key_set->nid, key_set->frnd, key_set);
+	return l_queue_remove(net->key_sets, key_set);
+}
+
+void mesh_friend_sub_add(struct mesh_net *net, uint16_t lpn, uint8_t ele_cnt,
+							uint8_t grp_cnt,
+							const uint8_t *list)
+{
+	uint16_t *new_list;
+	uint16_t *grp_list;
+	struct mesh_friend *frnd = l_queue_find(net->friends,
+							match_by_friend,
+							L_UINT_TO_PTR(lpn));
+	if (!frnd)
+		return;
+
+	new_list = l_malloc((grp_cnt + frnd->grp_cnt) * sizeof(uint16_t));
+	grp_list = frnd->grp_list;
+
+	if (grp_list && frnd->grp_cnt)
+		memcpy(new_list, grp_list, frnd->grp_cnt * sizeof(uint16_t));
+
+	memcpy(&new_list[frnd->grp_cnt], list, grp_cnt * sizeof(uint16_t));
+	l_free(grp_list);
+	frnd->ele_cnt = ele_cnt;
+	frnd->grp_list = new_list;
+	frnd->grp_cnt += grp_cnt;
+}
+
+void mesh_friend_sub_del(struct mesh_net *net, uint16_t lpn,
+						uint8_t cnt,
+						const uint8_t *del_list)
+{
+	uint16_t *grp_list;
+	int16_t i, grp_cnt;
+	size_t cnt16 = cnt * sizeof(uint16_t);
+	struct mesh_friend *frnd = l_queue_find(net->friends,
+							match_by_friend,
+							L_UINT_TO_PTR(lpn));
+	if (!frnd)
+		return;
+
+	grp_cnt = frnd->grp_cnt;
+	grp_list = frnd->grp_list;
+
+	while (cnt-- && grp_cnt) {
+		cnt16 -= sizeof(uint16_t);
+		for (i = grp_cnt - 1; i >= 0; i--) {
+			if (l_get_le16(del_list + cnt16) == grp_list[i]) {
+				grp_cnt--;
+				memcpy(&grp_list[i], &grp_list[i + 1],
+					(grp_cnt - i) * sizeof(uint16_t));
+				break;
+			}
+		}
+	}
+
+	frnd->grp_cnt = grp_cnt;
+
+	if (!grp_cnt) {
+		l_free(frnd->grp_list);
+		frnd->grp_list = NULL;
+	}
+}
+
+uint32_t mesh_net_next_seq_num(struct mesh_net *net)
+{
+	uint32_t seq = net->seq_num;
+
+	net->seq_num++;
+
+	/* Periodically store advanced sequence number */
+	if (net->seq_num + MIN_SEQ_TRIGGER >= net->cached_seq_num) {
+		net->cached_seq_num = net->seq_num +
+					node_seq_cache(net->local_node);
+		node_set_sequence_number(net->local_node, net->cached_seq_num);
+	}
+
+	return seq;
+}
+
+static struct mesh_sar *mesh_sar_new(size_t len)
+{
+	size_t size = sizeof(struct mesh_sar) + len;
+	struct mesh_sar *sar;
+
+	sar = l_malloc(size);
+
+	memset(sar, 0, size);
+
+	return sar;
+}
+
+static void mesh_sar_free(void *data)
+{
+	struct mesh_sar *sar = data;
+
+	if (!sar)
+		return;
+
+	l_timeout_remove(sar->seg_timeout);
+	l_timeout_remove(sar->msg_timeout);
+	l_free(sar);
+}
+
+static void mesh_msg_free(void *data)
+{
+	struct mesh_msg *msg = data;
+
+	l_free(msg);
+}
+
+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;
+
+	subnet = l_new(struct mesh_subnet, 1);
+	if (!subnet)
+		return NULL;
+
+	subnet->net = net;
+	subnet->idx = idx;
+	subnet->tx = &subnet->current;
+	subnet->updated.key_set.nid = NET_NID_INVALID;
+	subnet->snb.beacon[0] = MESH_AD_TYPE_BEACON;
+	return subnet;
+}
+
+static bool create_keys(struct mesh_net *net, struct net_key *keys,
+			const uint8_t *net_key)
+{
+	uint8_t nid[1];
+	uint8_t enc_key[16];
+	uint8_t privacy_key[16];
+	uint8_t network_id[8];
+	uint8_t p[] = {0};
+
+	if (!mesh_crypto_k2(net_key, p, sizeof(p),
+				nid, enc_key, privacy_key))
+		return false;
+
+	if (!mesh_crypto_k3(net_key, network_id))
+		return false;
+
+	if (!mesh_crypto_nkbk(net_key, keys->beacon_key))
+		return false;
+
+	keys->key_set.frnd = false;
+	keys->key_set.nid = nid[0];
+	memcpy(keys->key_set.enc_key, enc_key, 16);
+	memcpy(keys->key_set.privacy_key, privacy_key, 16);
+	memcpy(keys->network_id, network_id, 8);
+	memcpy(keys->key, net_key, 16);
+	return true;
+}
+
+static bool create_secure_beacon(struct mesh_net *net,
+					struct mesh_subnet *subnet,
+					uint8_t *beacon_data, uint8_t size)
+{
+	uint64_t cmac;
+
+	if (size < 22)
+		return false;
+
+	beacon_data[0] = BEACON_TYPE_SNB;
+	beacon_data[1] = 0;
+
+	if (subnet->key_refresh)
+		beacon_data[1] |= 0x01;
+
+	if (iv_is_updating(net))
+		beacon_data[1] |= 0x02;
+
+	memcpy(beacon_data + 2, subnet->tx->network_id, 8);
+	l_put_be32(net->iv_index, beacon_data + 10);
+
+	if (!mesh_crypto_beacon_cmac(subnet->tx->beacon_key,
+					subnet->tx->network_id,
+					net->iv_index, subnet->key_refresh,
+					iv_is_updating(net), &cmac))
+		return false;
+
+	l_put_be64(cmac, beacon_data + 14);
+
+	return true;
+}
+
+static void send_network_beacon(struct mesh_subnet *subnet,
+							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
+	};
+
+	l_info("Send SNB on network %3.3x", subnet->idx);
+	mesh_io_send(net->io, &info, subnet->snb.beacon,
+						sizeof(subnet->snb.beacon));
+}
+
+static void network_beacon_timeout(struct l_timeout *timeout, void *user_data)
+{
+	struct mesh_subnet *subnet = user_data;
+	uint32_t interval;
+
+	send_network_beacon(subnet, subnet->net);
+
+	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 (interval < BEACON_INTERVAL_MIN)
+		interval = BEACON_INTERVAL_MIN;
+
+	if (interval > BEACON_INTERVAL_MAX)
+		interval = BEACON_INTERVAL_MAX;
+
+	subnet->snb.ts = get_timestamp_secs();
+	subnet->snb.half_period ^= 1;
+	l_timeout_modify(timeout, interval);
+}
+
+static void start_network_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);
+}
+
+struct mesh_net *mesh_net_new(uint16_t index)
+{
+	struct mesh_net *net;
+
+	net = l_new(struct mesh_net, 1);
+
+	if (!net)
+		return NULL;
+
+	net->pkt_id = 0;
+	net->bea_id = 0;
+	net->key_id_next = 0;
+
+	net->beacon_enable = true;
+	net->proxy_enable = false;
+	net->relay.enable = false;
+
+	net->seq_num = 0x000000;
+	net->src_addr = 0x0000;
+	net->default_ttl = 0x00;
+
+	net->provisioner = false;
+
+	net->test_mode = false;
+	memset(&net->prov_caps, 0, sizeof(net->prov_caps));
+	net->prov_caps.algorithms = 1;
+
+	net->tx_cnt = DEFAULT_TRANSMIT_COUNT;
+	net->tx_interval = DEFAULT_TRANSMIT_INTERVAL;
+
+	net->subnets = l_queue_new();
+	net->key_sets = l_queue_new();
+	net->fast_cache = l_queue_new();
+	net->msg_cache = l_queue_new();
+	net->sar_in = l_queue_new();
+	net->sar_out = l_queue_new();
+	net->frnd_msgs = l_queue_new();
+	net->friends = l_queue_new();
+	net->destinations = l_queue_new();
+	net->app_keys = l_queue_new();
+
+	memset(&net->heartbeat, 0, sizeof(net->heartbeat));
+
+	return mesh_net_ref(net);
+}
+
+struct mesh_net *mesh_net_ref(struct mesh_net *net)
+{
+	if (!net)
+		return NULL;
+
+	__sync_fetch_and_add(&net->ref_count, 1);
+
+	return net;
+}
+
+void mesh_net_unref(struct mesh_net *net)
+{
+	if (!net)
+		return;
+
+	if (__sync_sub_and_fetch(&net->ref_count, 1))
+		return;
+
+	/* key_sets are not allocated to this queue. Only Borrowed */
+	l_queue_destroy(net->key_sets, NULL);
+	net->key_sets = NULL;
+
+	l_queue_destroy(net->subnets, l_free);
+	l_queue_destroy(net->fast_cache, mesh_msg_free);
+	l_queue_destroy(net->msg_cache, mesh_msg_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);
+	l_queue_destroy(net->friends, mesh_friend_free);
+	l_queue_destroy(net->destinations, l_free);
+	l_queue_destroy(net->app_keys, appkey_key_free);
+
+	l_free(net);
+}
+
+int mesh_net_del_key(struct mesh_net *net, uint16_t idx)
+{
+	struct mesh_subnet *subnet;
+
+	if (!net)
+		return MESH_STATUS_UNSPECIFIED_ERROR;
+
+	/* Cannot remove primary key */
+	if (l_queue_length(net->subnets) <= 1)
+		return MESH_STATUS_CANNOT_REMOVE;
+
+	subnet = l_queue_find(net->subnets, match_key_index,
+							L_UINT_TO_PTR(idx));
+	if (!subnet)
+		return MESH_STATUS_CANNOT_REMOVE;
+
+	/* Delete associated app keys */
+	appkey_delete_bound_keys(net, idx);
+
+	/* Disable hearbeat publication on this subnet */
+	if (idx == net->heartbeat.pub_net_idx)
+		net->heartbeat.pub_dst = UNASSIGNED_ADDRESS;
+
+	mesh_net_remove_keyset(net, &subnet->current.key_set);
+	mesh_net_remove_keyset(net, &subnet->updated.key_set);
+
+	/* TODO: cancel beacon_enable on this subnet */
+
+	l_queue_remove(net->subnets, subnet);
+	if (!storage_local_net_key_del(net, idx))
+		return MESH_STATUS_STORAGE_FAIL;
+
+	return MESH_STATUS_SUCCESS;
+}
+
+int mesh_net_add_key(struct mesh_net *net, bool update, uint16_t idx,
+							const void *value)
+{
+	int status;
+	struct mesh_subnet *subnet;
+
+	subnet = l_queue_find(net->subnets, match_key_index,
+							L_UINT_TO_PTR(idx));
+
+	if (update) {
+		if (subnet && subnet->kr_phase == KEY_REFRESH_PHASE_NONE) {
+			l_info("Start key refresh");
+			status = mesh_net_kr_phase_one(net, idx, value);
+			if (status == MESH_STATUS_SUCCESS &&
+				!storage_local_net_key_add(net, idx,
+						value, KEY_REFRESH_PHASE_ONE))
+				return MESH_STATUS_STORAGE_FAIL;
+		} else
+			return MESH_STATUS_CANNOT_UPDATE;
+	}
+
+	if (subnet)
+		return memcmp(subnet->current.key, value, 16) ?
+			MESH_STATUS_IDX_ALREADY_STORED : MESH_STATUS_SUCCESS;
+
+	subnet = subnet_new(net, idx);
+	if (!subnet)
+		return MESH_STATUS_INSUFF_RESOURCES;
+
+	if (!create_keys(net, &subnet->current, value) ||
+			!mesh_net_add_keyset(net, &subnet->current.key_set)) {
+		l_free(subnet);
+		return MESH_STATUS_INSUFF_RESOURCES;
+	}
+
+	if (!create_secure_beacon(net, subnet, &subnet->snb.beacon[1], 22) ||
+				!l_queue_push_tail(net->subnets, subnet)) {
+		mesh_net_remove_keyset(net, &subnet->current.key_set);
+		l_free(subnet);
+		return MESH_STATUS_INSUFF_RESOURCES;
+	}
+
+	if (!storage_local_net_key_add(net, idx, value,
+					KEY_REFRESH_PHASE_NONE)) {
+		l_queue_remove(net->subnets, subnet);
+		mesh_net_remove_keyset(net, &subnet->current.key_set);
+		l_free(subnet);
+		return MESH_STATUS_STORAGE_FAIL;
+	}
+
+	start_network_beacon(subnet, net);
+
+	return MESH_STATUS_SUCCESS;
+}
+
+void mesh_net_flush_msg_queues(struct mesh_net *net)
+{
+	l_queue_clear(net->msg_cache, mesh_msg_free);
+	l_queue_clear(net->fast_cache, mesh_msg_free);
+}
+
+static bool match_cache(const void *a, const void *b)
+{
+	const struct mesh_msg *msg = a;
+	const struct mesh_msg *tst = b;
+
+	if (msg->seq != tst->seq || msg->mic != tst->mic ||
+					msg->src != tst->src)
+		return false;
+
+	return true;
+}
+
+static bool msg_in_cache(struct mesh_net *net, uint16_t src, uint32_t seq,
+								uint32_t mic)
+{
+	struct mesh_msg *msg;
+	struct mesh_msg tst = {
+		.src = src,
+		.seq = seq,
+		.mic = mic,
+	};
+
+	msg = l_queue_remove_if(net->msg_cache, match_cache, &tst);
+
+	if (msg) {
+		l_debug("Supressing duplicate %4.4x + %6.6x + %8.8x",
+							src, seq, mic);
+		l_queue_push_head(net->msg_cache, msg);
+		return true;
+	}
+
+	msg = l_new(struct mesh_msg, 1);
+	*msg = tst;
+	l_queue_push_head(net->msg_cache, msg);
+	l_debug("Add %4.4x + %6.6x + %8.8x", src, seq, mic);
+
+	if (l_queue_length(net->msg_cache) > MSG_CACHE_SIZE) {
+		msg = l_queue_peek_tail(net->msg_cache);
+		/* Remove Tail (oldest msg in cache) */
+		l_debug("Remove %4.4x + %6.6x + %8.8x",
+						msg->src, msg->seq, msg->mic);
+		if (l_queue_remove(net->msg_cache, msg))
+			l_free(msg);
+	}
+
+	return false;
+}
+
+static bool match_sar_seq0(const void *a, const void *b)
+{
+	const struct mesh_sar *sar = a;
+	uint16_t seqZero = L_PTR_TO_UINT(b);
+
+	return sar->seqZero == seqZero;
+}
+
+static bool match_sar_remote(const void *a, const void *b)
+{
+	const struct mesh_sar *sar = a;
+	uint16_t remote = L_PTR_TO_UINT(b);
+
+	return sar->remote == remote;
+}
+
+static bool match_msg_timeout(const void *a, const void *b)
+{
+	const struct mesh_sar *sar = a;
+	const struct l_timeout *msg_timeout = b;
+
+	return sar->msg_timeout == msg_timeout;
+}
+
+static bool match_sar_id(const void *a, const void *b)
+{
+	const struct mesh_sar *sar = a;
+	unsigned int id = L_PTR_TO_UINT(b);
+
+	return sar->id == id;
+}
+
+static bool match_seg_timeout(const void *a, const void *b)
+{
+	const struct mesh_sar *sar = a;
+	const struct l_timeout *seg_timeout = b;
+
+	return sar->seg_timeout == seg_timeout;
+}
+
+static bool match_dest_dst(const void *a, const void *b)
+{
+	const struct mesh_destination *dest = a;
+	uint16_t dst = L_PTR_TO_UINT(b);
+
+	return dst == dest->dst;
+}
+
+static bool match_frnd_dst(const void *a, const void *b)
+{
+	const struct mesh_friend *frnd = a;
+	uint16_t dst = L_PTR_TO_UINT(b);
+	int16_t i, grp_cnt = frnd->grp_cnt;
+	uint16_t *grp_list = frnd->grp_list;
+
+	/*
+	 * Determine if this message is for this friends unicast
+	 * address, and/or one of it's group/virtual addresses
+	 */
+	if (dst >= frnd->dst && dst < (frnd->dst + frnd->ele_cnt))
+		return true;
+
+	if (!(dst & 0x8000))
+		return false;
+
+	for (i = 0; i < grp_cnt; i++) {
+		if (dst == grp_list[i])
+			return true;
+	}
+
+	return false;
+}
+
+static bool is_lpn_friend(struct mesh_net *net, uint16_t addr, bool frnd)
+{
+	void *tst;
+
+	if (!frnd)
+		return false;
+
+	tst = l_queue_find(net->friends, match_frnd_dst, L_UINT_TO_PTR(addr));
+
+	return tst != NULL;
+}
+
+static bool is_us(struct mesh_net *net, uint16_t addr, bool src)
+{
+	void *tst;
+
+	if (IS_ALL_NODES(addr))
+		return true;
+
+	if (addr == FRIENDS_ADDRESS)
+		return net->friend_enable;
+
+	if (addr == RELAYS_ADDRESS)
+		return net->relay.enable;
+
+	if (addr == PROXIES_ADDRESS)
+		return net->proxy_enable;
+
+	if (addr >= net->src_addr && addr <= net->last_addr)
+		return true;
+
+	tst = l_queue_find(net->destinations, match_dest_dst,
+							L_UINT_TO_PTR(addr));
+
+	if (tst == NULL && !src)
+		tst = l_queue_find(net->friends, match_frnd_dst,
+							L_UINT_TO_PTR(addr));
+
+	return tst != NULL;
+}
+
+static struct mesh_friend_msg *mesh_friend_msg_new(uint8_t seg_max)
+{
+	struct mesh_friend_msg *frnd_msg;
+
+	if (seg_max) {
+		size_t size = sizeof(struct mesh_friend_msg) -
+					sizeof(struct mesh_friend_seg_one);
+
+		size += (seg_max + 1) * sizeof(struct mesh_friend_seg_12);
+		frnd_msg =  (struct mesh_friend_msg *) l_new(uint8_t, size);
+	} else
+		frnd_msg = l_new(struct mesh_friend_msg, 1);
+
+
+	return frnd_msg;
+}
+
+
+static bool match_ack(const void *a, const void *b)
+{
+	const struct mesh_friend_msg *old = a;
+	const struct mesh_friend_msg *rx = b;
+	uint32_t old_hdr;
+	uint32_t new_hdr;
+
+	/* Determine if old pkt is ACK to same SAR message that new ACK is */
+	if (!old->ctl || old->src != rx->src)
+		return false;
+
+	/* Check the quickest items first before digging deeper */
+	old_hdr = old->u.one[0].hdr & HDR_ACK_MASK;
+	new_hdr = rx->u.one[0].hdr & HDR_ACK_MASK;
+
+	return old_hdr == new_hdr;
+}
+
+static void enqueue_friend_pkt(void *a, void *b)
+{
+	struct mesh_friend *frnd = a;
+	struct mesh_friend_msg *pkt, *rx = b;
+	size_t size;
+	int16_t i;
+
+	if (rx->done)
+		return;
+
+	/*
+	 * Determine if this message is for this friends unicast
+	 * address, and/or one of it's group/virtual addresses
+	 */
+	if (rx->dst >= frnd->dst && (rx->dst - frnd->dst) < frnd->ele_cnt) {
+		rx->done = true;
+		goto enqueue;
+	}
+
+	if (!(rx->dst & 0x8000))
+		return;
+
+	if (!IS_ALL_NODES(rx->dst)) {
+		for (i = 0; i < frnd->grp_cnt; i++) {
+			if (rx->dst == frnd->grp_list[i])
+				goto enqueue;
+		}
+		return;
+	}
+
+enqueue:
+	/* Special handling for Seg Ack -- Only one per message queue */
+	if (((rx->u.one[0].hdr >> OPCODE_HDR_SHIFT) & OPCODE_MASK) ==
+						NET_OP_SEG_ACKNOWLEDGE) {
+		void *old_head = l_queue_peek_head(frnd->pkt_cache);
+		/* Suppress duplicate ACKs */
+		do {
+			void *old = l_queue_remove_if(frnd->pkt_cache,
+							match_ack, rx);
+
+			if (old) {
+				if (old_head == old) {
+					/*
+					 * If we are discarding head for any
+					 * reason, reset FRND SEQ
+					 */
+					frnd->last = frnd->seq;
+				}
+
+				l_free(old);
+			} else
+				break;
+
+		} while (true);
+	}
+
+	l_debug("%s for %4.4x from %4.4x ttl: %2.2x (seq: %6.6x) (ctl: %d)",
+			__func__, frnd->dst, rx->src, rx->ttl,
+			rx->u.one[0].seq, rx->ctl);
+
+	if (rx->cnt_in) {
+		size = sizeof(struct mesh_friend_msg) -
+				sizeof(struct mesh_friend_seg_one);
+		size += (rx->cnt_in + 1) * sizeof(struct mesh_friend_seg_12);
+	} else
+		size = sizeof(struct mesh_friend_msg);
+
+	pkt = l_malloc(size);
+	memcpy(pkt, rx, size);
+
+	l_queue_push_tail(frnd->pkt_cache, pkt);
+
+	if (l_queue_length(frnd->pkt_cache) > FRND_CACHE_MAX) {
+		/*
+		 * TODO: Guard against popping UPDATE packets
+		 * (disallowed per spec)
+		 */
+		pkt = l_queue_pop_head(frnd->pkt_cache);
+		l_free(pkt);
+		frnd->last = frnd->seq;
+	}
+}
+
+static void enqueue_update(void *a, void *b)
+{
+	struct mesh_friend *frnd = a;
+	struct mesh_friend_msg *pkt = b;
+
+	pkt->dst = frnd->dst;
+	pkt->done = false;
+	enqueue_friend_pkt(frnd, pkt);
+}
+
+static uint32_t seq_auth(uint32_t seq, uint16_t seqZero)
+{
+	uint32_t seqAuth = seqZero & SEQ_ZERO_MASK;
+
+	seqAuth |= seq & (~SEQ_ZERO_MASK);
+	if (seqAuth > seq)
+		seqAuth -= (SEQ_ZERO_MASK + 1);
+
+	return seqAuth;
+}
+
+static bool friend_packet_queue(struct mesh_net *net,
+					uint32_t iv_index,
+					bool ctl, uint8_t ttl,
+					uint32_t seq,
+					uint16_t src, uint16_t dst,
+					uint32_t hdr,
+					const uint8_t *data, uint16_t size)
+{
+	struct mesh_friend_msg *frnd_msg;
+	uint8_t seg_max = SEG_TOTAL(hdr);
+	bool ret;
+
+	if (seg_max && !IS_SEGMENTED(hdr))
+		return false;
+
+	frnd_msg = mesh_friend_msg_new(seg_max);
+
+	if (IS_SEGMENTED(hdr)) {
+		uint32_t seqAuth = seq_auth(seq, hdr >> SEQ_ZERO_HDR_SHIFT);
+		uint8_t i;
+
+		for (i = 0; i <= seg_max; i++) {
+			memcpy(frnd_msg->u.s12[i].data, data, 12);
+			frnd_msg->u.s12[i].hdr = hdr;
+			frnd_msg->u.s12[i].seq = seqAuth + i;
+			data += 12;
+			hdr += (1 << SEGO_HDR_SHIFT);
+		}
+		frnd_msg->u.s12[seg_max].seq = seq;
+		frnd_msg->cnt_in = seg_max;
+		frnd_msg->last_len = size % 12;
+		if (!frnd_msg->last_len)
+			frnd_msg->last_len = 12;
+	} else {
+		uint8_t opcode = hdr >> OPCODE_HDR_SHIFT;
+
+		if (ctl && opcode != NET_OP_SEG_ACKNOWLEDGE) {
+
+			/* Don't cache Friend Ctl opcodes */
+			if (FRND_OPCODE(opcode)) {
+				l_free(frnd_msg);
+				return false;
+			}
+
+			memcpy(frnd_msg->u.one[0].data + 1, data, size);
+			frnd_msg->last_len = size + 1;
+			frnd_msg->u.one[0].data[0] = opcode;
+		} else {
+			memcpy(frnd_msg->u.one[0].data, data, size);
+			frnd_msg->last_len = size;
+		}
+		frnd_msg->u.one[0].hdr = hdr;
+		frnd_msg->u.one[0].seq = seq;
+	}
+
+	frnd_msg->iv_index = iv_index;
+	frnd_msg->src = src;
+	frnd_msg->dst = dst;
+	frnd_msg->ctl = ctl;
+	frnd_msg->ttl = ttl;
+
+	/* Re-Package into Friend Delivery payload */
+	l_queue_foreach(net->friends, enqueue_friend_pkt, frnd_msg);
+	ret = frnd_msg->done;
+
+	/* TODO Optimization(?): Unicast messages keep this buffer */
+	l_free(frnd_msg);
+
+	return ret;
+}
+
+static void friend_ack_rxed(struct mesh_net *net, uint32_t iv_index,
+					uint32_t seq,
+					uint16_t src, uint16_t dst,
+					const uint8_t *pkt)
+{
+	uint32_t hdr = l_get_be32(pkt) &
+		((SEQ_ZERO_MASK << SEQ_ZERO_HDR_SHIFT) | /* Preserve SeqZero */
+		 (true << RELAY_HDR_SHIFT));		/* Preserve Relay bit */
+	uint32_t flags = l_get_be32(pkt + 3);
+	struct mesh_friend_msg frnd_ack = {
+		.ctl = true,
+		.iv_index = iv_index,
+		.src = src,
+		.dst = dst,
+		.last_len = sizeof(flags),
+		.u.one[0].seq = seq,
+		.done = false,
+	};
+
+	hdr |= NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT;
+	frnd_ack.u.one[0].hdr = hdr;
+	l_put_be32(flags, frnd_ack.u.one[0].data);
+	l_queue_foreach(net->friends, enqueue_friend_pkt, &frnd_ack);
+}
+
+static bool send_seg(struct mesh_net *net, struct mesh_sar *msg, uint8_t seg);
+
+static void send_frnd_ack(struct mesh_net *net, uint16_t src, uint16_t dst,
+						uint32_t hdr, uint32_t flags)
+{
+	uint32_t expected;
+	uint8_t msg[7];
+
+	/* We don't ACK from multicast destinations */
+	if (src & 0x8000)
+		return;
+
+	/* Calculate the "Full ACK" mask */
+	expected = 0xffffffff >> (31 - SEG_TOTAL(hdr));
+
+	/* Clear Hdr bits that don't apply to Seg ACK */
+	hdr &= ~((true << SEG_HDR_SHIFT) |
+			(OPCODE_MASK << OPCODE_HDR_SHIFT) |
+			(true << SZMIC_HDR_SHIFT) |
+			(SEG_MASK << SEGO_HDR_SHIFT) |
+			(SEG_MASK << SEGN_HDR_SHIFT));
+
+	hdr |= NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT;
+	hdr |= true << RELAY_HDR_SHIFT;
+
+	/* Clear all unexpected bits */
+	flags &= expected;
+
+	l_put_be32(hdr, msg);
+	l_put_be32(flags, msg + 3);
+
+	l_info("Send Friend ACK to Segs: %8.8x", flags);
+
+	if (is_lpn_friend(net, dst, true)) {
+		/* If we are acking our LPN Friend, queue, don't send */
+		friend_ack_rxed(net, mesh_net_get_iv_index(net),
+				mesh_net_next_seq_num(net), 0, dst, msg);
+	} else {
+		mesh_net_transport_send(net, NULL, false,
+				mesh_net_get_iv_index(net), DEFAULT_TTL,
+				0, 0, dst, msg, sizeof(msg));
+	}
+}
+
+static void send_net_ack(struct mesh_net *net, struct mesh_sar *sar,
+								uint32_t flags)
+{
+	uint8_t msg[7];
+	uint32_t hdr;
+	uint16_t src = sar->src;
+	uint16_t dst = sar->remote;
+
+	/* We don't ACK from multicast destinations */
+	if (src & 0x8000)
+		return;
+
+	/* We don't ACK segments as a Low Power Node */
+	if (net->friend_addr)
+		return;
+
+	hdr = NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT;
+	hdr |= sar->seqZero << SEQ_ZERO_HDR_SHIFT;
+
+	if (is_lpn_friend(net, src, true))
+		hdr |= true << RELAY_HDR_SHIFT;
+
+	l_put_be32(hdr, msg);
+	l_put_be32(flags, msg + 3);
+	l_info("Send%s ACK to Segs: %8.8x", sar->frnd ? " Friend" : "", flags);
+
+	if (is_lpn_friend(net, dst, true)) {
+		/* If we are acking our LPN Friend, queue, don't send */
+		friend_ack_rxed(net, mesh_net_get_iv_index(net),
+				mesh_net_next_seq_num(net), src, dst, msg);
+		return;
+	}
+
+	mesh_net_transport_send(net, NULL, false,
+				mesh_net_get_iv_index(net), DEFAULT_TTL,
+				0, src, dst, msg, sizeof(msg));
+}
+
+static void inseg_to(struct l_timeout *seg_timeout, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_sar *sar = l_queue_find(net->sar_in,
+					match_seg_timeout, seg_timeout);
+
+	l_timeout_remove(seg_timeout);
+	if (!sar)
+		return;
+
+	/* Send NAK */
+	l_info("Timeout %p %3.3x", sar, sar->app_idx);
+	send_net_ack(net, sar, sar->flags);
+
+	sar->seg_timeout = l_timeout_create(SEG_TO, inseg_to, net, NULL);
+}
+
+static void inmsg_to(struct l_timeout *msg_timeout, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_sar *sar = l_queue_remove_if(net->sar_in,
+			match_msg_timeout, msg_timeout);
+
+	l_timeout_remove(msg_timeout);
+	if (!sar)
+		return;
+
+	sar->msg_timeout = NULL;
+
+	/* print_packet("Incoming SAR Timeout", sar->buf, sar->len); */
+	mesh_sar_free(sar);
+}
+
+static void outmsg_to(struct l_timeout *msg_timeout, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_sar *sar = l_queue_remove_if(net->sar_out,
+			match_msg_timeout, msg_timeout);
+
+	l_timeout_remove(msg_timeout);
+	if (!sar)
+		return;
+
+	sar->msg_timeout = NULL;
+
+	if (sar->status_func)
+		sar->status_func(sar->remote, 1,
+				sar->buf, sar->len - 4,
+				sar->user_data);
+
+	/* print_packet("Outgoing SAR Timeout", sar->buf, sar->len); */
+	mesh_sar_free(sar);
+}
+
+static void outseg_to(struct l_timeout *seg_timeout, void *user_data);
+static void ack_received(struct mesh_net *net, bool timeout,
+				uint16_t src, uint16_t dst,
+				uint16_t seq0, uint32_t ack_flag)
+{
+	struct mesh_sar *outgoing;
+	uint32_t seg_flag = 0x00000001;
+	uint32_t ack_copy = ack_flag;
+	uint16_t i;
+
+	l_info("ACK Rxed (%x) (to:%d): %8.8x", seq0, timeout, ack_flag);
+
+	outgoing = l_queue_find(net->sar_out, match_sar_seq0,
+							L_UINT_TO_PTR(seq0));
+
+	if (!outgoing) {
+		l_info("Not Found: %4.4x", seq0);
+		return;
+	}
+
+	/*
+	 * TODO -- If we receive from different
+	 * SRC than we are sending to, make sure the OBO flag is set
+	 */
+
+	if ((!timeout && !ack_flag) ||
+			(outgoing->flags & ack_flag) == outgoing->flags) {
+		l_debug("ob_sar_removal (%x)", outgoing->flags);
+
+		/* Note: ack_flags == 0x00000000 is a remote Cancel request */
+		if (outgoing->status_func)
+			outgoing->status_func(src, ack_flag ? 0 : 1,
+					outgoing->buf,
+					outgoing->len - 4, outgoing->user_data);
+
+		l_queue_remove(net->sar_out, outgoing);
+		mesh_sar_free(outgoing);
+
+		return;
+	}
+
+	outgoing->last_nak |= ack_flag;
+
+	ack_copy &= outgoing->flags;
+
+	for (i = 0; i <= SEG_MAX(outgoing->len); i++, seg_flag <<= 1) {
+		if (seg_flag & ack_flag) {
+			l_debug("Skipping Seg %d of %d",
+					i, SEG_MAX(outgoing->len));
+			continue;
+		}
+
+		ack_copy |= seg_flag;
+
+		l_info("Resend Seg %d net:%p dst:%x app_idx:%3.3x",
+				i, net, outgoing->remote, outgoing->app_idx);
+
+		send_seg(net, outgoing, i);
+	}
+
+	l_timeout_remove(outgoing->seg_timeout);
+	outgoing->seg_timeout = l_timeout_create(SEG_TO, outseg_to, net, NULL);
+}
+
+static void outack_to(struct l_timeout *seg_timeout, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_sar *sar = l_queue_find(net->sar_out,
+					match_seg_timeout, seg_timeout);
+
+	l_timeout_remove(seg_timeout);
+	if (!sar)
+		return;
+
+	sar->seg_timeout = NULL;
+
+	/* Re-Send missing segments by faking NAK */
+	ack_received(net, true, sar->remote, sar->src,
+				sar->seqZero, sar->last_nak);
+}
+
+static void outseg_to(struct l_timeout *seg_timeout, void *user_data)
+{
+	struct mesh_net *net = user_data;
+	struct mesh_sar *sar = l_queue_find(net->sar_out,
+					match_seg_timeout, seg_timeout);
+
+	l_timeout_remove(seg_timeout);
+	if (!sar)
+		return;
+
+	sar->seg_timeout = NULL;
+
+	if (net->friend_addr) {
+		/* We are LPN -- Poll for ACK */
+		frnd_ack_poll(net);
+		sar->seg_timeout = l_timeout_create(SEG_TO,
+				outack_to, net, NULL);
+	} else {
+		/* Re-Send missing segments by faking NACK */
+		ack_received(net, true, sar->remote, sar->src,
+					sar->seqZero, sar->last_nak);
+	}
+}
+
+static bool msg_rxed(struct mesh_net *net, bool frnd,
+					uint32_t iv_index,
+					uint8_t ttl,
+					uint32_t seq,
+					uint16_t src, uint16_t dst,
+					uint8_t key_id,
+					bool szmic, uint16_t seqZero,
+					const uint8_t *data, uint16_t size)
+{
+	uint32_t seqAuth = seq_auth(seq, seqZero);
+
+	/* Sanity check seqAuth */
+	if (seqAuth > seq)
+		return false;
+
+	/* Save un-decrypted messages for our friends */
+	if (!frnd && l_queue_length(net->friends)) {
+		uint32_t hdr = key_id << KEY_HDR_SHIFT;
+		uint8_t frnd_ttl = ttl;
+
+		/* If not from us, decrement for our hop */
+		if (src < net->src_addr || src > net->last_addr) {
+			if (frnd_ttl > 1)
+				frnd_ttl--;
+			else
+				goto not_for_friend;
+		}
+
+		if (szmic || size > 15) {
+			hdr |= true << SEG_HDR_SHIFT;
+			hdr |= szmic << SZMIC_HDR_SHIFT;
+			hdr |= (seqZero & SEQ_ZERO_MASK) << SEQ_ZERO_HDR_SHIFT;
+			hdr |= SEG_MAX(size) << SEGN_HDR_SHIFT;
+		}
+
+		if (friend_packet_queue(net, iv_index, false, frnd_ttl,
+					seq, src, dst,
+					hdr, data, size))
+			return true;
+	}
+
+not_for_friend:
+	return mesh_model_rx(net, szmic, seqAuth, seq, iv_index,
+					ttl, src, dst, key_id, data, size);
+}
+
+static bool match_frnd_sar_dst(const void *a, const void *b)
+{
+	const struct mesh_friend_msg *frnd_msg = a;
+	uint16_t dst = L_PTR_TO_UINT(b);
+
+	return frnd_msg->dst == dst;
+}
+
+static void friend_seg_rxed(struct mesh_net *net,
+				uint32_t iv_index,
+				uint8_t ttl, uint32_t seq,
+				uint16_t src, uint16_t dst, uint32_t hdr,
+				const uint8_t *data, uint8_t size)
+{
+	struct mesh_friend *frnd = NULL;
+	struct mesh_friend_msg *frnd_msg = NULL;
+	uint8_t cnt;
+	uint8_t segN = hdr & 0x1f;
+	uint8_t segO = ((hdr >> 5) & 0x1f);
+	uint32_t expected = 0xffffffff >> (31 - segN);
+	uint32_t this_seg_flag = 0x00000001 << segO;
+	uint32_t largest = (0xffffffff << segO) & expected;
+	uint32_t hdr_key =  hdr & HDR_KEY_MASK;
+
+	frnd = l_queue_find(net->friends, match_frnd_dst,
+			L_UINT_TO_PTR(dst));
+	if (!frnd)
+		return;
+
+	if (frnd->last_hdr == hdr_key) {
+		/* We are no longer receiving this msg. Resend final ACK */
+		send_frnd_ack(net, dst, src, frnd->last_hdr, 0xffffffff);
+		return;
+	}
+
+	/* Check if we have a SAR-in-progress that matches incoming segment */
+	frnd_msg = l_queue_find(net->frnd_msgs, match_frnd_sar_dst,
+			L_UINT_TO_PTR(dst));
+
+	if (frnd_msg) {
+		/* Flush if SZMICN or IV Index has changed */
+		if (frnd_msg->iv_index != iv_index)
+			frnd_msg->u.s12[0].hdr = 0;
+
+		/* Flush incomplete old SAR message if it doesn't match */
+		if ((frnd_msg->u.s12[0].hdr & HDR_KEY_MASK) != hdr_key) {
+			l_queue_remove(net->frnd_msgs, frnd_msg);
+			l_free(frnd_msg);
+			frnd_msg = NULL;
+		}
+	}
+
+	if (!frnd_msg) {
+		frnd_msg = mesh_friend_msg_new(segN);
+		frnd_msg->iv_index = iv_index;
+		frnd_msg->src = src;
+		frnd_msg->dst = dst;
+		frnd_msg->ttl = ttl;
+		l_queue_push_tail(net->frnd_msgs, frnd_msg);
+	} else if (frnd_msg->flags & this_seg_flag) /* Ignore dup segs */
+		return;
+
+	cnt = frnd_msg->cnt_in;
+	frnd_msg->flags |= this_seg_flag;
+
+	frnd_msg->u.s12[cnt].hdr = hdr;
+	frnd_msg->u.s12[cnt].seq = seq;
+	memcpy(frnd_msg->u.s12[cnt].data, data, size);
+
+	/* Last segment could be short */
+	if (segN == segO)
+		frnd_msg->last_len = size;
+
+	l_info("RXed Seg %d, Flags %8.8x (cnt: %d)",
+						segO, frnd_msg->flags, cnt);
+
+	/* In reality, if one of these is true, then *both* must be true */
+	if ((cnt == segN) || (frnd_msg->flags == expected)) {
+		l_info("Full ACK");
+		send_frnd_ack(net, dst, src, hdr, frnd_msg->flags);
+
+		if (frnd_msg->ttl > 1) {
+			frnd_msg->ttl--;
+			/* Add to friends cache  */
+			l_queue_foreach(net->friends,
+					enqueue_friend_pkt, frnd_msg);
+		}
+
+		/* Remove from "in progress" queue */
+		l_queue_remove(net->frnd_msgs, frnd_msg);
+
+		/* TODO Optimization(?): Unicast messages keep this buffer */
+		l_free(frnd_msg);
+		return;
+	}
+
+	/* Always ACK if this is the largest outstanding segment */
+	if ((largest & frnd_msg->flags) == largest) {
+		l_info("Partial ACK");
+		send_frnd_ack(net, dst, src, hdr, frnd_msg->flags);
+	}
+
+	frnd_msg->cnt_in++;
+}
+
+static bool seg_rxed(struct mesh_net *net, bool frnd,
+					uint32_t iv_index,
+					uint8_t ttl,
+					uint32_t seq,
+					uint16_t src, uint16_t dst,
+					uint8_t key_id,
+					bool szmic, uint16_t seqZero,
+					uint8_t segO, uint8_t segN,
+					const uint8_t *data, uint8_t size)
+{
+	struct mesh_sar *sar_in = NULL;
+	uint16_t seg_off = 0;
+	uint32_t expected, this_seg_flag, largest, seqAuth;
+	bool reset_seg_to = true;
+
+	/*
+	 * DST could receive additional Segments after
+	 * completing due to a lost ACK, so re-ACK and discard
+	 */
+	sar_in = l_queue_find(net->sar_in, match_sar_remote,
+						L_UINT_TO_PTR(src));
+
+	/* Discard *old* incoming-SAR-in-progress if this segment newer */
+	seqAuth = seq_auth(seq, seqZero);
+	if (sar_in && (sar_in->seqAuth != seqAuth ||
+				sar_in->iv_index != iv_index)) {
+		bool newer;
+
+		if (iv_index > sar_in->iv_index)
+			newer = true;
+		else if (iv_index == sar_in->iv_index)
+			newer = seqAuth > sar_in->seqAuth;
+		else
+			newer = false;
+
+		if (newer) {
+			/* Cancel Old, start New */
+			l_queue_remove(net->sar_in, sar_in);
+			mesh_sar_free(sar_in);
+			sar_in = NULL;
+		} else
+			/* Ignore Old */
+			return false;
+	}
+
+	expected = 0xffffffff >> (31 - segN);
+
+	if (sar_in) {
+		l_info("RXed (old: %04x %06x size:%d) %d of %d",
+					seqZero, seq, size, segO, segN);
+		/* Sanity Check--> certain things must match */
+		if (SEG_MAX(sar_in->len) != segN ||
+				sar_in->key_id != key_id)
+			return false;
+
+		if (sar_in->flags == expected) {
+			/* Re-Send ACK for full msg */
+			if (!net->friend_addr)
+				send_net_ack(net, sar_in, expected);
+			return true;
+		}
+	} else {
+		uint16_t len = MAX_SEG_TO_LEN(segN);
+
+		l_info("RXed (new: %04x %06x size: %d len: %d) %d of %d",
+				seqZero, seq, size, len, segO, segN);
+		l_debug("Queue Size: %d", l_queue_length(net->sar_in));
+		sar_in = mesh_sar_new(len);
+		sar_in->seqAuth = seqAuth;
+		sar_in->iv_index = iv_index;
+		sar_in->src = dst;
+		sar_in->remote = src;
+		sar_in->seqZero = seqZero;
+		sar_in->key_id = key_id;
+		sar_in->len = len;
+		sar_in->last_seg = 0xff;
+		if (!net->friend_addr)
+			sar_in->msg_timeout = l_timeout_create(MSG_TO,
+					inmsg_to, net, NULL);
+
+		l_debug("First Seg %4.4x", sar_in->flags);
+		l_queue_push_head(net->sar_in, sar_in);
+	}
+	/* print_packet("Seg", data, size); */
+
+	seg_off = segO * MAX_SEG_LEN;
+	memcpy(sar_in->buf + seg_off, data, size);
+	this_seg_flag = 0x00000001 << segO;
+
+	/* Don't reset Seg TO or NAK if we already have this seg */
+	if (this_seg_flag & sar_in->flags)
+		reset_seg_to = false;
+
+	sar_in->flags |= this_seg_flag;
+	sar_in->ttl = ttl;
+
+	l_debug("Have Frags %4.4x", sar_in->flags);
+
+	/* Msg length only definitive on last segment */
+	if (segO == segN)
+		sar_in->len = segN * MAX_SEG_LEN + size;
+
+	if (sar_in->flags == expected) {
+		/* Got it all */
+		if (!net->friend_addr)
+			send_net_ack(net, sar_in, expected);
+
+		msg_rxed(net, frnd,
+				iv_index,
+				ttl,
+				seq,
+				sar_in->remote, dst,
+				key_id,
+				szmic, sar_in->seqZero,
+				sar_in->buf, sar_in->len);
+
+		/* Kill Inter-Seg timeout */
+		l_timeout_remove(sar_in->seg_timeout);
+		sar_in->seg_timeout = NULL;
+		return true;
+
+	} else if (reset_seg_to) {
+		/* Restart Inter-Seg Timeout */
+		l_timeout_remove(sar_in->seg_timeout);
+
+		/* if this is the largest outstanding segment, send NAK now */
+		if (!net->friend_addr) {
+			largest = (0xffffffff << segO) & expected;
+			if ((largest & sar_in->flags) == largest)
+				send_net_ack(net, sar_in, sar_in->flags);
+
+			sar_in->seg_timeout = l_timeout_create(SEG_TO,
+				inseg_to, net, NULL);
+		}
+	}
+
+	l_debug("NAK: %d expected:%08x largest:%08x flags:%08x",
+			reset_seg_to, expected, largest, sar_in->flags);
+	return false;
+}
+
+static bool ctl_received(struct mesh_net *net, bool frnd, uint32_t iv_index,
+						uint8_t ttl,
+						uint32_t seq,
+						uint16_t src, uint16_t dst,
+						uint8_t opcode, int8_t rssi,
+						const uint8_t *pkt, uint8_t len)
+{
+	uint8_t msg[12];
+	uint8_t rsp_ttl = DEFAULT_TTL;
+	uint8_t n = 0;
+
+	if (!frnd && ttl > 1) {
+		uint32_t hdr = opcode << OPCODE_HDR_SHIFT;
+		uint8_t frnd_ttl = ttl - 1;
+
+		if (friend_packet_queue(net, iv_index,
+					true, frnd_ttl,
+					seq,
+					src, dst,
+					hdr,
+					pkt, len))
+			return true;
+	}
+
+	/* Don't process other peoples Unicast destinations */
+	if (dst < 0x8000 && (dst < net->src_addr || dst > net->last_addr))
+		return false;
+
+	switch (opcode) {
+	default:
+		l_error("Unsupported Ctl Opcode: %2.2x", opcode);
+		break;
+
+	case NET_OP_FRND_POLL:
+		if (len != 1 || ttl)
+			return false;
+
+		print_packet("Rx-NET_OP_FRND_POLL", pkt, len);
+		friend_poll(net, src, !!(pkt[0]),
+				l_queue_find(net->friends,
+						match_by_friend,
+						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;
+
+		if (!IS_ALL_NODES(dst) && dst != FRIENDS_ADDRESS)
+			return false;
+
+		if (len != 10 || ttl)
+			return false;
+
+		print_packet("Rx-NET_OP_FRND_REQUEST", pkt, len);
+		friend_request(net, src, pkt[0], pkt[1],
+				l_get_be32(pkt + 1) & 0xffffff,
+				l_get_be16(pkt + 5), pkt[7],
+				l_get_be16(pkt + 8), rssi);
+		break;
+
+	case NET_OP_FRND_OFFER:
+		if (len != 6 || ttl)
+			return false;
+
+		print_packet("Rx-NET_OP_FRND_OFFER", pkt, len);
+		frnd_offer(net, src, pkt[0], pkt[1], pkt[2],
+				(int8_t) pkt[3], rssi, l_get_be16(pkt + 4));
+		break;
+
+	case NET_OP_FRND_CLEAR_CONFIRM:
+		if (len != 4)
+			return false;
+
+		print_packet("Rx-NET_OP_FRND_CLEAR_CONFIRM", pkt, len);
+		friend_clear_confirm(net, src, l_get_be16(pkt),
+						l_get_be16(pkt + 2));
+		break;
+
+	case NET_OP_FRND_CLEAR:
+		if (len != 4 || dst != net->src_addr)
+			return false;
+
+		print_packet("Rx-NET_OP_FRND_CLEAR", pkt, len);
+		friend_clear(net, src, l_get_be16(pkt), l_get_be16(pkt + 2),
+				l_queue_find(net->friends,
+					match_by_friend,
+					L_UINT_TO_PTR(l_get_be16(pkt))));
+		l_info("Remaining Friends: %d", l_queue_length(net->friends));
+		break;
+
+	case NET_OP_PROXY_SUB_ADD:
+		if (ttl)
+			return false;
+
+		print_packet("Rx-NET_OP_PROXY_SUB_ADD", pkt, len);
+		friend_sub_add(net, l_queue_find(net->friends,
+					match_by_friend, L_UINT_TO_PTR(src)),
+				pkt, len);
+		break;
+
+	case NET_OP_PROXY_SUB_REMOVE:
+		if (ttl)
+			return false;
+
+		print_packet("Rx-NET_OP_PROXY_SUB_REMOVE", pkt, len);
+		friend_sub_del(net, l_queue_find(net->friends,
+					match_by_friend, L_UINT_TO_PTR(src)),
+				pkt, len);
+		break;
+
+	case NET_OP_PROXY_SUB_CONFIRM:
+		if (ttl)
+			return false;
+
+		print_packet("Rx-NET_OP_PROXY_SUB_CONFIRM", pkt, len);
+		break;
+
+	case NET_OP_HEARTBEAT:
+		if (net->heartbeat.sub_enabled &&
+				src == net->heartbeat.sub_src) {
+			uint8_t hops = pkt[0] - ttl + 1;
+
+			print_packet("Rx-NET_OP_HEARTBEAT", pkt, len);
+
+			if (net->heartbeat.sub_count != 0xffff)
+				net->heartbeat.sub_count++;
+
+			if (net->heartbeat.sub_min_hops > hops)
+				net->heartbeat.sub_min_hops = hops;
+
+			if (net->heartbeat.sub_max_hops < hops)
+				net->heartbeat.sub_max_hops = hops;
+
+			l_info("HB: cnt:%4.4x min:%2.2x max:%2.2x",
+					net->heartbeat.sub_count,
+					net->heartbeat.sub_min_hops,
+					net->heartbeat.sub_max_hops);
+		}
+		break;
+	}
+
+	if (n) {
+		mesh_net_transport_send(net, NULL, false,
+				mesh_net_get_iv_index(net), rsp_ttl,
+				0, dst & 0x8000 ? 0 : dst, src,
+				msg, n);
+	}
+
+	return true;
+}
+
+static bool find_fast_hash(const void *a, const void *b)
+{
+	const uint64_t *entry = a;
+	const uint64_t *test = b;
+
+	return *entry == *test;
+}
+
+static void *check_fast_cache(struct mesh_net *net, uint64_t hash)
+{
+	void *found = l_queue_find(net->fast_cache, find_fast_hash, &hash);
+	uint64_t *new_hash;
+
+	if (found)
+		return NULL;
+
+	if (l_queue_length(net->fast_cache) >= 8)
+		new_hash = l_queue_pop_head(net->fast_cache);
+	else
+		new_hash = l_malloc(sizeof(hash));
+
+	*new_hash = hash;
+	l_queue_push_tail(net->fast_cache, new_hash);
+
+	return new_hash;
+}
+
+static bool match_keyset(const void *a, const void *b)
+{
+	const struct mesh_friend *frnd = a;
+	const struct mesh_key_set *key_set = b;
+
+	return (key_set == &frnd->key_set) || (key_set == &frnd->new_key_set);
+}
+
+static void try_decode(void *a, void *b)
+{
+	struct mesh_key_set *key_set = a;
+	struct net_decode *decode = b;
+	uint8_t tmp[29];
+	bool status;
+
+	if (decode->key_set || key_set->nid != decode->nid)
+		return;
+
+	status = mesh_crypto_packet_decode(decode->packet, decode->size,
+					decode->proxy, tmp, decode->iv_index,
+					key_set->enc_key, key_set->privacy_key);
+
+	if (!status)
+		return;
+
+	memcpy(decode->packet, tmp, decode->size);
+	decode->key_set = key_set;
+	if (key_set->frnd)
+		decode->frnd = l_queue_find(decode->net->friends,
+						match_keyset, key_set);
+	else
+		decode->frnd = NULL;
+}
+
+static struct mesh_key_set *net_packet_decode(struct mesh_net *net,
+				uint32_t iv_index, uint8_t nid,
+				struct mesh_friend **frnd,
+				bool proxy,
+				uint8_t *packet, uint8_t size)
+{
+	struct net_decode decode = {
+		.net = net,
+		.key_set = NULL,
+		.nid = nid,
+		.iv_index = iv_index,
+		.packet = packet,
+		.size = size,
+		.proxy = proxy,
+	};
+
+	l_queue_foreach(net->key_sets, try_decode, &decode);
+
+	if (decode.key_set != NULL) {
+		*frnd = decode.frnd;
+		return decode.key_set;
+	}
+	return NULL;
+}
+
+static bool match_key_nid(const void *a, const void *b)
+{
+	const struct mesh_key_set *key_set = a;
+	uint8_t nid = L_PTR_TO_UINT(b);
+
+	return key_set->nid == nid;
+}
+
+static bool match_by_dst(const void *a, const void *b)
+{
+	const struct mesh_destination *dest = a;
+	uint16_t dst = L_PTR_TO_UINT(b);
+
+	return dest->dst == dst;
+}
+
+static void send_relay_pkt(struct mesh_net *net, uint8_t *packet, uint8_t size)
+{
+	struct mesh_io *io = net->io;
+	struct mesh_io_send_info info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.interval = net->relay.interval,
+		.u.gen.cnt = net->relay.count,
+		.u.gen.min_delay = DEFAULT_MIN_DELAY,
+		.u.gen.max_delay = DEFAULT_MAX_DELAY
+	};
+
+	packet[0] = MESH_AD_TYPE_NETWORK;
+
+	mesh_io_send(io, &info, packet, size);
+}
+
+static void send_msg_pkt(struct mesh_net *net, uint8_t *packet, uint8_t size)
+{
+	struct mesh_io *io = net->io;
+	struct mesh_io_send_info info = {
+		.type = MESH_IO_TIMING_TYPE_GENERAL,
+		.u.gen.interval = net->tx_interval,
+		.u.gen.cnt = net->tx_cnt,
+		.u.gen.min_delay = DEFAULT_MIN_DELAY,
+		/* No extra randomization when sending regular mesh messages */
+		.u.gen.max_delay = DEFAULT_MIN_DELAY
+	};
+
+	packet[0] = MESH_AD_TYPE_NETWORK;
+
+	mesh_io_send(io, &info, packet, size);
+}
+
+static void packet_received(void *user_data, const void *data, uint8_t size,
+								int8_t rssi)
+{
+	struct mesh_net *net = user_data;
+	uint32_t iv_index;
+	uint8_t iv_flag;
+	uint8_t nid;
+	const uint8_t *msg = data;
+	uint8_t app_msg_len;
+	uint8_t net_ttl, net_key_id, net_segO, net_segN, net_opcode;
+	uint32_t net_seq, cache_cookie;
+	uint16_t net_src, net_dst, net_seqZero;
+	uint8_t packet[31];
+	bool net_ctl, net_segmented, net_szmic, net_relay;
+	struct mesh_friend *net_frnd;
+	bool drop = false;
+	uint64_t hash, *isNew = NULL;
+	struct mesh_key_set *keys;
+
+	nid = msg[0] & 0x7f;
+
+	/* Ignore unrecognized NIDs */
+	if (!(l_queue_find(net->key_sets, match_key_nid, L_UINT_TO_PTR(nid)))) {
+		/* print_packet("Nope", data, size); */
+		return;
+	}
+
+	iv_flag = msg[0] >> 7;
+	iv_index = net->iv_index;
+	l_debug("%s iv_index %d NID: %2.2x", __func__, iv_index, nid);
+
+	if (sizeof(uint16_t) <= sizeof(void *)) {
+		/* Add in additional cache to allow us to
+		 * avoid decrypting duplicatesr
+		 * Fast 64 bit Hash, Network MIC doesn't matter
+		 * With 64 bit hash, false pos chance is 1 in 1.8 * 10^19
+		 */
+		hash = l_get_le64(msg + 1) ^ l_get_le64(msg + 9);
+		isNew = check_fast_cache(net, hash);
+		if (!isNew)
+			return;
+
+		l_debug("New");
+	}
+
+	memcpy(packet + 2, data, size);
+
+	if (iv_index && (iv_index & 0x01) != iv_flag)
+		iv_index--;
+
+	/* Tester--Drop 90% of packets */
+	/* l_getrandom(&iv_flag, 1); */
+	/* if (iv_flag%10<9) drop = true; */
+
+	if (!drop)
+		print_packet("RX: Network [enc] :", data, size);
+
+	keys = net_packet_decode(net, iv_index, nid, &net_frnd, false,
+							packet + 2, size);
+	if (keys == NULL) {
+		l_debug("Failed to decode packet");
+		/* Remove fast-cache-hash */
+		l_queue_remove(net->fast_cache, isNew);
+		l_free(isNew);
+		return;
+	}
+
+	if (!drop)
+		print_packet("RX: Network [clr] :", packet + 2, size);
+
+	if (!mesh_crypto_packet_parse(packet + 2, size,
+					&net_ctl, &net_ttl,
+					&net_seq,
+					&net_src, &net_dst,
+					&cache_cookie,
+					&net_opcode,
+					&net_segmented,
+					&net_key_id,
+					&net_szmic, &net_relay, &net_seqZero,
+					&net_segO, &net_segN,
+					&msg, &app_msg_len)) {
+		l_error("Failed to parse packet content");
+		return;
+	}
+
+	/* Ignore incoming packets if we are LPN and frnd bit not set */
+	if (net->friend_addr) {
+		struct mesh_subnet *subnet;
+
+		subnet = l_queue_find(net->subnets, match_key_set, keys);
+		if (subnet)
+			return;
+
+		/* If the queue is empty, stop polling */
+		if (net_ctl && net_opcode == NET_OP_FRND_UPDATE && !msg[5])
+			frnd_poll_cancel(net);
+		else
+			frnd_poll(net, false);
+
+	} else if (net_dst == 0) {
+		l_error("illegal parms: DST: %4.4x Ctl: %d TTL: %2.2x",
+						net_dst, net_ctl, net_ttl);
+		return;
+	}
+
+	/* Ignore if we originally sent this */
+	if (is_us(net, net_src, true))
+		return;
+
+	if (drop) {
+		l_info("Dropping SEQ 0x%06x", net_seq);
+		return;
+	}
+
+	l_debug("check %08x", cache_cookie);
+
+	/* As a Relay, suppress repeats of last N packets that pass through */
+	/* The "cache_cookie" should be unique part of App message */
+	if (msg_in_cache(net, net_src, net_seq, cache_cookie))
+		return;
+
+	l_debug("RX: Network %04x -> %04x : TTL 0x%02x : IV : %8.8x SEQ 0x%06x",
+			net_src, net_dst, net_ttl, iv_index, net_seq);
+
+	if (is_us(net, net_dst, false) ||
+			is_lpn_friend(net, net_src, !!(net_frnd)) ||
+			(net_ctl && net_opcode == NET_OP_HEARTBEAT)) {
+
+		l_info("RX: App 0x%04x -> 0x%04x : TTL 0x%02x : SEQ 0x%06x",
+					net_src, net_dst, net_ttl, net_seq);
+
+		l_debug("seq:%x seq0:%x", net_seq, net_seqZero);
+		if (net_ctl) {
+			l_debug("CTL - %4.4x RX", net_seqZero);
+			if (net_opcode == NET_OP_SEG_ACKNOWLEDGE) {
+				/* Illegal to send ACK to non-Unicast Addr */
+				if (net_dst & 0x8000)
+					return;
+
+				/* print_packet("Got ACK", msg, app_msg_len); */
+				/* Pedantic check for correct size */
+				if (app_msg_len != 7)
+					return;
+
+				/* If this is an ACK to our friend queue-only */
+				if (is_lpn_friend(net, net_dst, true))
+					friend_ack_rxed(net, iv_index, net_seq,
+							net_src, net_dst,
+							msg);
+				else
+					ack_received(net, false,
+							net_src, net_dst,
+							net_seqZero,
+							l_get_be32(msg + 3));
+			} else {
+				ctl_received(net, !!(net_frnd), iv_index,
+						net_ttl, net_seq, net_src,
+						net_dst, net_opcode, rssi,
+						msg, app_msg_len);
+			}
+		} else if (net_segmented) {
+			/* If we accept SAR packets to non-Unicast, then
+			 * Friend Sar at least needs to be Unicast Only
+			 */
+			if (is_lpn_friend(net, net_dst, true) &&
+							!(net_dst & 0x8000)) {
+				/* Check TTL >= 2 before accepting segments
+				 * for Friends
+				 */
+				if (net_ttl >= 2) {
+					friend_seg_rxed(net, iv_index,
+						net_ttl, net_seq,
+						net_src, net_dst,
+						l_get_be32(packet + 2 + 9),
+						msg, app_msg_len);
+				}
+			} else {
+				seg_rxed(net, net_frnd,
+						iv_index,
+						net_ttl,
+						net_seq,
+						net_src, net_dst,
+						net_key_id,
+						net_szmic, net_seqZero,
+						net_segO, net_segN,
+						msg, app_msg_len);
+			}
+
+		} else {
+			msg_rxed(net, net_frnd,
+						iv_index,
+						net_ttl,
+						net_seq,
+						net_src, net_dst,
+						net_key_id,
+						false, net_seq & SEQ_ZERO_MASK,
+						msg, app_msg_len);
+		}
+
+		if (!!(net_frnd))
+			l_info("Ask for more data!");
+
+		/* If this is one of our Unicast addresses, don't relay */
+		if (net_dst <= 0x7fff)
+			return;
+	}
+
+	if (!net->relay.enable || net_ttl < 0x02 || net_frnd)
+		return;
+
+	packet[2 + 1] = (packet[2 + 1] & ~TTL_MASK) | (net_ttl - 1);
+
+	if (!mesh_crypto_packet_encode(packet + 2, size, keys->enc_key,
+					iv_index, keys->privacy_key)) {
+		l_error("Failed to encode relay packet");
+		return;
+	}
+
+	if (net->relay.enable)
+		send_relay_pkt(net, packet, size + 1);
+}
+
+static void net_msg_recv(void *user_data, struct mesh_io_recv_info *info,
+					const uint8_t *data, uint16_t len)
+{
+	struct mesh_net *net = user_data;
+	int8_t rssi = 0;
+
+	if (len <= 2 || !net)
+		return;
+
+	if (info) {
+		net->instant = info->instant;
+		net->chan = info->chan;
+		rssi = info->rssi;
+	}
+
+	packet_received(user_data, data + 1, len - 1, rssi);
+}
+
+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,
+							sizeof(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;
+
+	switch (net->iv_upd_state) {
+	case IV_UPD_UPDATING:
+		if (l_queue_length(net->sar_out)) {
+			l_info("don't leave IV Update until sar_out empty");
+			l_timeout_modify(net->iv_update_timeout, 10);
+			break;
+		}
+
+		l_info("iv_upd_state = IV_UPD_NORMAL_HOLD");
+		net->iv_upd_state = IV_UPD_NORMAL_HOLD;
+		l_timeout_modify(net->iv_update_timeout, IV_IDX_UPD_MIN);
+		mesh_net_set_seq_num(net, 0);
+		l_queue_foreach(net->subnets, set_network_beacon, net);
+		mesh_net_flush_msg_queues(net);
+		break;
+
+	case IV_UPD_INIT:
+	case IV_UPD_NORMAL_HOLD:
+	case IV_UPD_NORMAL:
+		l_timeout_remove(upd_timeout);
+		net->iv_update_timeout = NULL;
+		l_info("iv_upd_state = IV_UPD_NORMAL");
+		net->iv_upd_state = IV_UPD_NORMAL;
+		if (net->seq_num > IV_UPDATE_SEQ_TRIGGER)
+			mesh_net_iv_index_update(net);
+		break;
+	}
+}
+
+static void update_iv_kr_state(struct mesh_subnet *subnet, uint32_t iv_index,
+				bool iv_update, bool kr_transition,
+				bool rxed_key_refresh, bool lpn)
+{
+	struct mesh_net *net = subnet->net;
+	uint8_t local_kr;
+	uint32_t local_iv_index;
+	bool local_iv_update;
+
+	/* Save original settings to avoid resetting same values,
+	 * and secure beacon timer
+	 */
+	local_iv_index = net->iv_index;
+	local_kr = subnet->key_refresh;
+	local_iv_update = iv_is_updating(net);
+
+	if (iv_index != local_iv_index || kr_transition)
+		l_info("SNB-RX: %8.8x - Key Refresh: %d IV Update: %d",
+					iv_index, rxed_key_refresh, iv_update);
+
+	if (iv_update && (net->iv_upd_state > IV_UPD_UPDATING)) {
+		if (iv_index != net->iv_index)
+			l_error("Update attempted to0 soon (Normal < MIN)");
+
+		return;
+	}
+
+	if (net->iv_upd_state == IV_UPD_INIT) {
+		if (iv_index > net->iv_index)
+			mesh_net_set_seq_num(net, 0);
+		net->iv_index = iv_index;
+
+		if (iv_update) {
+			/* Other devices will be accepting old or new iv_index,
+			 * but we don't know how far through update they are.
+			 * Starting permissive state will allow us maximum
+			 * (96 hours) to resync
+			 */
+			l_info("iv_upd_state = IV_UPD_UPDATING");
+			net->iv_upd_state = IV_UPD_UPDATING;
+			net->iv_update_timeout = l_timeout_create(
+				IV_IDX_UPD_MIN, iv_upd_to, net, NULL);
+		} else {
+			l_info("iv_upd_state = IV_UPD_NORMAL");
+			net->iv_upd_state = IV_UPD_NORMAL;
+		}
+
+		storage_local_set_iv_index(net, iv_index, net->iv_upd_state);
+
+		/* Figure out the key refresh phase */
+		if (kr_transition) {
+			if (rxed_key_refresh)
+				mesh_net_key_refresh_phase_two(net,
+								subnet->idx);
+			else
+				mesh_net_key_refresh_finish(net, subnet->idx);
+		}
+
+		if (!lpn)
+			set_network_beacon(subnet, net);
+
+		return;
+	}
+
+	if (iv_update && !iv_is_updating(net)) {
+		l_info("iv_upd_state = IV_UPD_UPDATING");
+		net->iv_upd_state = IV_UPD_UPDATING;
+		net->iv_update_timeout = l_timeout_create(IV_IDX_UPD_MIN,
+							iv_upd_to, net, NULL);
+		storage_local_set_iv_index(net, iv_index, net->iv_upd_state);
+	} else if (iv_update && iv_index != net->iv_index) {
+		l_error("Update attempted too soon (iv idx already updated)");
+		return;
+	}
+
+	if (iv_index != local_iv_index || kr_transition)
+		l_info("IVindex 0x%8.8x / Key Refresh update received",
+								iv_index);
+
+	if (iv_index > net->iv_index) {
+		l_queue_clear(net->msg_cache, mesh_msg_free);
+		net->iv_index = iv_index;
+		storage_local_set_iv_index(net, iv_index, net->iv_upd_state);
+	}
+
+	/* Figure out the key refresh phase */
+	if (kr_transition) {
+		if (rxed_key_refresh)
+			mesh_net_key_refresh_phase_two(net, subnet->idx);
+		else
+			mesh_net_key_refresh_finish(net, subnet->idx);
+	}
+
+	if (!lpn)
+		return;
+
+	if (local_kr != subnet->key_refresh ||
+					local_iv_index != net->iv_index ||
+					local_iv_update != iv_is_updating(net))
+		set_network_beacon(subnet, net);
+}
+
+static void 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 iv_index;
+	uint64_t cmac;
+	bool iv_update, rxed_iv_update, rxed_key_refresh;
+	struct mesh_subnet *subnet;
+	struct net_key *keys;
+	bool kr_transition = false;
+
+	if (size != 22 || buf[0] != 0x01)
+		return;
+
+	/* print_packet("Secure Net Beacon RXed", data, size); */
+	rxed_key_refresh = (buf[1] & 0x01) == 0x01;
+	rxed_iv_update = iv_update = (buf[1] & 0x02) == 0x02;
+	iv_index = l_get_be32(buf + 10);
+
+	l_debug("KR: %d -- IVU: %d -- IV: %8.8x",
+				rxed_key_refresh, rxed_iv_update, iv_index);
+
+	/* Inhibit recognizing iv_update true-->false
+	 * if we have outbound SAR messages in flight
+	 */
+	if (l_queue_length(net->sar_out)) {
+		if (!iv_update && iv_update != iv_is_updating(net))
+			iv_update = true;
+	}
+
+	subnet = l_queue_find(net->subnets, match_network_id, buf + 2);
+	if (!subnet)
+		return;
+
+	/* Check if Key Refresh flag value is different from
+	 * the locally stored one
+	 */
+	if (rxed_key_refresh != subnet->key_refresh)
+		kr_transition = true;
+
+	/* If the local node is a provisioner or there are no new keys,
+	 * ignore KR beacon setting
+	 */
+	if (net->provisioner)
+		kr_transition = false;
+	else if (subnet->updated.key_set.nid == NET_NID_INVALID)
+		kr_transition = false;
+	/* If beacon's key refresh bit is not set and the beacon is encoded
+	 * with the "new" network key, this signals transition from
+	 * key refresh procedure to normal operation
+	 */
+	else if (!rxed_key_refresh &&
+			!memcmp(subnet->updated.network_id, buf + 2, 8))
+		kr_transition = true;
+
+	if ((net->iv_index + IV_IDX_DIFF_RANGE < iv_index) ||
+						(iv_index < net->iv_index)) {
+		l_info("iv index outside range");
+		return;
+	}
+
+	/* Don't bother going further if nothing has changed */
+	if (!memcmp(&subnet->snb.beacon[1], buf, size)) {
+		subnet->snb.observed++;
+		return;
+	}
+
+	if (!rxed_key_refresh && !subnet->key_refresh && !kr_transition)
+		keys = &subnet->current;
+	else if (subnet->updated.key_set.nid != NET_NID_INVALID)
+		keys = &subnet->updated;
+	else
+		return;
+
+	if (memcmp(keys->network_id, buf + 2, 8))
+		return;
+
+	/* Any behavioral changes must pass CMAC test */
+	if (!mesh_crypto_beacon_cmac(keys->beacon_key, keys->network_id,
+						iv_index, rxed_key_refresh,
+						rxed_iv_update, &cmac)) {
+		l_error("mesh_crypto_beacon_cmac failed");
+		return;
+	}
+
+	if (cmac != l_get_be64(buf + 14)) {
+		l_error("cmac compare failed %16.16lx != %16.16lx",
+						cmac, l_get_be64(buf + 14));
+		return;
+	}
+
+	if (iv_index == net->iv_index &&
+			iv_is_updating(net) == iv_update && !kr_transition) {
+		l_info("No change: IV index %4.4x, rxed KR = %d ",
+						iv_index, rxed_key_refresh);
+		if (net->iv_upd_state == IV_UPD_INIT) {
+			l_info("iv_upd_state = IV_UPD_NORMAL");
+			net->iv_upd_state = IV_UPD_NORMAL;
+		}
+
+		subnet->snb.observed++;
+		return;
+	}
+
+	subnet->snb.observed++;
+
+	update_iv_kr_state(subnet, iv_index, iv_update, kr_transition,
+						rxed_key_refresh, false);
+}
+
+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 iv_index;
+	bool iv_update, rxed_key_refresh;
+	struct mesh_subnet *subnet;
+	bool kr_transition = false;
+
+	/* print_packet("lpn: Secure Net Beacon RXed", data, size); */
+	rxed_key_refresh = (buf[0] & 0x01) == 0x01;
+	iv_index = l_get_be32(buf + 1);
+
+	l_debug("KR: %d -- IVU: %d -- IV: %8.8x",
+				rxed_key_refresh, iv_index);
+
+	/* Inhibit recognizing iv_update true-->false if we have outbound
+	 * SAR messages in flight
+	 */
+	if (l_queue_length(net->sar_out)) {
+		if (!iv_update && iv_update != iv_is_updating(net))
+			iv_update = true;
+	}
+
+	/* TODO: figure out actual network index (i.e., friendship subnet) */
+	subnet = get_primary_subnet(net);
+	if (!subnet)
+		return;
+
+	/* Check if Key Refresh flag value is different from
+	 * the locally stored one
+	 */
+	if (rxed_key_refresh != subnet->key_refresh)
+		kr_transition = true;
+
+	/* If the local node is a provisioner or there are no new keys,
+	 * ignore KR beacon setting
+	 */
+	if (subnet->updated.key_set.nid == NET_NID_INVALID)
+		kr_transition = false;
+
+	if ((net->iv_index + IV_IDX_DIFF_RANGE < iv_index) ||
+					(iv_index < net->iv_index)) {
+		l_info("iv index outside range");
+		return;
+	}
+
+	/* Don't bother going further if nothing has changed */
+	if (!kr_transition && iv_index == net->iv_index &&
+			iv_update == iv_is_updating(net) &&
+			net->iv_upd_state != IV_UPD_INIT)
+		return;
+
+	if (iv_index == net->iv_index &&
+			iv_is_updating(net) == iv_update && !kr_transition) {
+		l_info("No change: IV index %4.4x, rxed KR = %d ",
+						iv_index, rxed_key_refresh);
+		if (net->iv_upd_state == IV_UPD_INIT) {
+			l_info("iv_upd_state = IV_UPD_NORMAL");
+			net->iv_upd_state = IV_UPD_NORMAL;
+		}
+		return;
+	}
+
+	update_iv_kr_state(subnet, iv_index, iv_update, kr_transition,
+						rxed_key_refresh, true);
+}
+
+static void beacon_recv(void *user_data, struct mesh_io_recv_info *info,
+					const uint8_t *data, uint16_t len)
+{
+	struct mesh_net *net = user_data;
+	int8_t rssi = 0;
+
+	if (len <= 2 || !net)
+		return;
+
+	if (info) {
+		net->instant = info->instant;
+		net->chan = info->chan;
+		rssi = info->rssi;
+	}
+
+	process_beacon(user_data, data + 1, len - 1, rssi);
+}
+
+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;
+
+		net->beacon_enable = enable;
+
+		if (!enable)
+			mesh_io_send_cancel(net->io, &type, 1);
+
+		l_queue_foreach(net->subnets, start_network_beacon, net);
+	}
+
+	return true;
+}
+
+
+bool mesh_net_attach(struct mesh_net *net, struct mesh_io *io)
+{
+	if (!net)
+		return false;
+
+	net->io = io;
+
+	return true;
+}
+
+struct mesh_io *mesh_net_detach(struct mesh_net *net)
+{
+	struct mesh_io *io;
+	uint8_t type = 0;
+
+	if (!net)
+		return NULL;
+
+	io  = net->io;
+
+	mesh_io_send_cancel(net->io, &type, 1);
+	mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
+	mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_NET);
+
+	net->io = NULL;
+
+	return io;
+}
+
+bool mesh_net_iv_index_update(struct mesh_net *net)
+{
+	if (net->iv_upd_state != IV_UPD_NORMAL)
+		return false;
+
+	l_info("iv_upd_state = IV_UPD_UPDATING");
+	mesh_net_flush_msg_queues(net);
+	net->iv_upd_state = IV_UPD_UPDATING;
+	net->iv_index++;
+	if (!storage_local_set_iv_index(net, net->iv_index, IV_UPD_UPDATING))
+		return false;
+
+	l_queue_foreach(net->subnets, set_network_beacon, net);
+	net->iv_update_timeout = l_timeout_create(
+			IV_IDX_UPD_MIN,
+			iv_upd_to, net, NULL);
+
+	return true;
+}
+
+
+
+void mesh_net_sub_list_add(struct mesh_net *net, uint16_t addr)
+{
+	uint8_t msg[11] = { PROXY_OP_FILTER_ADD };
+	uint8_t n = 1;
+
+	l_put_be16(addr, msg + n);
+	n += 2;
+
+	mesh_net_transport_send(net, NULL, false,
+			mesh_net_get_iv_index(net), 0,
+			0, 0, 0, msg, n);
+}
+
+void mesh_net_sub_list_del(struct mesh_net *net, uint16_t addr)
+{
+	uint8_t msg[11] = { PROXY_OP_FILTER_DEL };
+	uint8_t n = 1;
+
+	l_put_be16(addr, msg + n);
+	n += 2;
+
+	mesh_net_transport_send(net, NULL, false,
+			mesh_net_get_iv_index(net), 0,
+			0, 0, 0, msg, n);
+}
+
+bool mesh_net_dst_reg(struct mesh_net *net, uint16_t dst)
+{
+	struct mesh_destination *dest = l_queue_find(net->destinations,
+					match_by_dst, L_UINT_TO_PTR(dst));
+
+	if (IS_UNASSIGNED(dst) || IS_ALL_NODES(dst))
+		return false;
+
+	if (!dest) {
+		dest = l_new(struct mesh_destination, 1);
+
+		if (dst < 0x8000)
+			l_queue_push_head(net->destinations, dest);
+		else
+			l_queue_push_tail(net->destinations, dest);
+
+		/* If LPN, and Group/Virtual, add to Subscription List */
+		if (net->friend_addr) {
+			/* TODO: Fix this garbage */
+			uint32_t u32_dst[7] = {dst, 0xffffffff};
+
+			frnd_sub_add(net, u32_dst);
+		}
+	}
+
+	dest->dst = dst;
+	dest->ref_cnt++;
+
+	return true;
+}
+
+bool mesh_net_dst_unreg(struct mesh_net *net, uint16_t dst)
+{
+	struct mesh_destination *dest = l_queue_find(net->destinations,
+					match_by_dst, L_UINT_TO_PTR(dst));
+
+	if (!dest)
+		return false;
+
+	if (dest->ref_cnt)
+		dest->ref_cnt--;
+
+	if (dest->ref_cnt)
+		return true;
+
+	/* TODO: If LPN, and Group/Virtual, remove from Subscription List */
+	if (net->friend_addr) {
+		/* TODO: Fix this garbage */
+		uint32_t u32_dst[7] = {dst, 0xffffffff};
+
+		frnd_sub_del(net, u32_dst);
+	}
+
+	l_queue_remove(net->destinations, dest);
+
+	l_free(dest);
+	return true;
+}
+
+bool mesh_net_flush(struct mesh_net *net)
+{
+	if (!net)
+		return false;
+
+	/* TODO mesh-io Flush */
+	return true;
+}
+
+/* TODO: add net key index */
+static bool send_seg(struct mesh_net *net, struct mesh_sar *msg, uint8_t segO)
+{
+	uint8_t seg_len;
+	uint8_t gatt_data[30];
+	uint8_t *packet = gatt_data;
+	uint8_t packet_len;
+	uint8_t segN = SEG_MAX(msg->len);
+	uint16_t seg_off = SEG_OFF(segO);
+	struct mesh_key_set *key_set = NULL;
+	uint32_t seq_num = mesh_net_next_seq_num(net);
+
+	if (segN) {
+		if (msg->len - seg_off > SEG_OFF(1))
+			seg_len = SEG_OFF(1);
+		else
+			seg_len = msg->len - seg_off;
+	} else {
+		seg_len = msg->len;
+	}
+
+	/* Start IV Update procedure when we hit our trigger point */
+	if (!msg->frnd && net->seq_num > IV_UPDATE_SEQ_TRIGGER)
+		mesh_net_iv_index_update(net);
+
+	l_debug("segN %d segment %d seg_off %d", segN, segO, seg_off);
+	/* print_packet("Sending", msg->buf + seg_off, seg_len); */
+	{
+		/* TODO: Are we RXing on an LPN's behalf? Then set RLY bit */
+
+		if (!mesh_crypto_packet_build(false, msg->ttl,
+					seq_num,
+					msg->src, msg->remote,
+					0,
+					segN ? true : false, msg->key_id,
+					msg->szmic, false, msg->seqZero,
+					segO, segN,
+					msg->buf + seg_off, seg_len,
+					packet + 1, &packet_len)) {
+			l_error("Failed to build packet");
+			return false;
+		}
+	}
+	print_packet("Clr-Net Tx", packet + 1, packet_len);
+
+	if (msg->frnd_cred && net->friend_addr)
+		key_set = frnd_get_key(net);
+
+	if (key_set == NULL) {
+		struct mesh_subnet *subnet = get_primary_subnet(net);
+
+		key_set = &subnet->tx->key_set;
+	}
+
+	if (!mesh_crypto_packet_encode(packet + 1, packet_len, key_set->enc_key,
+					msg->iv_index, key_set->privacy_key)) {
+		l_error("Failed to encode packet");
+		return false;
+	}
+
+	print_packet("Step 3", packet + 1, packet_len);
+	if (!mesh_crypto_packet_label(packet + 1, packet_len,
+				msg->iv_index, key_set->nid)) {
+		l_error("Failed to label packet");
+		return false;
+	}
+	/* print_packet("Step 4", packet + 1, packet_len); */
+
+	{
+		char *str;
+
+		send_msg_pkt(net, packet, packet_len + 1);
+
+		str = l_util_hexstring(packet + 1, packet_len);
+		l_info("TX: Network %04x -> %04x : %s (%u) : TTL %d : SEQ %06x",
+				msg->src, msg->remote, str,
+				packet_len, msg->ttl,
+				msg->frnd ? msg->seqAuth + segO : seq_num);
+		l_free(str);
+	}
+
+	msg->last_seg = segO;
+
+	return true;
+}
+
+void mesh_net_send_seg(struct mesh_net *net, struct mesh_key_set *key_set,
+				uint32_t iv_index,
+				uint8_t ttl,
+				uint32_t seq,
+				uint16_t src, uint16_t dst,
+				uint32_t hdr,
+				const void *seg, uint16_t seg_len)
+{
+	char *str;
+	uint8_t packet[30];
+	uint8_t packet_len;
+	bool segmented = !!((hdr >> SEG_HDR_SHIFT) & true);
+	uint8_t key_id = (hdr >> KEY_HDR_SHIFT) & KEY_ID_MASK;
+	bool szmic = !!((hdr >> SZMIC_HDR_SHIFT) & true);
+	uint16_t seqZero = (hdr >> SEQ_ZERO_HDR_SHIFT) & SEQ_ZERO_MASK;
+	uint8_t segO = (hdr >> SEGO_HDR_SHIFT) & SEG_MASK;
+	uint8_t segN = (hdr >> SEGN_HDR_SHIFT) & SEG_MASK;
+
+	/* TODO: Only used for current POLLed segments to LPNs */
+
+	l_debug("SEQ: %6.6x", seq + segO);
+	l_debug("SEQ0: %6.6x", seq);
+	l_debug("segO: %d", segO);
+
+	if (!mesh_crypto_packet_build(false, ttl,
+				seq,
+				src, dst,
+				0,
+				segmented, key_id,
+				szmic, false, seqZero,
+				segO, segN,
+				seg, seg_len,
+				packet + 1, &packet_len)) {
+		l_error("Failed to build packet");
+		return;
+	}
+
+	if (!mesh_crypto_packet_encode(packet + 1, packet_len, key_set->enc_key,
+					iv_index, key_set->privacy_key)) {
+		l_error("Failed to encode packet");
+		return;
+	}
+
+	/* print_packet("Step 3", packet + 0, packet_len); */
+	if (!mesh_crypto_packet_label(packet + 1, packet_len, iv_index,
+							key_set->nid)) {
+		l_error("Failed to label packet");
+		return;
+	}
+	/* print_packet("Step 4", packet + 1, packet_len); */
+
+	send_msg_pkt(net, packet, packet_len + 1);
+
+	str = l_util_hexstring(packet + 1, packet_len);
+	l_info("TX: Friend Seg-%d %04x -> %04x : %s (%u) : TTL %d : SEQ %06x",
+			segO, src, dst, str, packet_len, ttl, seq);
+	l_free(str);
+}
+
+unsigned int mesh_net_app_send(struct mesh_net *net, bool frnd_cred,
+				uint16_t src, uint16_t dst,
+				uint8_t key_id, uint8_t ttl,
+				uint32_t seq, uint32_t iv_index,
+				bool szmic,
+				const void *msg, uint16_t msg_len,
+				mesh_net_status_func_t status_func,
+				void *user_data)
+{
+	struct mesh_sar *payload = NULL;
+	uint8_t seg, seg_max;
+	unsigned int ret = 0;
+	bool result;
+
+	if (!net || msg_len > 384)
+		return 0;
+
+	if (!src)
+		src = net->src_addr;
+
+	if (!src || !dst)
+		return 0;
+
+	if (ttl == 0xff)
+		ttl = net->default_ttl;
+
+	seg_max = SEG_MAX(msg_len);
+
+	/* First enqueue to any Friends and internal models */
+	result = msg_rxed(net, false,
+				iv_index,
+				ttl,
+				seq + seg_max,
+				src, dst,
+				key_id,
+				szmic, seq & SEQ_ZERO_MASK,
+				msg, msg_len);
+
+	/* If successfully enqued or delivered
+	 * to Unicast address, we are done
+	 */
+	if (result || src == dst ||
+			(dst >= net->src_addr && dst <= net->last_addr)) {
+		/* Adjust our seq_num for "virtual" delivery */
+		net->seq_num += seg_max;
+		mesh_net_next_seq_num(net);
+		return 0;
+	}
+
+	/* If Segmented, Cancel any OB segmented message to same DST */
+	if (seg_max) {
+		payload = l_queue_remove_if(net->sar_out, match_sar_remote,
+							L_UINT_TO_PTR(dst));
+		mesh_sar_free(payload);
+	}
+
+	/* Setup OTA Network send */
+	payload = mesh_sar_new(msg_len);
+	memcpy(payload->buf, msg, msg_len);
+	payload->len = msg_len;
+	payload->src = src;
+	payload->remote = dst;
+	payload->ttl = ttl;
+	payload->szmic = szmic;
+	payload->frnd_cred = frnd_cred;
+	payload->key_id = key_id;
+	if (seg_max) {
+		payload->flags = 0xffffffff >> (31 - seg_max);
+		payload->seqZero = seq & SEQ_ZERO_MASK;
+	}
+
+	payload->iv_index = mesh_net_get_iv_index(net);
+	payload->seqAuth = net->seq_num;
+
+	result = true;
+	if (!IS_UNICAST(dst) && seg_max) {
+		for (int i = 0; i < 4; i++) {
+			for (seg = 0; seg <= seg_max && result; seg++)
+				result = send_seg(net, payload, seg);
+		}
+	} else {
+		for (seg = 0; seg <= seg_max && result; seg++)
+			result = send_seg(net, payload, seg);
+	}
+
+	/* Reliable: Cache; Unreliable: Flush*/
+	if (result && seg_max && IS_UNICAST(dst)) {
+		l_queue_push_head(net->sar_out, payload);
+		payload->seg_timeout =
+			l_timeout_create(SEG_TO, outseg_to, net, NULL);
+		payload->msg_timeout =
+			l_timeout_create(MSG_TO, outmsg_to, net, NULL);
+		payload->status_func = status_func;
+		payload->user_data = user_data;
+		ret = payload->id = ++net->sar_id_next;
+	} else
+		mesh_sar_free(payload);
+
+	return ret;
+}
+
+void mesh_net_app_send_cancel(struct mesh_net *net, unsigned int id)
+{
+	struct mesh_sar *sar = l_queue_remove_if(net->sar_out, match_sar_id,
+						L_UINT_TO_PTR(id));
+
+	if (sar) {
+		l_info("Canceling OB %d", id);
+		if (sar->status_func)
+			sar->status_func(sar->remote, 2,
+				sar->buf, sar->len - 4,
+				sar->user_data);
+	}
+	mesh_sar_free(sar);
+}
+
+/* TODO: add net key index */
+void mesh_net_ack_send(struct mesh_net *net, struct mesh_key_set *key_set,
+				uint32_t iv_index,
+				uint8_t ttl,
+				uint32_t seq,
+				uint16_t src, uint16_t dst,
+				bool rly, uint16_t seqZero,
+				uint32_t ack_flags)
+{
+	uint32_t hdr;
+	uint8_t data[7];
+	uint8_t pkt_len;
+	uint8_t pkt[30];
+	char *str;
+
+	hdr = NET_OP_SEG_ACKNOWLEDGE << OPCODE_HDR_SHIFT;
+	hdr |= rly << RELAY_HDR_SHIFT;
+	hdr |= (seqZero & SEQ_ZERO_MASK) << SEQ_ZERO_HDR_SHIFT;
+	l_put_be32(hdr, data);
+	l_put_be32(ack_flags, data + 3);
+	if (!mesh_crypto_packet_build(true, ttl,
+					seq,
+					src, dst,
+					NET_OP_SEG_ACKNOWLEDGE,
+					false, /* Not Segmented */
+					0,	/* No Key ID associated */
+					false, rly, seqZero,
+					0, 0,	/* no segO or segN */
+					data + 1, 6,
+					pkt + 1, &pkt_len)) {
+		return;
+	}
+
+	if (key_set == NULL) {
+		struct mesh_subnet *subnet = get_primary_subnet(net);
+
+		key_set = &subnet->tx->key_set;
+	}
+
+	if (!mesh_crypto_packet_encode(pkt + 1, pkt_len, key_set->enc_key,
+				iv_index, key_set->privacy_key)) {
+		l_error("Failed to encode packet");
+		return;
+	}
+
+	/* print_packet("Step 3", pkt, pkt_len); */
+	if (!mesh_crypto_packet_label(pkt + 1, pkt_len,
+				iv_index, key_set->nid)) {
+		l_error("Failed to label packet");
+		return;
+	}
+
+	/* print_packet("Step 4", pkt, pkt_len); */
+	send_msg_pkt(net, pkt, pkt_len + 1);
+
+	str = l_util_hexstring(pkt + 1, pkt_len);
+	l_info("TX: Friend ACK %04x -> %04x : %s (%u) : TTL %d : SEQ %06x",
+			src, dst, str, pkt_len,
+			ttl, seq);
+	l_free(str);
+}
+
+/* TODO: add net key index */
+void mesh_net_transport_send(struct mesh_net *net, struct mesh_key_set *key_set,
+				bool fast, uint32_t iv_index, uint8_t ttl,
+				uint32_t seq, uint16_t src, uint16_t dst,
+				const uint8_t *msg, uint16_t msg_len)
+{
+	uint32_t use_seq = seq;
+	uint8_t pkt_len;
+	uint8_t pkt[30];
+	bool result = false;
+
+	if (!net->src_addr)
+		return;
+
+	if (!src)
+		src = net->src_addr;
+
+	if (src == dst)
+		return;
+
+	if (ttl == 0xff)
+		ttl = net->default_ttl;
+
+	/* Range check the Opcode and msg length*/
+	if (*msg & 0xc0 || (9 + msg_len + 8 > 29))
+		return;
+
+	/* Enqueue for Friend if forwardable and from us */
+	if (!(key_set) && src >= net->src_addr && src <= net->last_addr) {
+		uint32_t hdr = msg[0] << OPCODE_HDR_SHIFT;
+		uint8_t frnd_ttl = ttl;
+
+		if (friend_packet_queue(net, iv_index,
+					true, frnd_ttl,
+					mesh_net_next_seq_num(net),
+					src, dst,
+					hdr,
+					msg + 1, msg_len - 1)) {
+			return;
+		}
+	}
+
+	/* Deliver to Local entities if applicable */
+	if (!(dst & 0x8000) && src >= net->src_addr && src <= net->last_addr) {
+		result = ctl_received(net, !!(key_set),
+					iv_index, ttl,
+					mesh_net_next_seq_num(net),
+					src, dst,
+					msg[0], 0, msg + 1, msg_len - 1);
+	}
+
+	if (key_set == NULL) {
+		struct mesh_subnet *subnet = get_primary_subnet(net);
+
+		key_set = &subnet->tx->key_set;
+		use_seq = mesh_net_next_seq_num(net);
+
+		if (result || (dst >= net->src_addr && dst <= net->last_addr))
+			return;
+	}
+
+	if (!mesh_crypto_packet_build(true, ttl,
+				use_seq,
+				src, dst,
+				msg[0],
+				false, 0,
+				false, false, 0,
+				0, 0,
+				msg + 1, msg_len - 1,
+				pkt + 1, &pkt_len))
+		return;
+
+	/* print_packet("Step 2", pkt + 1, pkt_len); */
+
+	if (!mesh_crypto_packet_encode(pkt + 1, pkt_len, key_set->enc_key,
+					iv_index, key_set->privacy_key)) {
+		l_error("Failed to encode pkt");
+		return;
+	}
+
+	/* print_packet("Step 3", pkt + 1, pkt_len); */
+	if (!mesh_crypto_packet_label(pkt, pkt_len, iv_index,
+							key_set->nid)) {
+		l_error("Failed to label pkt");
+		return;
+	}
+
+	/* print_packet("Step 4", pkt + 1, pkt_len); */
+
+	if (dst != 0) {
+		char *str;
+
+		send_msg_pkt(net, pkt, pkt_len + 1);
+
+		str = l_util_hexstring(pkt + 1, pkt_len);
+		l_info("TX: Network %04x -> %04x : %s (%u) : TTL %d : SEQ %06x",
+				src, dst, str, pkt_len,
+				ttl, use_seq);
+		l_free(str);
+	}
+}
+
+uint8_t mesh_net_key_refresh_phase_set(struct mesh_net *net, uint16_t idx,
+							uint8_t transition)
+{
+	struct mesh_subnet *subnet;
+
+	if (!net)
+		return MESH_STATUS_UNSPECIFIED_ERROR;
+
+	subnet = l_queue_find(net->subnets, match_key_index,
+							L_UINT_TO_PTR(idx));
+	if (!subnet)
+		return MESH_STATUS_INVALID_NETKEY;
+
+	if (transition == subnet->kr_phase)
+		return MESH_STATUS_SUCCESS;
+
+	if ((transition != 2 && transition != 3) ||
+						transition < subnet->kr_phase)
+		return MESH_STATUS_CANNOT_SET;
+
+	switch (transition) {
+	case 2:
+		if (mesh_net_key_refresh_phase_two(net, idx)
+							!= MESH_STATUS_SUCCESS)
+			return MESH_STATUS_CANNOT_SET;
+		break;
+	case 3:
+		if (mesh_net_key_refresh_finish(net, idx)
+							!= MESH_STATUS_SUCCESS)
+			return MESH_STATUS_CANNOT_SET;
+		break;
+	default:
+		return MESH_STATUS_CANNOT_SET;
+	}
+	return MESH_STATUS_SUCCESS;
+}
+
+uint8_t mesh_net_key_refresh_phase_get(struct mesh_net *net, uint16_t idx,
+								uint8_t *phase)
+{
+	struct mesh_subnet *subnet;
+
+	if (!net)
+		return MESH_STATUS_UNSPECIFIED_ERROR;
+
+	subnet = l_queue_find(net->subnets, match_key_index,
+							L_UINT_TO_PTR(idx));
+	if (!subnet)
+		return MESH_STATUS_INVALID_NETKEY;
+
+	*phase = subnet->kr_phase;
+	return MESH_STATUS_SUCCESS;
+}
+
+int mesh_net_kr_phase_one(struct mesh_net *net, uint16_t idx,
+							const uint8_t *value)
+{
+	struct mesh_subnet *subnet;
+
+	if (!net)
+		return MESH_STATUS_UNSPECIFIED_ERROR;
+
+	subnet = l_queue_find(net->subnets, match_key_index,
+							L_UINT_TO_PTR(idx));
+	if (!subnet)
+		return MESH_STATUS_CANNOT_UPDATE;
+
+	if (subnet->updated.key_set.nid != NET_NID_INVALID)
+		l_info("Warning: overwriting new keys");
+
+	/* Preserve starting data */
+	subnet->updated = subnet->current;
+
+	/* Generate new keys */
+	if (!create_keys(net, &subnet->updated, value)) {
+		subnet->updated.key_set.nid = NET_NID_INVALID;
+		l_error("Failed to start key refresh phase one");
+		return MESH_STATUS_CANNOT_UPDATE;
+	}
+
+	/* If we are an LPN, generate our keys here */
+	if (net->friend_addr)
+		frnd_key_refresh(net, 1);
+	else
+		/* If we are a Friend-Node, generate all our new keys */
+		l_queue_foreach(net->friends, frnd_kr_phase1, (void *)value);
+
+	l_info("key refresh phase 1: NID 0x%2x", subnet->updated.key_set.nid);
+
+	mesh_net_add_keyset(net, &subnet->updated.key_set);
+	subnet->kr_phase = KEY_REFRESH_PHASE_ONE;
+
+	return MESH_STATUS_SUCCESS;
+}
+
+int mesh_net_key_refresh_phase_two(struct mesh_net *net, uint16_t idx)
+{
+	struct mesh_subnet *subnet;
+
+	if (!net)
+		return MESH_STATUS_UNSPECIFIED_ERROR;
+
+	subnet = l_queue_find(net->subnets, match_key_index,
+							L_UINT_TO_PTR(idx));
+
+	if (!subnet || subnet->updated.key_set.nid == NET_NID_INVALID)
+		return MESH_STATUS_INVALID_NETKEY;
+
+	l_info("Key refresh procedure phase 2: start using new net TX keys");
+	subnet->key_refresh = 1;
+	subnet->tx = &subnet->updated;
+	/* TODO: Provisioner may need to stay in phase three until
+	 * it hears beacons from all the nodes
+	 */
+	subnet->kr_phase = KEY_REFRESH_PHASE_TWO;
+	set_network_beacon(subnet, net);
+
+	if (net->friend_addr)
+		frnd_key_refresh(net, 2);
+	else
+		l_queue_foreach(net->friends, frnd_kr_phase2, net);
+
+	return MESH_STATUS_SUCCESS;
+}
+
+int mesh_net_key_refresh_finish(struct mesh_net *net, uint16_t idx)
+{
+	struct mesh_subnet *subnet;
+
+	if (!net)
+		return MESH_STATUS_UNSPECIFIED_ERROR;
+
+	subnet = l_queue_find(net->subnets, match_key_index,
+							L_UINT_TO_PTR(idx));
+
+	if (!subnet || subnet->updated.key_set.nid == NET_NID_INVALID)
+		return MESH_STATUS_INVALID_NETKEY;
+
+	if (subnet->kr_phase == KEY_REFRESH_PHASE_NONE)
+		return MESH_STATUS_SUCCESS;
+
+	l_info("Key refresh phase 3: use new keys only, discard old ones");
+
+	/* Switch to using new keys, discard old ones */
+	subnet->current = subnet->updated;
+	subnet->tx = &subnet->current;
+	subnet->updated.key_set.nid = NET_NID_INVALID;
+	mesh_net_remove_keyset(net, &subnet->updated.key_set);
+	subnet->key_refresh = 0;
+	subnet->kr_phase = KEY_REFRESH_PHASE_NONE;
+	set_network_beacon(subnet, net);
+
+	if (net->friend_addr)
+		frnd_key_refresh(net, 3);
+	else
+		l_queue_foreach(net->friends, frnd_kr_phase3, net);
+
+	return MESH_STATUS_SUCCESS;
+}
+
+void mesh_net_heartbeat_send(struct mesh_net *net)
+{
+	struct mesh_net_heartbeat *hb = &net->heartbeat;
+	uint8_t msg[4];
+	int n = 0;
+
+	if (hb->pub_dst == UNASSIGNED_ADDRESS)
+		return;
+
+	msg[n++] = NET_OP_HEARTBEAT;
+	msg[n++] = hb->pub_ttl;
+	l_put_be16(hb->features, msg + n);
+	n += 2;
+
+	mesh_net_transport_send(net, NULL, false, mesh_net_get_iv_index(net),
+				hb->pub_ttl, 0, 0, hb->pub_dst, msg, n);
+}
+
+void mesh_net_heartbeat_init(struct mesh_net *net)
+{
+	struct mesh_net_heartbeat *hb = &net->heartbeat;
+
+	memset(hb, 0, sizeof(struct mesh_net_heartbeat));
+	hb->sub_min_hops = 0xff;
+	hb->features = mesh_net_get_features(net);
+}
+
+uint32_t mesh_net_friend_timeout(struct mesh_net *net, uint16_t addr)
+{
+	struct mesh_friend *frnd = l_queue_find(net->friends, match_by_friend,
+						L_UINT_TO_PTR(addr));
+
+	if (!frnd)
+		return 0;
+	else
+		return frnd->poll_timeout;
+}
+
+bool mesh_net_local_node_set(struct mesh_net *net, struct mesh_node *node,
+							bool provisioner)
+{
+	if (net->local_node) {
+		l_info("Local node already registered");
+		return false;
+	}
+
+	net->local_node = node;
+	net->provisioner = provisioner;
+
+	return true;
+}
+
+bool mesh_net_provisioned_new(struct mesh_net *net, uint8_t device_key[16],
+				uint16_t net_idx,  uint8_t net_key[16],
+				uint16_t unicast, uint16_t snb_flags,
+				uint32_t iv_index, mesh_status_func_t cb,
+				void *user_data)
+{
+	if (net->provisioned || !net->local_node)
+		return false;
+
+	if (!node_set_primary(net->local_node, unicast) ||
+				!(mesh_net_register_unicast(net, unicast,
+						mesh_net_get_num_ele(net))))
+		return false;
+
+	if (!node_set_device_key(net->local_node, device_key))
+		return false;
+
+	net->iv_index = iv_index;
+	net->iv_update = ((snb_flags & 0x02) != 0);
+	if (!storage_local_set_iv_index(net, iv_index, net->iv_update))
+		return false;
+
+	if (mesh_net_add_key(net, false, net_idx, net_key) !=
+							MESH_STATUS_SUCCESS)
+		return false;
+
+	if ((snb_flags & 0x01) &&
+			(mesh_net_add_key(net, true, net_idx, net_key) !=
+							MESH_STATUS_SUCCESS)) {
+		l_queue_clear(net->subnets, l_free);
+		return false;
+	}
+
+	return storage_save_new_config(net, net->cfg_file, cb, user_data);
+
+}
+
+void mesh_net_provisioned_set(struct mesh_net *net, bool provisioned)
+{
+	struct mesh_io *io;
+
+	if (!net)
+		return;
+
+	net->provisioned = provisioned;
+	io = net->io;
+
+	if (provisioned) {
+		mesh_io_register_recv_cb(io, MESH_IO_FILTER_BEACON,
+							beacon_recv, net);
+		mesh_io_register_recv_cb(io, MESH_IO_FILTER_NET,
+							net_msg_recv, net);
+	} else {
+		uint8_t *uuid = node_uuid_get(net->local_node);
+
+		if (!uuid)
+			return;
+
+		mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_BEACON);
+		mesh_io_deregister_recv_cb(io, MESH_IO_FILTER_NET);
+
+		mesh_prov_listen(net, uuid, (uint8_t *) &net->prov_caps,
+					acceptor_prov_open,
+					acceptor_prov_close,
+					acceptor_prov_receive, net);
+	}
+}
-- 
2.14.3

--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux