Blob Blame History Raw
/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2017  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.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <inttypes.h>
#include <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>

#include "src/shared/util.h"
#include "src/shared/shell.h"

#include "tools/mesh/crypto.h"
#include "tools/mesh/gatt.h"
#include "tools/mesh/mesh-net.h"
#include "tools/mesh/util.h"
#include "tools/mesh/keys.h"
#include "tools/mesh/node.h"
#include "tools/mesh/prov-db.h"
#include "tools/mesh/net.h"

struct address_range
{
	uint16_t min;
	uint16_t max;
};

struct mesh_net {
	uint32_t iv_index;
	uint32_t seq_num;
	uint32_t seq_num_reserved;
	uint16_t primary_addr;
	uint8_t iv_upd_state;
	uint8_t num_elements;
	uint8_t default_ttl;
	bool iv_update;
	bool provisioner;
	bool blacklist;
	guint iv_update_timeout;
	GDBusProxy *proxy_in;
	GList *address_pool;
	GList *dest;	/* List of valid local destinations for Whitelist */
	GList *sar_in;	/* Incoming segmented messages in progress */
	GList *msg_out;	/* Pre-Network encoded, might be multi-segment */
	GList *pkt_out; /* Fully encoded packets awaiting Tx in order */
	net_mesh_session_open_callback open_cb;
};

struct generic_key {
	uint16_t	idx;
};

struct net_key_parts {
	uint8_t nid;
	uint8_t enc_key[16];
	uint8_t privacy_key[16];
	uint8_t	net_key[16];
	uint8_t	beacon_key[16];
	uint8_t	net_id[8];
};

struct mesh_net_key {
	struct generic_key	generic;
	uint8_t			phase;
	struct net_key_parts	current;
	struct net_key_parts	new;
};

struct app_key_parts {
	uint8_t key[16];
	uint8_t akf_aid;
};

struct mesh_app_key {
	struct generic_key	generic;
	uint16_t		net_idx;
	struct app_key_parts	current;
	struct app_key_parts	new;
};

struct mesh_virt_addr {
	uint16_t	va16;
	uint32_t	va32;
	uint8_t		va128[16];
};

struct mesh_pkt {
	uint8_t		data[30];
	uint8_t		len;
};

struct mesh_sar_msg {
	guint		ack_to;
	guint		msg_to;
	uint32_t	iv_index;
	uint32_t	seqAuth;
	uint32_t	ack;
	uint32_t	dst;
	uint16_t	src;
	uint16_t	net_idx;
	uint16_t	len;
	uint8_t		akf_aid;
	uint8_t		ttl;
	uint8_t		segN;
	uint8_t		activity_cnt;
	bool		ctl;
	bool		segmented;
	bool		szmic;
	bool		proxy;
	uint8_t		data[20]; /* Open ended, min 20 */
};

struct mesh_destination {
	uint16_t	cnt;
	uint16_t	dst;
};

/* Network Packet Layer based Offsets */
#define AKF_BIT			0x40

#define PKT_IVI(p)		!!((p)[0] & 0x80)
#define SET_PKT_IVI(p,v)	do {(p)[0] &= 0x7f; \
					(p)[0] |= ((v) ? 0x80 : 0);} while(0)
#define PKT_NID(p)		((p)[0] & 0x7f)
#define SET_PKT_NID(p,v)	do {(p)[0] &= 0x80; (p)[0] |= (v);} while(0)
#define PKT_CTL(p)		(!!((p)[1] & 0x80))
#define SET_PKT_CTL(p,v)	do {(p)[1] &= 0x7f; \
					(p)[1] |= ((v) ? 0x80 : 0);} while(0)
#define PKT_TTL(p)		((p)[1] & 0x7f)
#define SET_PKT_TTL(p,v)	do {(p)[1] &= 0x80; (p)[1] |= (v);} while(0)
#define PKT_SEQ(p)		(get_be32((p) + 1) & 0xffffff)
#define SET_PKT_SEQ(p,v)	put_be32(((p)[1] << 24) + ((v) & 0xffffff), \
									(p) + 1)
#define PKT_SRC(p)		get_be16((p) + 5)
#define SET_PKT_SRC(p,v)	put_be16(v, (p) + 5)
#define PKT_DST(p)		get_be16((p) + 7)
#define SET_PKT_DST(p,v)	put_be16(v, (p) + 7)
#define PKT_TRANS(p)		((p) + 9)
#define PKT_TRANS_LEN(l)	((l) - 9)

#define PKT_SEGMENTED(p)	(!!((p)[9] & 0x80))
#define SET_PKT_SEGMENTED(p,v)	do {(p)[9] &= 0x7f; \
					(p)[9] |= ((v) ? 0x80 : 0);} while(0)
#define PKT_AKF_AID(p)		((p)[9] & 0x7f)
#define SET_PKT_AKF_AID(p,v)	do {(p)[9] &= 0x80; (p)[9] |= (v);} while(0)
#define PKT_OPCODE(p)		((p)[9] & 0x7f)
#define SET_PKT_OPCODE(p,v)	do {(p)[9] &= 0x80; (p)[9] |= (v);} while(0)
#define PKT_OBO(p)		(!!((p)[10] & 0x80))
#define PKT_SZMIC(p)		(!!(PKT_SEGMENTED(p) ? ((p)[10] & 0x40) : 0))
#define SET_PKT_SZMIC(p,v)	do {(p)[10] &= 0x7f; \
					(p)[10] |= ((v) ? 0x80 : 0);} while(0)
#define PKT_SEQ0(p)		((get_be16((p) + 10) >> 2) & 0x1fff)
#define SET_PKT_SEQ0(p,v)	do {put_be16((get_be16((p) + 10) & 0x8003) \
					| (((v) & 0x1fff) << 2), \
					(p) + 10);} while(0)
#define SET_PKT_SEGO(p,v)	do {put_be16((get_be16( \
					(p) + 11) & 0xfc1f) | ((v) << 5), \
					(p) + 11);} while(0)
#define SET_PKT_SEGN(p,v)	do {(p)[12] = ((p)[12] & 0xe0) | (v);} while(0)
#define PKT_ACK(p)		(get_be32((p) + 12))
#define SET_PKT_ACK(p,v)	(put_be32((v)(p) + 12))

/* Transport Layer based offsets */
#define TRANS_SEGMENTED(t)	(!!((t)[0] & 0x80))
#define SET_TRANS_SEGMENTD(t,v)	do {(t)[0] &= 0x7f; \
					(t)[0] |= ((v) ? 0x80 : 0);} while(0)
#define TRANS_OPCODE(t)		((t)[0] & 0x7f)
#define SET_TRANS_OPCODE(t,v)	do {(t)[0] &= 0x80; (t)[0] |= (v);} while(0)
#define TRANS_AKF_AID(t)		((t)[0] & 0x7f)
#define SET_TRANS_AKF_AID(t,v)	do {(t)[0] &= 0xc0; (t)[0] |= (v);} while(0)
#define TRANS_AKF(t)		(!!((t)[0] & AKF_BIT))
#define TRANS_SZMIC(t)		(!!(TRANS_SEGMENTED(t) ? ((t)[1] & 0x80) : 0))
#define TRANS_SEQ0(t)		((get_be16((t) + 1) >> 2) & 0x1fff)
#define SET_TRANS_SEQ0(t,v)	do {put_be16((get_be16((t) + 1) & 0x8003) \
					| (((v) & 0x1fff) << 2), \
					(t) + 1);} while(0)
#define SET_TRANS_ACK(t,v)	put_be32((v), (t) + 3)
#define TRANS_SEGO(t)		((get_be16((t) + 2) >> 5) & 0x1f)
#define TRANS_SEGN(t)		((t)[3] & 0x1f)

#define TRANS_PAYLOAD(t)	((t) + (TRANS_SEGMENTED(t) ? 4 : 1))
#define TRANS_LEN(t,l)		((l) -(TRANS_SEGMENTED(t) ? 4 : 1))

/* Proxy Config Opcodes */
#define FILTER_SETUP		0x00
#define FILTER_ADD		0x01
#define FILTER_DEL		0x02
#define FILTER_STATUS		0x03

/* Proxy Filter Types */
#define WHITELIST_FILTER	0x00
#define BLACKLIST_FILTER	0x01

/* IV Updating states for timing enforcement */
#define IV_UPD_INIT 		0
#define IV_UPD_NORMAL		1
#define IV_UPD_UPDATING		2
#define IV_UPD_NORMAL_HOLD	3

#define IV_IDX_DIFF_RANGE	42

static struct mesh_net net;
static GList *virt_addrs = NULL;
static GList *net_keys = NULL;
static GList *app_keys = NULL;

/* Forward static declarations */
static void resend_segs(struct mesh_sar_msg *sar);

static int match_net_id(const void *a, const void *net_id)
{
	const struct mesh_net_key *net_key = a;

	if (net_key->current.nid != 0xff &&
			!memcmp(net_key->current.net_id, net_id, 8))
		return 0;

	if (net_key->new.nid != 0xff &&
			!memcmp(net_key->new.net_id, net_id, 8))
		return 0;

	return -1;
}

static struct mesh_net_key *find_net_key_by_id(const uint8_t *net_id)
{
	GList *l;

	l = g_list_find_custom(net_keys, net_id, match_net_id);

	if (!l)
		return NULL;

	return l->data;
}

uint16_t net_validate_proxy_beacon(const uint8_t *proxy_beacon)
{
	struct mesh_net_key *net_key = find_net_key_by_id(proxy_beacon);

	if (net_key == NULL)
		return NET_IDX_INVALID;

	return net_key->generic.idx;
}

static int match_sar_dst(const void *a, const void *b)
{
	const struct mesh_sar_msg *sar = a;
	uint16_t dst = GPOINTER_TO_UINT(b);

	return (sar->dst == dst) ? 0 : -1;
}

static struct mesh_sar_msg *find_sar_out_by_dst(uint16_t dst)
{
	GList *l;

	l = g_list_find_custom(net.msg_out, GUINT_TO_POINTER(dst),
			match_sar_dst);

	if (!l)
		return NULL;

	return l->data;
}

static int match_sar_src(const void *a, const void *b)
{
	const struct mesh_sar_msg *sar = a;
	uint16_t src = GPOINTER_TO_UINT(b);

	return (sar->src == src) ? 0 : -1;
}

static struct mesh_sar_msg *find_sar_in_by_src(uint16_t src)
{
	GList *l;

	l = g_list_find_custom(net.sar_in, GUINT_TO_POINTER(src),
			match_sar_src);

	if (!l)
		return NULL;

	return l->data;
}

static int match_key_index(const void *a, const void *b)
{
	const struct generic_key *generic = a;
	uint16_t index = GPOINTER_TO_UINT(b);

	return (generic->idx == index) ? 0 : -1;
}

static bool delete_key(GList **list, uint16_t index)
{
	GList *l;

	l = g_list_find_custom(*list, GUINT_TO_POINTER(index),
				match_key_index);

	if (!l)
		return false;

	*list = g_list_delete_link(*list, l);

	return true;

}

static uint8_t *get_key(GList *list, uint16_t index)
{
	GList *l;
	struct mesh_app_key *app_key;
	struct mesh_net_key *net_key;

	l = g_list_find_custom(list, GUINT_TO_POINTER(index),
				match_key_index);

	if (!l) return NULL;

	if (list == app_keys) {
		app_key = l->data;

		/* All App Keys must belong to a valid Net Key */
		l = g_list_find_custom(net_keys,
				GUINT_TO_POINTER(app_key->net_idx),
				match_key_index);

		if (!l) return NULL;

		net_key = l->data;

		if (net_key->phase == 2 && app_key->new.akf_aid != 0xff)
			return app_key->new.key;

		if (app_key->current.akf_aid != 0xff)
			return app_key->current.key;

		return NULL;
	}

	net_key = l->data;

	if (net_key->phase == 2 && net_key->new.nid != 0xff)
		return net_key->new.net_key;

	if (net_key->current.nid != 0xff)
		return net_key->current.net_key;

	return NULL;
}

bool keys_app_key_add(uint16_t net_idx, uint16_t app_idx, uint8_t *key,
			bool update)
{
	struct mesh_app_key *app_key = NULL;
	uint8_t akf_aid;
	GList *l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
				match_key_index);

	if (!mesh_crypto_k4(key, &akf_aid))
		return false;

	akf_aid |= AKF_BIT;

	if (l && update) {

		app_key = l->data;

		if (app_key->net_idx != net_idx)
			return false;

		memcpy(app_key->new.key, key, 16);
		app_key->new.akf_aid = akf_aid;

	} else if (l) {

		app_key = l->data;

		if (memcmp(app_key->current.key, key, 16) ||
				app_key->net_idx != net_idx)
			return false;

	} else {

		app_key = g_new(struct mesh_app_key, 1);
		memcpy(app_key->current.key, key, 16);
		app_key->net_idx = net_idx;
		app_key->generic.idx = app_idx;
		app_key->current.akf_aid = akf_aid;

		/* Invalidate "New" version */
		app_key->new.akf_aid = 0xff;

		app_keys = g_list_append(app_keys, app_key);

	}

	return true;
}

bool keys_net_key_add(uint16_t net_idx, uint8_t *key, bool update)
{
	struct mesh_net_key *net_key = NULL;
	uint8_t p = 0;
	GList *l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
				match_key_index);

	if (l && update) {
		bool result;

		net_key = l->data;

		memcpy(net_key->new.net_key, key, 16);

		/* Calculate the many component parts */
		result = mesh_crypto_nkbk(key, net_key->new.beacon_key);
		if (!result)
			return false;

		result = mesh_crypto_k3(key, net_key->new.net_id);
		if (!result)
			return false;

		result = mesh_crypto_k2(key, &p, 1,
				&net_key->new.nid,
				net_key->new.enc_key,
				net_key->new.privacy_key);
		if (!result)
			net_key->new.nid = 0xff;

		return result;

	} else if (l) {
		net_key = l->data;

		if (memcmp(net_key->current.net_key, key, 16))
			return false;
	} else {
		bool result;

		net_key = g_new(struct mesh_net_key, 1);
		memcpy(net_key->current.net_key, key, 16);
		net_key->generic.idx = net_idx;

		/* Invalidate "New" version */
		net_key->new.nid = 0xff;

		/* Calculate the many component parts */
		result = mesh_crypto_nkbk(key, net_key->current.beacon_key);
		if (!result) {
			g_free(net_key);
			return false;
		}

		result = mesh_crypto_k3(key, net_key->current.net_id);
		if (!result) {
			g_free(net_key);
			return false;
		}

		result = mesh_crypto_k2(key, &p, 1,
				&net_key->current.nid,
				net_key->current.enc_key,
				net_key->current.privacy_key);

		if (!result) {
			g_free(net_key);
			return false;
		}

		net_keys = g_list_append(net_keys, net_key);
	}

	return true;
}

static struct mesh_app_key *find_app_key_by_idx(uint16_t app_idx)
{
	GList *l;

	l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
				match_key_index);

	if (!l) return NULL;

	return l->data;
}

static struct mesh_net_key *find_net_key_by_idx(uint16_t net_idx)
{
	GList *l;

	l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
				match_key_index);

	if (!l) return NULL;

	return l->data;
}

static int match_virt_dst(const void *a, const void *b)
{
	const struct mesh_virt_addr *virt = a;
	uint32_t dst = GPOINTER_TO_UINT(b);

	if (dst < 0x10000 && dst == virt->va16)
		return 0;

	if (dst == virt->va32)
		return 0;

	return -1;
}

static struct mesh_virt_addr *find_virt_by_dst(uint32_t dst)
{
	GList *l;

	l = g_list_find_custom(virt_addrs, GUINT_TO_POINTER(dst),
				match_virt_dst);

	if (!l) return NULL;

	return l->data;
}

uint8_t *keys_net_key_get(uint16_t net_idx, bool current)
{
	GList *l;


	l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
				match_key_index);
	if (!l) {
		return NULL;
	} else {
		struct mesh_net_key *key = l->data;
		if (current)
			return key->current.net_key;
		else
			return key->new.net_key;
	}
}

bool keys_app_key_delete(uint16_t app_idx)
{
	/* TODO: remove all associated bindings */
	return delete_key(&app_keys, app_idx);
}

bool keys_net_key_delete(uint16_t net_idx)
{
	/* TODO: remove all associated app keys and bindings */
	return delete_key(&net_keys, net_idx);
}

uint8_t keys_get_kr_phase(uint16_t net_idx)
{
	GList *l;
	struct mesh_net_key *key;

	l = g_list_find_custom(net_keys, GUINT_TO_POINTER(net_idx),
				match_key_index);

	if (!l)
		return KR_PHASE_INVALID;

	key = l->data;

	return key->phase;
}

bool keys_set_kr_phase(uint16_t index, uint8_t phase)
{
	GList *l;
	struct mesh_net_key *net_key;

	l = g_list_find_custom(net_keys, GUINT_TO_POINTER(index),
				match_key_index);

	if (!l)
		return false;

	net_key = l->data;
	net_key->phase = phase;

	return true;
}

uint16_t keys_app_key_get_bound(uint16_t app_idx)
{
	GList *l;

	l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
				match_key_index);
	if (!l)
		return NET_IDX_INVALID;
	else {
		struct mesh_app_key *key = l->data;
		return key->net_idx;
	}
}

uint8_t *keys_app_key_get(uint16_t app_idx, bool current)
{
	GList *l;


	l = g_list_find_custom(app_keys, GUINT_TO_POINTER(app_idx),
				match_key_index);
	if (!l) {
		return NULL;
	} else {
		struct mesh_app_key *key = l->data;
		if (current)
			return key->current.key;
		else
			return key->new.key;
	}
}

void keys_cleanup_all(void)
{
	g_list_free_full(app_keys, g_free);
	g_list_free_full(net_keys, g_free);
	app_keys = net_keys = NULL;
}

bool net_get_key(uint16_t net_idx, uint8_t *key)
{
	uint8_t *buf;

	buf = get_key(net_keys, net_idx);

	if (!buf)
		return false;

	memcpy(key, buf, 16);
	return true;
}

bool net_get_flags(uint16_t net_idx, uint8_t *out_flags)
{
	uint8_t phase;

	phase = keys_get_kr_phase(net_idx);

	if (phase == KR_PHASE_INVALID || !out_flags)
		return false;

	if (phase != KR_PHASE_NONE)
		*out_flags = 0x01;
	else
		*out_flags = 0x00;

	if (net.iv_update)
		*out_flags |= 0x02;

	return true;
}

uint32_t net_get_iv_index(bool *update)
{
	if (update)
		*update = net.iv_update;

	return net.iv_index;
}

void net_set_iv_index(uint32_t iv_index, bool update)
{
	net.iv_index = iv_index;
	net.iv_update = update;
}

void set_sequence_number(uint32_t seq_num)
{
	net.seq_num = seq_num;
}

uint32_t get_sequence_number(void)
{
	return net.seq_num;
}

bool net_add_address_pool(uint16_t min, uint16_t max)
{
	uint32_t range;
	if (max < min)
		return false;
	range = min + (max << 16);
	net.address_pool = g_list_append(net.address_pool,
						GUINT_TO_POINTER(range));
	return true;
}

static int match_address_range(const void *a, const void *b)
{
	uint32_t range = GPOINTER_TO_UINT(a);
	uint8_t num_elements = (uint8_t) (GPOINTER_TO_UINT(b));
	uint16_t max = range >> 16;
	uint16_t min = range & 0xffff;

	return ((max - min + 1) >= num_elements) ? 0 : -1;

}

uint16_t net_obtain_address(uint8_t num_eles)
{
	uint16_t addr;
	GList *l;

	l = g_list_find_custom(net.address_pool, GUINT_TO_POINTER(num_eles),
				match_address_range);
	if (l) {
		uint32_t range = GPOINTER_TO_UINT(l->data);
		uint16_t max = range >> 16;
		uint16_t min = range & 0xffff;

		addr = min;
		min += num_eles;

		if (min > max) {
			net.address_pool = g_list_delete_link(net.address_pool,
								l);
		} else {
			range = min + (max << 16);
			l->data = GUINT_TO_POINTER(range);
		}

		return addr;
	}

	return UNASSIGNED_ADDRESS;
}

static int range_cmp(const void *a, const void *b)
{
	uint32_t range1 = GPOINTER_TO_UINT(a);
	uint32_t range2 = GPOINTER_TO_UINT(b);

	return range2 - range1;
}

void net_release_address(uint16_t addr, uint8_t num_elements)
{
	GList *l;
	uint32_t range;

	if (addr == UNASSIGNED_ADDRESS)
		return;

	for (l = net.address_pool; l != NULL; l = l->next)
	{
		uint16_t max;
		uint16_t min;
		GList *l1 = l->next;

		range = GPOINTER_TO_UINT(l->data);

		max = range >> 16;
		min = range & 0xffff;

		if (min == (addr + num_elements))
			min  = addr;
		else if (max == (addr - 1))
			max = addr + num_elements;
		else
			continue;

		/* Check if range pools need to be merged */
		if (l1) {
			uint16_t min1;

			range = GPOINTER_TO_UINT(l1->data);
			min1 = range & 0xffff;

			if (min1 == (max + 1)) {
				max = range >> 16;
				l->next = l1->next;
				net.address_pool = g_list_delete_link(
							net.address_pool, l1);
			}
		}

		range = min + (max << 16);
		l->data = GUINT_TO_POINTER(range);
		return;
	}

	range = addr + ((addr + num_elements - 1) << 16);
	net.address_pool = g_list_insert_sorted(net.address_pool,
						GUINT_TO_POINTER(range),
						range_cmp);
}

bool net_reserve_address_range(uint16_t base, uint8_t num_elements)
{
	GList *l;
	uint32_t range;
	uint16_t max;
	uint16_t min;
	bool shrink;

	for (l = net.address_pool; l != NULL; l = l->next) {

		range = GPOINTER_TO_UINT(l->data);

		max = range >> 16;
		min = range & 0xffff;

		if (base >= min && (base + num_elements - 1) <= max)
			break;
	}

	if (!l)
		return false;

	net.address_pool = g_list_delete_link(net.address_pool, l);

	shrink = false;

	if (base == min) {
		shrink = true;
		min = base + num_elements;
	}

	if (max == base + num_elements - 1) {
		shrink = true;
		max -= num_elements;
	}

	if (min > max)
		return true;

	if (shrink)
		range = min + (max << 16);
	else
		range = min + ((base - 1) << 16);

	net.address_pool = g_list_insert_sorted(net.address_pool,
						GUINT_TO_POINTER(range),
						range_cmp);

	if (shrink)
		return true;

	range = (base + num_elements) + (max << 16);
	net.address_pool = g_list_insert_sorted(net.address_pool,
						GUINT_TO_POINTER(range),
						range_cmp);

	return true;
}

static int match_destination(const void *a, const void *b)
{
	const struct mesh_destination *dest = a;
	uint16_t dst = GPOINTER_TO_UINT(b);

	return (dest->dst == dst) ? 0 : -1;
}

void net_dest_ref(uint16_t dst)
{
	struct mesh_destination *dest;
	GList *l;

	if (!dst) return;

	l = g_list_find_custom(net.dest, GUINT_TO_POINTER(dst),
			match_destination);

	if (l) {
		dest = l->data;
		dest->cnt++;
		return;
	}

	dest = g_new0(struct mesh_destination, 1);
	dest->dst = dst;
	dest->cnt++;
	net.dest = g_list_append(net.dest, dest);
}

void net_dest_unref(uint16_t dst)
{
	struct mesh_destination *dest;
	GList *l;

	l = g_list_find_custom(net.dest, GUINT_TO_POINTER(dst),
			match_destination);

	if (!l)
		return;

	dest = l->data;
	dest->cnt--;

	if (dest->cnt == 0) {
		net.dest = g_list_remove(net.dest, dest);
		g_free(dest);
	}
}

struct build_whitelist {
	uint8_t len;
	uint8_t data[12];
};

static void whitefilter_add(gpointer data, gpointer user_data)
{
	struct mesh_destination	*dest = data;
	struct build_whitelist *white = user_data;

	if (white->len == 0)
		white->data[white->len++] = FILTER_ADD;

	put_be16(dest->dst, white->data + white->len);
	white->len += 2;

	if (white->len > (sizeof(white->data) - sizeof(uint16_t))) {
		net_ctl_msg_send(0, 0, 0, white->data, white->len);
		white->len = 0;
	}
}

static void setup_whitelist()
{
	struct build_whitelist white;

	white.len = 0;

	/* Enable (and Clear) Proxy Whitelist */
	white.data[white.len++] = FILTER_SETUP;
	white.data[white.len++] = WHITELIST_FILTER;

	net_ctl_msg_send(0, 0, 0, white.data, white.len);

	white.len = 0;
	g_list_foreach(net.dest, whitefilter_add, &white);

	if (white.len)
		net_ctl_msg_send(0, 0, 0, white.data, white.len);
}

static void beacon_update(bool first, bool iv_update, uint32_t iv_index)
{

	/* Enforcement of 96 hour and 192 hour IVU time windows */
	if (iv_update && !net.iv_update) {
		bt_shell_printf("iv_upd_state = IV_UPD_UPDATING\n");
		net.iv_upd_state = IV_UPD_UPDATING;
		/* TODO: Start timer to enforce IV Update parameters */
	} else if (first) {
		if (iv_update)
			net.iv_upd_state = IV_UPD_UPDATING;
		else
			net.iv_upd_state = IV_UPD_NORMAL;

		bt_shell_printf("iv_upd_state = IV_UPD_%s\n",
				iv_update ? "UPDATING" : "NORMAL");

	} else if (iv_update && iv_index != net.iv_index) {
		bt_shell_printf("IV Update too soon -- Rejecting\n");
		return;
	}

	if (iv_index > net.iv_index ||
			iv_update != net.iv_update) {

		/* Don't reset our seq_num unless
		 * we start using new iv_index */
		if (!(iv_update && (net.iv_index + 1 == iv_index))) {
			net.seq_num = 0;
			net.seq_num_reserved = 100;
		}
	}

	if (!net.seq_num || net.iv_index != iv_index ||
			net.iv_update != iv_update) {

		if (net.seq_num_reserved <= net.seq_num)
			net.seq_num_reserved = net.seq_num + 100;

		prov_db_local_set_iv_index(iv_index, iv_update,
				net.provisioner);
		prov_db_local_set_seq_num(net.seq_num_reserved);
	}

	net.iv_index = iv_index;
	net.iv_update = iv_update;

	if (first) {
		/* Must be done once per Proxy Connection after Beacon RXed */
		setup_whitelist();
		if (net.open_cb)
			net.open_cb(0);
	}
}

static bool process_beacon(uint8_t *data, uint8_t size)
{
	struct mesh_net_key *net_key;
	struct net_key_parts *key_part;
	bool rxed_iv_update, rxed_key_refresh, iv_update;
	bool  my_krf;
	uint32_t rxed_iv_index, iv_index;
	uint64_t cmac;

	if (size != 22)
		return false;

	rxed_key_refresh = (data[1] & 0x01) == 0x01;
	iv_update = rxed_iv_update = (data[1] & 0x02) == 0x02;
	iv_index = rxed_iv_index = get_be32(data + 10);

	/* Inhibit recognizing iv_update true-->false
	 * if we have outbound SAR messages in flight */
	if (net.msg_out != NULL) {
		if (net.iv_update && !rxed_iv_update)
			iv_update = true;
	}

	/* Don't bother going further if nothing has changed */
	if (iv_index == net.iv_index && iv_update == net.iv_update &&
			net.iv_upd_state != IV_UPD_INIT)
		return true;

	/* Find key we are using for SNBs */
	net_key = find_net_key_by_id(data + 2);

	if (net_key == NULL)
		return false;

	/* We are Provisioner, and control the key_refresh flag */
	if (rxed_key_refresh != !!(net_key->phase == 2))
		return false;

	if (net_key->phase != 2) {
		my_krf = false;
		key_part = &net_key->current;
	} else {
		my_krf = true;
		key_part = &net_key->new;
	}

	/* Ignore for incorrect KR state */
	if (memcmp(key_part->net_id, data + 2, 8))
		return false;

	if ((net.iv_index + IV_IDX_DIFF_RANGE < iv_index) ||
			(iv_index < net.iv_index)) {
		bt_shell_printf("iv index outside range\n");
		return false;
	}

	/* Any behavioral changes must pass CMAC test */
	if (!mesh_crypto_beacon_cmac(key_part->beacon_key, key_part->net_id,
				rxed_iv_index, my_krf,
				rxed_iv_update, &cmac)) {
		return false;
	}

	if (cmac != get_be64(data + 14))
		return false;

	if (iv_update && (net.iv_upd_state > IV_UPD_UPDATING)) {
		if (iv_index != net.iv_index) {
			bt_shell_printf("Update too soon -- Rejecting\n");
		}
		/* Silently ignore old beacons */
		return true;
	}

	beacon_update(net.iv_upd_state == IV_UPD_INIT, iv_update, iv_index);

	return true;
}

struct decode_params {
	struct mesh_net_key	*net_key;
	uint8_t			*packet;
	uint32_t		iv_index;
	uint8_t			size;
	bool			proxy;
};

static void try_decode(gpointer data, gpointer user_data)
{
	struct mesh_net_key *net_key = data;
	struct decode_params *decode = user_data;
	uint8_t nid = decode->packet[0] & 0x7f;
	uint8_t tmp[29];
	bool status = false;

	if (decode->net_key)
		return;

	if (net_key->current.nid == nid)
		status = mesh_crypto_packet_decode(decode->packet,
				decode->size, decode->proxy, tmp,
				decode->iv_index,
				net_key->current.enc_key,
				net_key->current.privacy_key);

	if (!status && net_key->new.nid == nid)
		status = mesh_crypto_packet_decode(decode->packet,
				decode->size, decode->proxy, tmp,
				decode->iv_index,
				net_key->new.enc_key,
				net_key->new.privacy_key);

	if (status) {
		decode->net_key = net_key;
		memcpy(decode->packet, tmp, decode->size);
		return;
	}
}

static struct mesh_net_key *net_packet_decode(bool proxy, uint32_t iv_index,
				uint8_t *packet, uint8_t size)
{
	struct decode_params decode = {
		.proxy = proxy,
		.iv_index = iv_index,
		.packet = packet,
		.size = size,
		.net_key = NULL,
	};

	g_list_foreach(net_keys, try_decode, &decode);

	return decode.net_key;
}

static void flush_sar(GList **list, struct mesh_sar_msg *sar)
{
	*list = g_list_remove(*list, sar);

	if (sar->msg_to)
		g_source_remove(sar->msg_to);

	if (sar->ack_to)
		g_source_remove(sar->ack_to);

	g_free(sar);
}

static void flush_sar_list(GList **list)
{
	struct mesh_sar_msg *sar;
	GList *l = g_list_first(*list);

	while (l) {
		sar = l->data;
		flush_sar(list, sar);
		l = g_list_first(*list);
	}
}

static void flush_pkt_list(GList **list)
{
	struct mesh_pkt *pkt;
	GList *l = g_list_first(*list);

	while (l) {
		pkt = l->data;
		*list = g_list_remove(*list, pkt);
		g_free(pkt);
	}
}

static void resend_unacked_segs(gpointer data, gpointer user_data)
{
	struct mesh_sar_msg *sar = data;

	if (sar->activity_cnt)
		resend_segs(sar);
}

static void send_pkt_cmplt(DBusMessage *message, void *user_data)
{
	struct mesh_pkt *pkt = user_data;
	GList *l = g_list_first(net.pkt_out);

	if (l && user_data == l->data) {
		net.pkt_out = g_list_delete_link(net.pkt_out, l);
		g_free(pkt);
	} else {
		/* This is a serious error, and probable memory leak */
		bt_shell_printf("ERR: send_pkt_cmplt %p not head of queue\n", pkt);
	}

	l = g_list_first(net.pkt_out);

	if (l == NULL) {
		/* If queue is newly empty, resend all SAR outbound packets */
		g_list_foreach(net.msg_out, resend_unacked_segs, NULL);
		return;
	}

	pkt = l->data;

	mesh_gatt_write(net.proxy_in, pkt->data, pkt->len,
			send_pkt_cmplt, pkt);
}

static void send_mesh_pkt(struct mesh_pkt *pkt)
{
	bool queued = !!(net.pkt_out);

	net.pkt_out = g_list_append(net.pkt_out, pkt);

	if (queued)
		return;

	mesh_gatt_write(net.proxy_in, pkt->data, pkt->len,
			send_pkt_cmplt, pkt);
}

static uint32_t get_next_seq()
{
	uint32_t this_seq = net.seq_num++;

	if (net.seq_num + 32 >= net.seq_num_reserved) {
		net.seq_num_reserved = net.seq_num + 100;
		prov_db_local_set_seq_num(net.seq_num_reserved);
	}

	return this_seq;
}

static void send_seg(struct mesh_sar_msg *sar, uint8_t seg)
{
	struct mesh_net_key *net_key;
	struct net_key_parts *part;
	struct mesh_pkt *pkt;
	uint8_t *data;

	net_key = find_net_key_by_idx(sar->net_idx);

	if (net_key == NULL)
		return;

	/* Choose which components to use to secure pkt */
	if (net_key->phase == 2 && net_key->new.nid != 0xff)
		part = &net_key->new;
	else
		part = &net_key->current;

	pkt = g_new0(struct mesh_pkt, 1);

	if (pkt == NULL)
		return;

	/* leave extra byte at start for GATT Proxy type */
	data = pkt->data + 1;

	SET_PKT_NID(data, part->nid);
	SET_PKT_IVI(data, sar->iv_index & 1);
	SET_PKT_CTL(data, sar->ctl);
	SET_PKT_TTL(data, sar->ttl);
	SET_PKT_SEQ(data, get_next_seq());
	SET_PKT_SRC(data, sar->src);
	SET_PKT_DST(data, sar->dst);
	SET_PKT_SEGMENTED(data, sar->segmented);

	if (sar->ctl)
		SET_PKT_OPCODE(data, sar->data[0]);
	else
		SET_PKT_AKF_AID(data, sar->akf_aid);

	if (sar->segmented) {

		if (!sar->ctl)
			SET_PKT_SZMIC(data, sar->szmic);

		SET_PKT_SEQ0(data, sar->seqAuth);
		SET_PKT_SEGO(data, seg);
		SET_PKT_SEGN(data, sar->segN);

		memcpy(PKT_TRANS(data) + 4,
				sar->data + sar->ctl + (seg * 12), 12);

		pkt->len = 9 + 4;

		if (sar->segN == seg)
			pkt->len += (sar->len - sar->ctl) % 12;

		if (pkt->len == (9 + 4))
			pkt->len += 12;

	} else {
		memcpy(PKT_TRANS(data) + 1,
				sar->data + sar->ctl, 15);

		pkt->len = 9 + 1 + sar->len - sar->ctl;
	}

	pkt->len += (sar->ctl ? 8 : 4);
	mesh_crypto_packet_encode(data, pkt->len,
			part->enc_key,
			sar->iv_index,
			part->privacy_key);


	/* Prepend GATT_Proxy packet type */
	if (sar->proxy)
		pkt->data[0] = PROXY_CONFIG_PDU;
	else
		pkt->data[0] = PROXY_NETWORK_PDU;

	pkt->len++;

	send_mesh_pkt(pkt);
}

static void resend_segs(struct mesh_sar_msg *sar)
{
	uint32_t ack = 1;
	uint8_t i;

	sar->activity_cnt = 0;

	for (i = 0; i <= sar->segN; i++, ack <<= 1) {
		if (!(ack & sar->ack))
			send_seg(sar, i);
	}
}

static bool ack_rxed(bool to, uint16_t src, uint16_t dst, bool obo,
				uint16_t seq0, uint32_t ack_flags)
{
	struct mesh_sar_msg *sar = find_sar_out_by_dst(src);
	uint32_t full_ack;

	/* Silently ignore unknown (stale?) ACKs */
	if (sar == NULL)
		return true;

	full_ack = 0xffffffff >> (31 - sar->segN);

	sar->ack |= (ack_flags & full_ack);

	if (sar->ack == full_ack) {
		/* Outbound message 100% received by remote node */
		flush_sar(&net.msg_out, sar);
		return true;
	}

	/* Because we are GATT, and slow, only resend PKTs if it is
	 * time *and* our outbound PKT queue is empty.  */
	sar->activity_cnt++;

	if (net.pkt_out == NULL)
		resend_segs(sar);

	return true;
}

static bool proxy_ctl_rxed(uint16_t net_idx, uint32_t iv_index,
		uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
		uint8_t *trans, uint16_t len)
{
	if (len < 1)
		return false;

	switch(trans[0]) {
		case FILTER_STATUS:
			if (len != 4)
				return false;

			net.blacklist = !!(trans[1] == BLACKLIST_FILTER);
			bt_shell_printf("Proxy %slist filter length: %d\n",
					net.blacklist ? "Black" : "White",
					get_be16(trans + 2));

			return true;

		default:
			return false;
	}

	return false;
}

static bool ctl_rxed(uint16_t net_idx, uint32_t iv_index,
		uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
		uint8_t *trans, uint16_t len)
{
	/* TODO: Handle control messages */

	/* Per Mesh Profile 3.6.5.10 */
	if (trans[0] == NET_OP_HEARTBEAT) {
		uint16_t feat = get_be16(trans + 2);

		bt_shell_printf("HEARTBEAT src: %4.4x dst: %4.4x \
				TTL: %2.2x feat: %s%s%s%s\n",
				src, dst, trans[1],
				(feat & MESH_FEATURE_RELAY) ? "relay " : "",
				(feat & MESH_FEATURE_PROXY) ? "proxy " : "",
				(feat & MESH_FEATURE_FRIEND) ? "friend " : "",
				(feat & MESH_FEATURE_LPN) ? "lpn" : "");
		return true;
	}

	bt_shell_printf("unrecognized control message src:%4.4x dst:%4.4x len:%d\n",
			src, dst, len);
	print_byte_array("msg: ", trans, len);
	return false;
}

struct decrypt_params {
	uint8_t		*nonce;
	uint8_t		*aad;
	uint8_t		*out_msg;
	uint8_t		*trans;
	uint32_t	iv_index;
	uint32_t	seq_num;
	uint16_t	src;
	uint16_t	dst;
	uint16_t	len;
	uint16_t	net_idx;
	uint16_t	app_idx;
	uint8_t		akf_aid;
	bool		szmic;
};


static void try_decrypt(gpointer data, gpointer user_data)
{
	struct mesh_app_key *app_key = data;
	struct decrypt_params *decrypt = user_data;
	size_t mic_size = decrypt->szmic ? sizeof(uint64_t) : sizeof(uint32_t);
	bool status = false;

	/* Already done... Nothing to do */
	if (decrypt->app_idx != APP_IDX_INVALID)
		return;

	/* Don't decrypt on Appkeys not owned by this NetKey */
	if (app_key->net_idx != decrypt->net_idx)
		return;

	/* Test and decrypt against current key copy */
	if (app_key->current.akf_aid == decrypt->akf_aid)
		status = mesh_crypto_aes_ccm_decrypt(decrypt->nonce,
				app_key->current.key,
				decrypt->aad, decrypt->aad ? 16 : 0,
				decrypt->trans, decrypt->len,
				decrypt->out_msg, NULL, mic_size);

	/* Test and decrypt against new key copy */
	if (!status && app_key->new.akf_aid == decrypt->akf_aid)
		status = mesh_crypto_aes_ccm_decrypt(decrypt->nonce,
				app_key->new.key,
				decrypt->aad, decrypt->aad ? 16 : 0,
				decrypt->trans, decrypt->len,
				decrypt->out_msg, NULL, mic_size);

	/* If successful, terminate with successful App IDX */
	if (status)
		decrypt->app_idx = app_key->generic.idx;
}

static uint16_t access_pkt_decrypt(uint8_t *nonce, uint8_t *aad,
		uint16_t net_idx, uint8_t akf_aid, bool szmic,
		uint8_t *trans, uint16_t len)
{
	uint8_t *out_msg;
	struct decrypt_params decrypt = {
		.nonce = nonce,
		.aad = aad,
		.net_idx = net_idx,
		.akf_aid = akf_aid,
		.szmic = szmic,
		.trans = trans,
		.len = len,
		.app_idx = APP_IDX_INVALID,
	};

	out_msg = g_malloc(len);

	if (out_msg == NULL)
		return false;

	decrypt.out_msg = out_msg;

	g_list_foreach(app_keys, try_decrypt, &decrypt);

	if (decrypt.app_idx != APP_IDX_INVALID)
		memcpy(trans, out_msg, len);

	g_free(out_msg);

	return decrypt.app_idx;
}

static bool access_rxed(uint8_t *nonce, uint16_t net_idx,
		uint32_t iv_index, uint32_t seq_num,
		uint16_t src, uint16_t dst,
		uint8_t akf_aid, bool szmic, uint8_t *trans, uint16_t len)
{
	uint16_t app_idx = access_pkt_decrypt(nonce, NULL,
			net_idx, akf_aid, szmic, trans, len);

	if (app_idx != APP_IDX_INVALID) {
		len -= szmic ? sizeof(uint64_t) : sizeof(uint32_t);

		node_local_data_handler(src, dst, iv_index, seq_num,
				app_idx, trans, len);
		return true;
	}

	return false;
}

static void try_virt_decrypt(gpointer data, gpointer user_data)
{
	struct mesh_virt_addr *virt = data;
	struct decrypt_params *decrypt = user_data;

	if (decrypt->app_idx != APP_IDX_INVALID || decrypt->dst != virt->va16)
		return;

	decrypt->app_idx = access_pkt_decrypt(decrypt->nonce,
			virt->va128,
			decrypt->net_idx, decrypt->akf_aid,
			decrypt->szmic, decrypt->trans, decrypt->len);

	if (decrypt->app_idx != APP_IDX_INVALID) {
		uint16_t len = decrypt->len;

		len -= decrypt->szmic ? sizeof(uint64_t) : sizeof(uint32_t);

		node_local_data_handler(decrypt->src, virt->va32,
				decrypt->iv_index, decrypt->seq_num,
				decrypt->app_idx, decrypt->trans, len);
	}
}

static bool virtual_rxed(uint8_t *nonce, uint16_t net_idx,
		uint32_t iv_index, uint32_t seq_num,
		uint16_t src, uint16_t dst,
		uint8_t akf_aid, bool szmic, uint8_t *trans, uint16_t len)
{
	struct decrypt_params decrypt = {
		.nonce = nonce,
		.net_idx = net_idx,
		.iv_index = iv_index,
		.seq_num = seq_num,
		.src = dst,
		.dst = dst,
		.akf_aid = akf_aid,
		.szmic = szmic,
		.trans = trans,
		.len = len,
		.app_idx = APP_IDX_INVALID,
	};

	/* Cycle through known virtual addresses */
	g_list_foreach(virt_addrs, try_virt_decrypt, &decrypt);

	if (decrypt.app_idx != APP_IDX_INVALID)
		return true;

	return false;
}

static bool msg_rxed(uint16_t net_idx, uint32_t iv_index, bool szmic,
		uint8_t ttl, uint32_t seq_num, uint32_t seq_auth,
		uint16_t src, uint16_t dst,
		uint8_t *trans, uint16_t len)
{
	uint8_t akf_aid = TRANS_AKF_AID(trans);
	bool result;
	size_t mic_size = szmic ? sizeof(uint64_t) : sizeof(uint32_t);
	uint8_t nonce[13];
	uint8_t *dev_key;
	uint8_t *out = NULL;

	if (!TRANS_AKF(trans)) {
		/* Compose Nonce */
		result = mesh_crypto_device_nonce(seq_auth, src, dst,
				iv_index, szmic, nonce);

		if (!result) return false;

		out = g_malloc0(TRANS_LEN(trans, len));
		if (out == NULL) return false;

		/* If we are provisioner, we probably RXed on remote Dev Key */
		if (net.provisioner) {
			dev_key = node_get_device_key(node_find_by_addr(src));

			if (dev_key == NULL)
				goto local_dev_key;
		} else
			goto local_dev_key;

		result = mesh_crypto_aes_ccm_decrypt(nonce, dev_key,
				NULL, 0,
				TRANS_PAYLOAD(trans), TRANS_LEN(trans, len),
				out, NULL, mic_size);

		if (result) {
			node_local_data_handler(src, dst,
					iv_index, seq_num, APP_IDX_DEV,
					out, TRANS_LEN(trans, len) - mic_size);
			goto done;
		}

local_dev_key:
		/* Always fallback to the local Dev Key */
		dev_key = node_get_device_key(node_get_local_node());

		if (dev_key == NULL)
			goto done;

		result = mesh_crypto_aes_ccm_decrypt(nonce, dev_key,
				NULL, 0,
				TRANS_PAYLOAD(trans), TRANS_LEN(trans, len),
				out, NULL, mic_size);

		if (result) {
			node_local_data_handler(src, dst,
					iv_index, seq_num, APP_IDX_DEV,
					out, TRANS_LEN(trans, len) - mic_size);
			goto done;
		}

		goto done;
	}

	result = mesh_crypto_application_nonce(seq_auth, src, dst,
			iv_index, szmic, nonce);

	if (!result) goto done;

	/* If Virtual destination wrap the Access decoder with Virtual */
	if (IS_VIRTUAL(dst)) {
		result = virtual_rxed(nonce, net_idx, iv_index, seq_num,
				src, dst, akf_aid, szmic,
				TRANS_PAYLOAD(trans), TRANS_LEN(trans, len));
		goto done;
	}

	/* Try all matching App Keys until success or exhaustion */
	result = access_rxed(nonce, net_idx, iv_index, seq_num,
			src, dst, akf_aid, szmic,
			TRANS_PAYLOAD(trans), TRANS_LEN(trans, len));

done:
	if (out != NULL)
		g_free(out);

	return result;
}

static void send_sar_ack(struct mesh_sar_msg *sar)
{
	uint8_t ack[7];

	sar->activity_cnt = 0;

	memset(ack, 0, sizeof(ack));
	SET_TRANS_OPCODE(ack, NET_OP_SEG_ACKNOWLEDGE);
	SET_TRANS_SEQ0(ack, sar->seqAuth);
	SET_TRANS_ACK(ack, sar->ack);

	net_ctl_msg_send(0xff, sar->dst, sar->src, ack, sizeof(ack));
}

static gboolean sar_out_ack_timeout(void *user_data)
{
	struct mesh_sar_msg *sar = user_data;

	sar->activity_cnt++;

	/* Because we are GATT, and slow, only resend PKTs if it is
	 * time *and* our outbound PKT queue is empty.  */
	if (net.pkt_out == NULL)
		resend_segs(sar);

	/* Only add resent SAR pkts to empty queue */
	return true;
}

static gboolean sar_out_msg_timeout(void *user_data)
{
	struct mesh_sar_msg *sar = user_data;

	/* msg_to will expire when we return false */
	sar->msg_to = 0;

	flush_sar(&net.msg_out, sar);

	return false;
}

static gboolean sar_in_ack_timeout(void *user_data)
{
	struct mesh_sar_msg *sar = user_data;
	uint32_t full_ack = 0xffffffff >> (31 - sar->segN);

	if (sar->activity_cnt || sar->ack != full_ack)
		send_sar_ack(sar);

	return true;
}

static gboolean sar_in_msg_timeout(void *user_data)
{
	struct mesh_sar_msg *sar = user_data;

	/* msg_to will expire when we return false */
	sar->msg_to = 0;

	flush_sar(&net.sar_in, sar);

	return false;
}

static uint32_t calc_seqAuth(uint32_t seq_num, uint8_t *trans)
{
	uint32_t seqAuth = seq_num & ~0x1fff;

	seqAuth |= TRANS_SEQ0(trans);

	return seqAuth;
}

static bool seg_rxed(uint16_t net_idx, uint32_t iv_index, bool ctl,
		uint8_t ttl, uint32_t seq_num, uint16_t src, uint16_t dst,
		uint8_t *trans, uint16_t len)
{
	struct mesh_sar_msg *sar;
	uint32_t seqAuth = calc_seqAuth(seq_num, trans);
	uint8_t segN, segO;
	uint32_t old_ack, full_ack, last_ack_mask;
	bool send_ack, result = false;

	segN = TRANS_SEGN(trans);
	segO = TRANS_SEGO(trans);

	/* Only support single incoming SAR'd message per SRC */
	sar = find_sar_in_by_src(src);

	/* Reuse existing SAR structure if appropriate */
	if (sar) {
		uint64_t iv_seqAuth = (uint64_t)iv_index << 32 | seqAuth;
		uint64_t old_iv_seqAuth = (uint64_t)sar->iv_index << 32 |
			sar->seqAuth;
		if (old_iv_seqAuth < iv_seqAuth) {

			flush_sar(&net.sar_in, sar);
			sar = NULL;

		} else if (old_iv_seqAuth > iv_seqAuth) {

			/* New segment is Stale. Silently ignore */
			return false;

		} else if (segN != sar->segN) {

			/* Remote side sent conflicting data: abandon */
			flush_sar(&net.sar_in, sar);
			sar = NULL;

		}
	}

	if (sar == NULL) {
		sar = g_malloc0(sizeof(*sar) + (12 * segN));

		if (sar == NULL)
			return false;

		sar->net_idx = net_idx;
		sar->iv_index = iv_index;
		sar->ctl = ctl;
		sar->ttl = ttl;
		sar->seqAuth = seqAuth;
		sar->src = src;
		sar->dst = dst;
		sar->segmented = true;
		sar->szmic = TRANS_SZMIC(trans);
		sar->segN = segN;

		/* In all cases, the reassembled packet should begin with the
		 * same first octet of all segments, minus the SEGMENTED flag */
		sar->data[0] = trans[0] & 0x7f;

		net.sar_in = g_list_append(net.sar_in, sar);

		/* Setup expiration timers */
		if (IS_UNICAST(dst))
			sar->ack_to = g_timeout_add(5000,
					sar_in_ack_timeout, sar);

		sar->msg_to = g_timeout_add(60000, sar_in_msg_timeout, sar);
	}

	/* If last segment, calculate full msg size */
	if (segN == segO)
		sar->len = (segN * 12) + len - 3;

	/* Copy to correct offset */
	memcpy(sar->data + 1 + (12 * segO), trans + 4, 12);

	full_ack = 0xffffffff >> (31 - segN);
	last_ack_mask = 0xffffffff << segO;
	old_ack = sar->ack;
	sar->ack |= 1 << segO;
	send_ack = false;

	/* Determine if we should forward message */
	if (sar->ack == full_ack && old_ack != full_ack) {

		/* First time we have seen this complete message */
		send_ack = true;

		if (ctl)
			result = ctl_rxed(sar->net_idx, sar->iv_index,
					sar->ttl, sar->seqAuth, sar->src,
					sar->dst, sar->data, sar->len);
		else
			result = msg_rxed(sar->net_idx, sar->iv_index,
					sar->szmic, sar->ttl,
					seq_num, sar->seqAuth, sar->src,
					sar->dst, sar->data, sar->len);
	}

	/* Never Ack Group addressed SAR messages */
	if (!IS_UNICAST(dst))
		return result;

	/* Tickle the ACK system so it knows we are still RXing segments */
	sar->activity_cnt++;

	/* Determine if we should ACK */
	if (old_ack == sar->ack)
		/* Let the timer generate repeat ACKs as needed */
		send_ack = false;
	else if ((last_ack_mask & sar->ack) == (last_ack_mask & full_ack))
		/* If this was largest segO outstanding segment, we ACK */
		send_ack = true;

	if (send_ack)
		send_sar_ack(sar);

	return result;
}

bool net_data_ready(uint8_t *msg, uint8_t len)
{
	uint8_t type = *msg++;
	uint32_t iv_index = net.iv_index;
	struct mesh_net_key *net_key;

	if (len-- < 10) return false;

	if (type == PROXY_MESH_BEACON)
		return process_beacon(msg, len);
	else if (type > PROXY_CONFIG_PDU)
		return false;

	/* RXed iv_index must be equal or 1 less than local iv_index */
	/* With the clue being high-order bit of first octet */
	if (!!(iv_index & 0x01) != !!(msg[0] & 0x80)) {
		if (iv_index)
			iv_index--;
		else
			return false;
	}

	net_key = net_packet_decode(type == PROXY_CONFIG_PDU,
			iv_index, msg, len);

	if (net_key == NULL)
		return false;

	/* CTL packets have 64 bit network MIC, otherwise 32 bit MIC */
	len -= PKT_CTL(msg) ? sizeof(uint64_t) : sizeof(uint32_t);

	if (type == PROXY_CONFIG_PDU) {

		/* Proxy Configuration DST messages must be 0x0000 */
		if (PKT_DST(msg))
			return false;

		return proxy_ctl_rxed(net_key->generic.idx,
				iv_index, PKT_TTL(msg), PKT_SEQ(msg),
				PKT_SRC(msg), PKT_DST(msg),
				PKT_TRANS(msg), PKT_TRANS_LEN(len));

	} if (PKT_CTL(msg) && PKT_OPCODE(msg) == NET_OP_SEG_ACKNOWLEDGE) {

		return ack_rxed(false, PKT_SRC(msg), PKT_DST(msg),
				PKT_OBO(msg), PKT_SEQ0(msg), PKT_ACK(msg));

	} else if (PKT_SEGMENTED(msg)) {

		return seg_rxed(net_key->generic.idx, iv_index, PKT_CTL(msg),
				PKT_TTL(msg), PKT_SEQ(msg),
				PKT_SRC(msg), PKT_DST(msg),
				PKT_TRANS(msg), PKT_TRANS_LEN(len));

	} else if (!PKT_CTL(msg)){

		return msg_rxed(net_key->generic.idx,
				iv_index, false, PKT_TTL(msg), PKT_SEQ(msg),
				PKT_SEQ(msg), PKT_SRC(msg), PKT_DST(msg),
				PKT_TRANS(msg), PKT_TRANS_LEN(len));
	} else {

		return ctl_rxed(net_key->generic.idx,
				iv_index, PKT_TTL(msg), PKT_SEQ(msg),
				PKT_SRC(msg), PKT_DST(msg),
				PKT_TRANS(msg), PKT_TRANS_LEN(len));

	}

	return false;
}

bool net_session_open(GDBusProxy *data_in, bool provisioner,
					net_mesh_session_open_callback cb)
{
	if (net.proxy_in)
		return false;

	net.proxy_in = data_in;
	net.iv_upd_state = IV_UPD_INIT;
	net.blacklist = false;
	net.provisioner = provisioner;
	net.open_cb = cb;
	flush_pkt_list(&net.pkt_out);
	return true;
}

void net_session_close(GDBusProxy *data_in)
{
	if (net.proxy_in == data_in)
		net.proxy_in = NULL;

	flush_sar_list(&net.sar_in);
	flush_sar_list(&net.msg_out);
	flush_pkt_list(&net.pkt_out);
}

bool net_register_unicast(uint16_t unicast, uint8_t count)
{
	/* TODO */
	return true;
}

bool net_register_group(uint16_t group_addr)
{
	/* TODO */
	return true;
}

uint32_t net_register_virtual(uint8_t buf[16])
{
	/* TODO */
	return 0;
}

static bool get_enc_keys(uint16_t app_idx, uint16_t dst,
		uint8_t *akf_aid, uint8_t **app_enc_key,
		uint16_t *net_idx)
{
	if (app_idx == APP_IDX_DEV) {
		struct mesh_node *node;
		uint8_t *enc_key = NULL;

		if (net.provisioner) {
			/* Default to Remote Device Key when Provisioner */
			node = node_find_by_addr(dst);
			enc_key = node_get_device_key(node);
		}

		if (enc_key == NULL) {
			/* Use Local node Device Key */
			node = node_get_local_node();
			enc_key = node_get_device_key(node);
		}

		if (enc_key == NULL || node == NULL)
			return false;

		if (akf_aid) *akf_aid = 0;
		if (app_enc_key) *app_enc_key = enc_key;
		if (net_idx) *net_idx = node_get_primary_net_idx(node);

	} else {
		struct mesh_app_key *app_key = find_app_key_by_idx(app_idx);
		struct mesh_net_key *net_key;
		bool phase_two;


		if (app_key == NULL)
			return false;

		net_key = find_net_key_by_idx(app_key->net_idx);

		if (net_key == NULL)
			return false;

		if (net_idx) *net_idx = app_key->net_idx;

		phase_two = !!(net_key->phase == 2);

		if (phase_two && app_key->new.akf_aid != 0xff) {
			if (app_enc_key) *app_enc_key = app_key->new.key;
			if (akf_aid) *akf_aid = app_key->new.akf_aid;
		} else {
			if (app_enc_key) *app_enc_key = app_key->current.key;
			if (akf_aid) *akf_aid = app_key->current.akf_aid;
		}
	}

	return true;
}

bool net_ctl_msg_send(uint8_t ttl, uint16_t src, uint16_t dst,
					uint8_t *buf, uint16_t len)
{
	struct mesh_node *node = node_get_local_node();
	struct mesh_sar_msg sar_ctl;

	/* For simplicity, we will reject segmented OB CTL messages */
	if (len > 12 || node == NULL || buf == NULL || buf[0] & 0x80)
		return false;

	if (!src) {
		src = node_get_primary(node);

		if (!src)
			return false;
	}

	if (ttl == 0xff)
		ttl = net.default_ttl;

	memset(&sar_ctl, 0, sizeof(sar_ctl));

	if (!dst)
		sar_ctl.proxy = true;

	/* Get the default net_idx for remote device (or local) */
	get_enc_keys(APP_IDX_DEV, dst, NULL, NULL, &sar_ctl.net_idx);
	sar_ctl.ctl = true;
	sar_ctl.iv_index = net.iv_index - net.iv_update;
	sar_ctl.ttl = ttl;
	sar_ctl.src = src;
	sar_ctl.dst = dst;
	sar_ctl.len = len;
	memcpy(sar_ctl.data, buf, len);
	send_seg(&sar_ctl, 0);

	return true;
}

bool net_access_layer_send(uint8_t ttl, uint16_t src, uint32_t dst,
				uint16_t app_idx, uint8_t *buf, uint16_t len)
{
	struct mesh_node *node = node_get_local_node();
	struct mesh_sar_msg *sar;
	uint8_t *app_enc_key = NULL;
	uint8_t *aad = NULL;
	uint32_t mic32;
	uint8_t aad_len = 0;
	uint8_t i, j, ackless_retries = 0;
	uint8_t segN, akf_aid;
	uint16_t net_idx;
	bool result;

	if (len > 384 || node == NULL)
		return false;

	if (!src)
		src = node_get_primary(node);

	if (!src || !dst)
		return false;

	if (ttl == 0xff)
		ttl = net.default_ttl;

	if (IS_VIRTUAL(dst)) {
		struct mesh_virt_addr *virt = find_virt_by_dst(dst);

		if (virt == NULL)
			return false;

		dst = virt->va16;
		aad = virt->va128;
		aad_len = sizeof(virt->va128);
	}

	result = get_enc_keys(app_idx, dst,
			&akf_aid, &app_enc_key, &net_idx);

	if (!result)
		return false;

	segN = SEG_MAX(len + sizeof(mic32));

	/* Only one ACK required SAR message per destination at a time */
	if (segN && IS_UNICAST(dst)) {
		sar = find_sar_out_by_dst(dst);

		if (sar)
			flush_sar(&net.msg_out, sar);
	}

	sar = g_malloc0(sizeof(struct mesh_sar_msg) + (segN * 12));

	if (sar == NULL)
		return false;

	if (segN)
		sar->segmented = true;

	sar->ttl = ttl;
	sar->segN = segN;
	sar->seqAuth = net.seq_num;
	sar->iv_index = net.iv_index - net.iv_update;
	sar->net_idx = net_idx;
	sar->src = src;
	sar->dst = dst;
	sar->akf_aid = akf_aid;
	sar->len = len + sizeof(uint32_t);

	mesh_crypto_application_encrypt(akf_aid,
			sar->seqAuth, src,
			dst, sar->iv_index,
			app_enc_key,
			aad, aad_len,
			buf, len,
			sar->data, &mic32,
			sizeof(uint32_t));

	/* If sending as a segmented message to a non-Unicast (thus non-ACKing)
	 * destination, send each segments multiple times. */
	if (!IS_UNICAST(dst) && segN)
		ackless_retries = 4;

	for (j = 0; j <= ackless_retries; j++) {
		for (i = 0; i <= segN; i++)
			send_seg(sar, i);
	}

	if (IS_UNICAST(dst) && segN) {
		net.msg_out = g_list_append(net.msg_out, sar);
		sar->ack_to = g_timeout_add(2000, sar_out_ack_timeout, sar);
		sar->msg_to = g_timeout_add(60000, sar_out_msg_timeout, sar);
	} else
		g_free(sar);

	return true;
}

bool net_set_default_ttl(uint8_t ttl)
{
	if (ttl > 0x7f)
		return false;

	net.default_ttl = ttl;
	return true;
}

uint8_t net_get_default_ttl()
{
	return net.default_ttl;
}

bool net_set_seq_num(uint32_t seq_num)
{
	if (seq_num > 0xffffff)
		return false;

	net.seq_num = seq_num;
	return true;
}

uint32_t net_get_seq_num()
{
	return net.seq_num;
}