Blame mesh/friend.c

Packit 34410b
/*
Packit 34410b
 *
Packit 34410b
 *  BlueZ - Bluetooth protocol stack for Linux
Packit 34410b
 *
Packit 34410b
 *  Copyright (C) 2018-2019  Intel Corporation. All rights reserved.
Packit 34410b
 *
Packit 34410b
 *
Packit 34410b
 *  This library is free software; you can redistribute it and/or
Packit 34410b
 *  modify it under the terms of the GNU Lesser General Public
Packit 34410b
 *  License as published by the Free Software Foundation; either
Packit 34410b
 *  version 2.1 of the License, or (at your option) any later version.
Packit 34410b
 *
Packit 34410b
 *  This library is distributed in the hope that it will be useful,
Packit 34410b
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 34410b
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 34410b
 *  Lesser General Public License for more details.
Packit 34410b
 *
Packit 34410b
 */
Packit 34410b
Packit 34410b
#ifdef HAVE_CONFIG_H
Packit 34410b
#include <config.h>
Packit 34410b
#endif
Packit 34410b
Packit 34410b
#include <ell/ell.h>
Packit 34410b
Packit 34410b
#include "mesh/mesh-defs.h"
Packit 34410b
Packit 34410b
#include "mesh/net-keys.h"
Packit 34410b
#include "mesh/net.h"
Packit 34410b
#include "mesh/model.h"
Packit 34410b
#include "mesh/util.h"
Packit 34410b
Packit 34410b
#include "mesh/friend.h"
Packit 34410b
Packit 34410b
#define MAX_FRND_GROUPS		20
Packit 34410b
#define FRND_RELAY_WINDOW	250		/* 250 ms */
Packit 34410b
#define FRND_CACHE_SIZE		16
Packit 34410b
#define FRND_SUB_LIST_SIZE	8
Packit 34410b
Packit 34410b
#define RESPONSE_DELAY		(100 - 12)	/*  100  ms - 12ms hw delay */
Packit 34410b
#define MIN_RESP_DELAY		10		/*   10  ms */
Packit 34410b
#define MAX_RESP_DELAY		255		/*  255  ms */
Packit 34410b
Packit 34410b
/* Absolute maximum time to wait for LPN to choose us. */
Packit 34410b
#define RESPONSE_POLL_DELAY	1300		/* 1.300  s */
Packit 34410b
Packit 34410b
static uint8_t frnd_relay_window = FRND_RELAY_WINDOW;
Packit 34410b
static uint8_t frnd_cache_size = FRND_CACHE_SIZE;
Packit 34410b
static uint8_t frnd_sublist_size = FRND_SUB_LIST_SIZE;
Packit 34410b
Packit 34410b
struct frnd_negotiation {
Packit 34410b
	struct l_timeout	*timeout;
Packit 34410b
	struct mesh_net		*net;
Packit 34410b
	uint32_t		key_id;
Packit 34410b
	uint32_t		poll_timeout;
Packit 34410b
	uint16_t		low_power_node;
Packit 34410b
	uint16_t		old_relay;
Packit 34410b
	uint8_t			num_ele;
Packit 34410b
	uint8_t			lp_cnt;
Packit 34410b
	uint8_t			fn_cnt;
Packit 34410b
	uint8_t			wrfrw;
Packit 34410b
	uint8_t			receive_delay;
Packit 34410b
	int8_t			rssi;
Packit 34410b
	bool			clearing;
Packit 34410b
};
Packit 34410b
Packit 34410b
static struct l_queue *frnd_negotiations;
Packit 34410b
static uint16_t counter;
Packit 34410b
Packit 34410b
static void response_timeout(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct frnd_negotiation *neg = user_data;
Packit 34410b
Packit 34410b
	/* LPN did not choose us */
Packit 34410b
	l_debug("Did not win negotiation for %4.4x", neg->low_power_node);
Packit 34410b
Packit 34410b
	net_key_unref(neg->key_id);
Packit 34410b
	l_queue_remove(frnd_negotiations, neg);
Packit 34410b
	l_timeout_remove(timeout);
Packit 34410b
	l_free(neg);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void response_delay(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct frnd_negotiation *neg = user_data;
Packit 34410b
	uint16_t net_idx = mesh_net_get_primary_idx(neg->net);
Packit 34410b
	uint32_t key_id;
Packit 34410b
	uint8_t msg[8];
Packit 34410b
	uint16_t n = 0;
Packit 34410b
	bool res;
Packit 34410b
Packit 34410b
	l_timeout_remove(timeout);
Packit 34410b
Packit 34410b
	/* Create key Set for this offer */
Packit 34410b
	res = mesh_net_get_key(neg->net, false, net_idx, &key_id);
Packit 34410b
	if (!res)
Packit 34410b
		goto cleanup;
Packit 34410b
Packit 34410b
	neg->key_id = net_key_frnd_add(key_id, neg->low_power_node,
Packit 34410b
						mesh_net_get_address(neg->net),
Packit 34410b
						neg->lp_cnt, counter);
Packit 34410b
	if (!neg->key_id)
Packit 34410b
		goto cleanup;
Packit 34410b
Packit 34410b
	neg->fn_cnt = counter++;
Packit 34410b
Packit 34410b
	msg[n++] = NET_OP_FRND_OFFER;
Packit 34410b
	msg[n++] = frnd_relay_window;
Packit 34410b
	msg[n++] = frnd_cache_size;
Packit 34410b
	msg[n++] = frnd_sublist_size;
Packit 34410b
	msg[n++] = neg->rssi;
Packit 34410b
	l_put_be16(neg->fn_cnt, msg + n);
Packit 34410b
	n += 2;
Packit 34410b
	print_packet("Tx-NET_OP_FRND_OFFER", msg, n);
Packit 34410b
	mesh_net_transport_send(neg->net, 0, true,
Packit 34410b
			mesh_net_get_iv_index(neg->net), 0,
Packit 34410b
			0, 0, neg->low_power_node,
Packit 34410b
			msg, n);
Packit 34410b
Packit 34410b
	/* Offer expires in 1.3 seconds, which is the max time for LPN to
Packit 34410b
	 * receive all offers, 1 second to make decision, and a little extra
Packit 34410b
	 */
Packit 34410b
	neg->timeout = l_timeout_create_ms(1000 + MAX_RESP_DELAY,
Packit 34410b
						response_timeout, neg, NULL);
Packit 34410b
Packit 34410b
	return;
Packit 34410b
Packit 34410b
cleanup:
Packit 34410b
	net_key_unref(neg->key_id);
Packit 34410b
	l_queue_remove(frnd_negotiations, neg);
Packit 34410b
	l_free(neg);
Packit 34410b
}
Packit 34410b
Packit 34410b
static uint8_t cache_size(uint8_t power)
Packit 34410b
{
Packit 34410b
	return 1 << power;
Packit 34410b
}
Packit 34410b
Packit 34410b
static bool match_by_lpn(const void *a, const void *b)
Packit 34410b
{
Packit 34410b
	const struct frnd_negotiation *neg = a;
Packit 34410b
	uint16_t lpn = L_PTR_TO_UINT(b);
Packit 34410b
Packit 34410b
	return neg->low_power_node == lpn;
Packit 34410b
}
Packit 34410b
Packit 34410b
static bool match_by_dst(const void *a, const void *b)
Packit 34410b
{
Packit 34410b
	const struct mesh_friend *frnd = a;
Packit 34410b
	uint16_t dst = L_PTR_TO_UINT(b);
Packit 34410b
Packit 34410b
	return frnd->dst == dst;
Packit 34410b
}
Packit 34410b
Packit 34410b
/* Scaling factors in 1/10 ms */
Packit 34410b
static const int32_t scaling[] = {
Packit 34410b
	10,
Packit 34410b
	15,
Packit 34410b
	20,
Packit 34410b
	15,
Packit 34410b
};
Packit 34410b
Packit 34410b
void friend_request(struct mesh_net *net, uint16_t src,
Packit 34410b
		uint8_t minReq, uint8_t delay, uint32_t timeout,
Packit 34410b
		uint16_t prev, uint8_t num_ele, uint16_t cntr,
Packit 34410b
		int8_t rssi)
Packit 34410b
{
Packit 34410b
	struct frnd_negotiation *neg;
Packit 34410b
	uint8_t rssiScale = (minReq >> 5) & 3;
Packit 34410b
	uint8_t winScale = (minReq >> 3) & 3;
Packit 34410b
	uint8_t minCache = (minReq >> 0) & 7;
Packit 34410b
	int32_t rsp_delay;
Packit 34410b
Packit 34410b
	l_debug("RSSI of Request: %d dbm", rssi);
Packit 34410b
	l_debug("Delay: %d ms", delay);
Packit 34410b
	l_debug("Poll Timeout of Request: %d ms", timeout * 100);
Packit 34410b
	l_debug("Previous Friend: %4.4x", prev);
Packit 34410b
	l_debug("Num Elem: %2.2x", num_ele);
Packit 34410b
	l_debug("Cache Requested: %d", cache_size(minCache));
Packit 34410b
	l_debug("Cache to offer: %d", frnd_cache_size);
Packit 34410b
Packit 34410b
	/* Determine our own suitability before
Packit 34410b
	 * deciding to participate in negotiation
Packit 34410b
	 */
Packit 34410b
	if (minCache == 0 || num_ele == 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (delay < 0x0A)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (timeout < 0x00000A || timeout > 0x34BBFF)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (cache_size(minCache) > frnd_cache_size)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (frnd_negotiations == NULL)
Packit 34410b
		frnd_negotiations = l_queue_new();
Packit 34410b
Packit 34410b
	/* TODO: Check RSSI, and then start Negotiation if appropriate */
Packit 34410b
Packit 34410b
	/* We are participating in this Negotiation */
Packit 34410b
	neg = l_new(struct frnd_negotiation, 1);
Packit 34410b
	l_queue_push_head(frnd_negotiations, neg);
Packit 34410b
Packit 34410b
	neg->net = net;
Packit 34410b
	neg->low_power_node = src;
Packit 34410b
	neg->lp_cnt = cntr;
Packit 34410b
	neg->rssi = rssi;
Packit 34410b
	neg->receive_delay = delay;
Packit 34410b
	neg->poll_timeout = timeout;
Packit 34410b
	neg->old_relay = prev;
Packit 34410b
	neg->num_ele = num_ele;
Packit 34410b
Packit 34410b
	/* RSSI (Negative Factor, larger values == less time)
Packit 34410b
	 * Scaling factor 0-3 == multiplier of 1.0 - 2.5
Packit 34410b
	 * Minimum factor of 1. Bit 1 adds additional factor
Packit 34410b
	 * of 1, bit zero and additional 0.5
Packit 34410b
	 */
Packit 34410b
	rsp_delay = -(rssi * scaling[rssiScale]);
Packit 34410b
	l_debug("RSSI Factor: %d ms", rsp_delay / 10);
Packit 34410b
Packit 34410b
	/* Relay Window (Positive Factor, larger values == more time)
Packit 34410b
	 * Scaling factor 0-3 == multiplier of 1.0 - 2.5
Packit 34410b
	 * Minimum factor of 1. Bit 1 adds additional factor
Packit 34410b
	 * of 1, bit zero and additional 0.5
Packit 34410b
	 */
Packit 34410b
	rsp_delay += frnd_relay_window * scaling[winScale];
Packit 34410b
	l_debug("Win Size Factor: %d ms",
Packit 34410b
			(frnd_relay_window * scaling[winScale]) / 10);
Packit 34410b
Packit 34410b
	/* Normalize to ms */
Packit 34410b
	rsp_delay /= 10;
Packit 34410b
Packit 34410b
	/* Range limits are 10-255 ms */
Packit 34410b
	if (rsp_delay < MIN_RESP_DELAY)
Packit 34410b
		rsp_delay = MIN_RESP_DELAY;
Packit 34410b
	else if (rsp_delay > MAX_RESP_DELAY)
Packit 34410b
		rsp_delay = MAX_RESP_DELAY;
Packit 34410b
Packit 34410b
	l_debug("Total Response Delay: %d ms", rsp_delay);
Packit 34410b
Packit 34410b
	/* Add in 100ms delay before start of "Offer Period" */
Packit 34410b
	rsp_delay += RESPONSE_DELAY;
Packit 34410b
Packit 34410b
	neg->timeout = l_timeout_create_ms(rsp_delay,
Packit 34410b
						response_delay, neg, NULL);
Packit 34410b
}
Packit 34410b
Packit 34410b
static struct l_queue *retired_lpns;
Packit 34410b
Packit 34410b
void friend_clear_confirm(struct mesh_net *net, uint16_t src,
Packit 34410b
					uint16_t lpn, uint16_t lpnCounter)
Packit 34410b
{
Packit 34410b
	struct frnd_negotiation *neg = l_queue_remove_if(frnd_negotiations,
Packit 34410b
					match_by_lpn, L_UINT_TO_PTR(lpn));
Packit 34410b
Packit 34410b
	l_debug("Friend Clear confirmed %4.4x (cnt %4.4x)", lpn, lpnCounter);
Packit 34410b
Packit 34410b
	if (!neg)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	l_timeout_remove(neg->timeout);
Packit 34410b
	l_queue_remove(frnd_negotiations, neg);
Packit 34410b
	l_free(neg);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void friend_poll_timeout(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct mesh_friend *frnd = user_data;
Packit 34410b
Packit 34410b
	if (mesh_friend_clear(frnd->net, frnd))
Packit 34410b
		l_debug("Friend Poll Timeout %4.4x", frnd->dst);
Packit 34410b
Packit 34410b
	l_timeout_remove(frnd->timeout);
Packit 34410b
	frnd->timeout = NULL;
Packit 34410b
Packit 34410b
	/* Friend may be in either Network or Retired list, so try both */
Packit 34410b
	l_queue_remove(retired_lpns, frnd);
Packit 34410b
	mesh_friend_free(frnd);
Packit 34410b
}
Packit 34410b
Packit 34410b
void friend_clear(struct mesh_net *net, uint16_t src, uint16_t lpn,
Packit 34410b
				uint16_t lpnCounter, struct mesh_friend *frnd)
Packit 34410b
{
Packit 34410b
	uint8_t msg[5] = { NET_OP_FRND_CLEAR_CONFIRM };
Packit 34410b
	bool removed = false;
Packit 34410b
	uint16_t lpnDelta;
Packit 34410b
Packit 34410b
	if (frnd) {
Packit 34410b
		lpnDelta = lpnCounter - frnd->lp_cnt;
Packit 34410b
Packit 34410b
		/* Ignore old Friend Clear commands */
Packit 34410b
		if (lpnDelta > 0x100)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		/* Move friend from Network list to Retired list */
Packit 34410b
		removed = mesh_friend_clear(net, frnd);
Packit 34410b
		if (removed) {
Packit 34410b
			struct mesh_friend *old;
Packit 34410b
			struct frnd_negotiation *neg = l_queue_remove_if(
Packit 34410b
						frnd_negotiations,
Packit 34410b
						match_by_lpn,
Packit 34410b
						L_UINT_TO_PTR(frnd->dst));
Packit 34410b
Packit 34410b
			/* Cancel any negotiations or clears */
Packit 34410b
			if (neg) {
Packit 34410b
				l_timeout_remove(neg->timeout);
Packit 34410b
				l_free(neg);
Packit 34410b
			}
Packit 34410b
Packit 34410b
			/* Create Retired LPN list if needed */
Packit 34410b
			if (retired_lpns == NULL)
Packit 34410b
				retired_lpns = l_queue_new();
Packit 34410b
Packit 34410b
			/* Find any duplicates */
Packit 34410b
			old = l_queue_find(retired_lpns, match_by_dst,
Packit 34410b
						L_UINT_TO_PTR(lpn));
Packit 34410b
Packit 34410b
			/* Force time-out of old friendship */
Packit 34410b
			if (old)
Packit 34410b
				friend_poll_timeout(old->timeout, old);
Packit 34410b
Packit 34410b
			/* Retire this LPN (keeps timeout running) */
Packit 34410b
			l_queue_push_tail(retired_lpns, frnd);
Packit 34410b
		}
Packit 34410b
	} else {
Packit 34410b
		frnd = l_queue_find(retired_lpns, match_by_dst,
Packit 34410b
						L_UINT_TO_PTR(lpn));
Packit 34410b
		if (!frnd)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		lpnDelta = lpnCounter - frnd->lp_cnt;
Packit 34410b
Packit 34410b
		/* Ignore old Friend Clear commands */
Packit 34410b
		if (!lpnDelta || (lpnDelta > 0x100))
Packit 34410b
			return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	l_debug("Friend Cleared %4.4x (%4.4x)", lpn, lpnCounter);
Packit 34410b
Packit 34410b
	l_put_be16(lpn, msg + 1);
Packit 34410b
	l_put_be16(lpnCounter, msg + 3);
Packit 34410b
	mesh_net_transport_send(net, 0, false,
Packit 34410b
			mesh_net_get_iv_index(net), DEFAULT_TTL,
Packit 34410b
			0, 0, src,
Packit 34410b
			msg, sizeof(msg));
Packit 34410b
}
Packit 34410b
Packit 34410b
static void clear_retry(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct frnd_negotiation *neg = user_data;
Packit 34410b
	uint8_t msg[5] = { NET_OP_FRND_CLEAR };
Packit 34410b
	uint32_t secs = 1 << neg->receive_delay;
Packit 34410b
Packit 34410b
Packit 34410b
	l_put_be16(neg->low_power_node, msg + 1);
Packit 34410b
	l_put_be16(neg->lp_cnt, msg + 3);
Packit 34410b
	mesh_net_transport_send(neg->net, 0, false,
Packit 34410b
			mesh_net_get_iv_index(neg->net), DEFAULT_TTL,
Packit 34410b
			0, 0, neg->old_relay,
Packit 34410b
			msg, sizeof(msg));
Packit 34410b
Packit 34410b
	if (secs && ((secs << 1) < neg->poll_timeout/10)) {
Packit 34410b
		neg->receive_delay++;
Packit 34410b
		l_debug("Try FRND_CLR again in %d seconds (total timeout %d)",
Packit 34410b
						secs, neg->poll_timeout/10);
Packit 34410b
		l_timeout_modify(neg->timeout, secs);
Packit 34410b
	} else {
Packit 34410b
		l_debug("FRND_CLR timed out %d", secs);
Packit 34410b
		l_timeout_remove(timeout);
Packit 34410b
		l_queue_remove(frnd_negotiations, neg);
Packit 34410b
		l_free(neg);
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
static void friend_delay_rsp(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct mesh_friend *frnd = user_data;
Packit 34410b
	struct mesh_friend_msg *pkt = frnd->pkt;
Packit 34410b
	struct mesh_net *net = frnd->net;
Packit 34410b
	uint32_t net_seq, iv_index;
Packit 34410b
	uint8_t upd[7] = { NET_OP_FRND_UPDATE };
Packit 34410b
Packit 34410b
	l_timeout_remove(timeout);
Packit 34410b
Packit 34410b
	if (pkt == NULL)
Packit 34410b
		goto update;
Packit 34410b
Packit 34410b
	if (pkt->ctl) {
Packit 34410b
		/* Make sure we don't change the bit-sense of MD,
Packit 34410b
		 * once it has been set because that would cause
Packit 34410b
		 * a "Dirty Nonce" security violation
Packit 34410b
		 */
Packit 34410b
		if (((pkt->u.one[0].hdr >> OPCODE_HDR_SHIFT) & OPCODE_MASK) ==
Packit 34410b
						NET_OP_SEG_ACKNOWLEDGE) {
Packit 34410b
			bool rly = !!((pkt->u.one[0].hdr >> RELAY_HDR_SHIFT) &
Packit 34410b
									true);
Packit 34410b
			uint16_t seqZero = pkt->u.one[0].hdr >>
Packit 34410b
							SEQ_ZERO_HDR_SHIFT;
Packit 34410b
Packit 34410b
			seqZero &= SEQ_ZERO_MASK;
Packit 34410b
Packit 34410b
			l_debug("Fwd ACK pkt %6.6x-%8.8x",
Packit 34410b
					pkt->u.one[0].seq,
Packit 34410b
					pkt->iv_index);
Packit 34410b
Packit 34410b
			pkt->u.one[0].sent = true;
Packit 34410b
			mesh_net_ack_send(net, frnd->net_key_cur,
Packit 34410b
					pkt->iv_index, pkt->ttl,
Packit 34410b
					pkt->u.one[0].seq, pkt->src, pkt->dst,
Packit 34410b
					rly, seqZero,
Packit 34410b
					l_get_be32(pkt->u.one[0].data));
Packit 34410b
Packit 34410b
Packit 34410b
		} else {
Packit 34410b
			l_debug("Fwd CTL pkt %6.6x-%8.8x",
Packit 34410b
					pkt->u.one[0].seq,
Packit 34410b
					pkt->iv_index);
Packit 34410b
Packit 34410b
			print_packet("Frnd-CTL",
Packit 34410b
					pkt->u.one[0].data, pkt->last_len);
Packit 34410b
Packit 34410b
			pkt->u.one[0].sent = true;
Packit 34410b
			mesh_net_transport_send(net, frnd->net_key_cur, false,
Packit 34410b
					pkt->iv_index, pkt->ttl,
Packit 34410b
					pkt->u.one[0].seq, pkt->src, pkt->dst,
Packit 34410b
					pkt->u.one[0].data, pkt->last_len);
Packit 34410b
		}
Packit 34410b
	} else {
Packit 34410b
		/* If segments after this one, then More Data must be TRUE */
Packit 34410b
		uint8_t len;
Packit 34410b
Packit 34410b
		if (pkt->cnt_out < pkt->cnt_in)
Packit 34410b
			len = sizeof(pkt->u.s12[0].data);
Packit 34410b
		else
Packit 34410b
			len = pkt->last_len;
Packit 34410b
Packit 34410b
		l_debug("Fwd FRND pkt %6.6x",
Packit 34410b
				pkt->u.s12[pkt->cnt_out].seq);
Packit 34410b
Packit 34410b
		print_packet("Frnd-Msg", pkt->u.s12[pkt->cnt_out].data, len);
Packit 34410b
Packit 34410b
		pkt->u.s12[pkt->cnt_out].sent = true;
Packit 34410b
		mesh_net_send_seg(net, frnd->net_key_cur,
Packit 34410b
				pkt->iv_index,
Packit 34410b
				pkt->ttl,
Packit 34410b
				pkt->u.s12[pkt->cnt_out].seq,
Packit 34410b
				pkt->src, pkt->dst,
Packit 34410b
				pkt->u.s12[pkt->cnt_out].hdr,
Packit 34410b
				pkt->u.s12[pkt->cnt_out].data, len);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return;
Packit 34410b
Packit 34410b
update:
Packit 34410b
	/* No More Data -- send Update message with md = false */
Packit 34410b
	net_seq = mesh_net_get_seq_num(net);
Packit 34410b
	l_debug("Fwd FRND UPDATE %6.6x with MD == 0", net_seq);
Packit 34410b
Packit 34410b
	frnd->last = frnd->seq;
Packit 34410b
	mesh_net_get_snb_state(net, upd + 1, &iv_index);
Packit 34410b
	l_put_be32(iv_index, upd + 2);
Packit 34410b
	upd[6] = false; /* Queue is Empty */
Packit 34410b
	print_packet("Update", upd, sizeof(upd));
Packit 34410b
	mesh_net_transport_send(net, frnd->net_key_cur, false,
Packit 34410b
			mesh_net_get_iv_index(net), 0,
Packit 34410b
			net_seq, 0, frnd->dst,
Packit 34410b
			upd, sizeof(upd));
Packit 34410b
	mesh_net_next_seq_num(net);
Packit 34410b
}
Packit 34410b
Packit 34410b
Packit 34410b
void friend_poll(struct mesh_net *net, uint16_t src, bool seq,
Packit 34410b
					struct mesh_friend *frnd)
Packit 34410b
{
Packit 34410b
	struct frnd_negotiation *neg;
Packit 34410b
	struct mesh_friend_msg *pkt;
Packit 34410b
	bool md;
Packit 34410b
Packit 34410b
	neg = l_queue_find(frnd_negotiations, match_by_lpn, L_UINT_TO_PTR(src));
Packit 34410b
	if (neg && !neg->clearing) {
Packit 34410b
		uint8_t msg[5] = { NET_OP_FRND_CLEAR };
Packit 34410b
Packit 34410b
		l_debug("Won negotiation for %4.4x", neg->low_power_node);
Packit 34410b
Packit 34410b
		/* This call will clean-up and replace if already friends */
Packit 34410b
		frnd = mesh_friend_new(net, src, neg->num_ele,
Packit 34410b
						neg->receive_delay,
Packit 34410b
						neg->wrfrw,
Packit 34410b
						neg->poll_timeout,
Packit 34410b
						neg->fn_cnt, neg->lp_cnt);
Packit 34410b
Packit 34410b
		frnd->timeout = l_timeout_create_ms(
Packit 34410b
					frnd->poll_timeout * 100,
Packit 34410b
					friend_poll_timeout, frnd, NULL);
Packit 34410b
Packit 34410b
		l_timeout_remove(neg->timeout);
Packit 34410b
		net_key_unref(neg->key_id);
Packit 34410b
		neg->key_id = 0;
Packit 34410b
Packit 34410b
		if (neg->old_relay == 0 ||
Packit 34410b
				neg->old_relay == mesh_net_get_address(net)) {
Packit 34410b
			l_queue_remove(frnd_negotiations, neg);
Packit 34410b
			l_free(neg);
Packit 34410b
		} else {
Packit 34410b
			neg->clearing = true;
Packit 34410b
			l_put_be16(neg->low_power_node, msg + 1);
Packit 34410b
			l_put_be16(neg->lp_cnt, msg + 3);
Packit 34410b
			mesh_net_transport_send(net, 0, false,
Packit 34410b
					mesh_net_get_iv_index(net), DEFAULT_TTL,
Packit 34410b
					0, 0, neg->old_relay,
Packit 34410b
					msg, sizeof(msg));
Packit 34410b
Packit 34410b
			/* Reuse receive_delay as a shift counter to
Packit 34410b
			 * time-out FRIEND_CLEAR
Packit 34410b
			 */
Packit 34410b
			neg->receive_delay = 1;
Packit 34410b
			neg->timeout = l_timeout_create(1, clear_retry,
Packit 34410b
								neg, NULL);
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (!frnd)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	/* Reset Poll Timeout */
Packit 34410b
	l_timeout_modify_ms(frnd->timeout, frnd->poll_timeout * 100);
Packit 34410b
Packit 34410b
	if (!l_queue_length(frnd->pkt_cache))
Packit 34410b
		goto update;
Packit 34410b
Packit 34410b
	if (frnd->seq != frnd->last && frnd->seq != seq) {
Packit 34410b
		pkt = l_queue_peek_head(frnd->pkt_cache);
Packit 34410b
		if (pkt->cnt_out < pkt->cnt_in) {
Packit 34410b
			pkt->cnt_out++;
Packit 34410b
		} else {
Packit 34410b
			pkt = l_queue_pop_head(frnd->pkt_cache);
Packit 34410b
			l_free(pkt);
Packit 34410b
		}
Packit 34410b
	}
Packit 34410b
Packit 34410b
	pkt = l_queue_peek_head(frnd->pkt_cache);
Packit 34410b
Packit 34410b
	if (!pkt)
Packit 34410b
		goto update;
Packit 34410b
Packit 34410b
	frnd->seq = seq;
Packit 34410b
	frnd->last = !seq;
Packit 34410b
	md = !!(l_queue_length(frnd->pkt_cache) > 1);
Packit 34410b
Packit 34410b
	if (pkt->ctl) {
Packit 34410b
		/* Make sure we don't change the bit-sense of MD,
Packit 34410b
		 * once it has been set because that would cause
Packit 34410b
		 * a "Dirty Nonce" security violation
Packit 34410b
		 */
Packit 34410b
		if (!(pkt->u.one[0].sent))
Packit 34410b
			pkt->u.one[0].md = md;
Packit 34410b
	} else {
Packit 34410b
		/* If segments after this one, then More Data must be TRUE */
Packit 34410b
		if (pkt->cnt_out < pkt->cnt_in)
Packit 34410b
			md = true;
Packit 34410b
Packit 34410b
		/* Make sure we don't change the bit-sense of MD, once
Packit 34410b
		 * it has been set because that would cause a
Packit 34410b
		 * "Dirty Nonce" security violation
Packit 34410b
		 */
Packit 34410b
		if (!(pkt->u.s12[pkt->cnt_out].sent))
Packit 34410b
			pkt->u.s12[pkt->cnt_out].md = md;
Packit 34410b
	}
Packit 34410b
	frnd->pkt = pkt;
Packit 34410b
	l_timeout_create_ms(frnd->frd, friend_delay_rsp, frnd, NULL);
Packit 34410b
Packit 34410b
	return;
Packit 34410b
Packit 34410b
update:
Packit 34410b
	frnd->pkt = NULL;
Packit 34410b
	l_timeout_create_ms(frnd->frd, friend_delay_rsp, frnd, NULL);
Packit 34410b
}
Packit 34410b
Packit 34410b
void friend_sub_add(struct mesh_net *net, struct mesh_friend *frnd,
Packit 34410b
					const uint8_t *pkt, uint8_t len)
Packit 34410b
{
Packit 34410b
	uint16_t *new_list;
Packit 34410b
	uint32_t net_seq;
Packit 34410b
	uint8_t plen = len;
Packit 34410b
	uint8_t msg[] = { NET_OP_PROXY_SUB_CONFIRM, 0 };
Packit 34410b
Packit 34410b
	if (!frnd || MAX_FRND_GROUPS < frnd->grp_cnt + (len/2))
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	msg[1] = *pkt++;
Packit 34410b
	plen--;
Packit 34410b
Packit 34410b
	/* Sanity Check Values, abort if any illegal */
Packit 34410b
	while (plen >= 2) {
Packit 34410b
		plen -= 2;
Packit 34410b
		if (l_get_be16(pkt + plen) < 0x8000)
Packit 34410b
			return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	new_list = l_malloc(frnd->grp_cnt * sizeof(uint16_t) + len);
Packit 34410b
	if (frnd->grp_list)
Packit 34410b
		memcpy(new_list, frnd->grp_list,
Packit 34410b
				frnd->grp_cnt * sizeof(uint16_t));
Packit 34410b
Packit 34410b
	while (len >= 2) {
Packit 34410b
		new_list[frnd->grp_cnt++] = l_get_be16(pkt);
Packit 34410b
		pkt += 2;
Packit 34410b
		len -= 2;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	l_free(frnd->grp_list);
Packit 34410b
	frnd->grp_list = new_list;
Packit 34410b
Packit 34410b
	print_packet("Tx-NET_OP_PROXY_SUB_CONFIRM", msg, sizeof(msg));
Packit 34410b
	net_seq = mesh_net_get_seq_num(net);
Packit 34410b
	mesh_net_transport_send(net, frnd->net_key_cur, false,
Packit 34410b
			mesh_net_get_iv_index(net), 0,
Packit 34410b
			net_seq, 0, frnd->dst,
Packit 34410b
			msg, sizeof(msg));
Packit 34410b
	mesh_net_next_seq_num(net);
Packit 34410b
}
Packit 34410b
Packit 34410b
void friend_sub_del(struct mesh_net *net, struct mesh_friend *frnd,
Packit 34410b
					const uint8_t *pkt, uint8_t len)
Packit 34410b
{
Packit 34410b
	uint32_t net_seq;
Packit 34410b
	uint8_t msg[] = { NET_OP_PROXY_SUB_CONFIRM, 0 };
Packit 34410b
	int i;
Packit 34410b
Packit 34410b
	if (!frnd)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	msg[1] = *pkt++;
Packit 34410b
	len--;
Packit 34410b
Packit 34410b
	while (len >= 2) {
Packit 34410b
		uint16_t grp = l_get_be16(pkt);
Packit 34410b
Packit 34410b
		for (i = frnd->grp_cnt - 1; i >= 0; i--) {
Packit 34410b
			if (frnd->grp_list[i] == grp) {
Packit 34410b
				frnd->grp_cnt--;
Packit 34410b
				memcpy(&frnd->grp_list[i],
Packit 34410b
						&frnd->grp_list[i + 1],
Packit 34410b
						(frnd->grp_cnt - i) * 2);
Packit 34410b
				break;
Packit 34410b
			}
Packit 34410b
		}
Packit 34410b
		len -= 2;
Packit 34410b
		pkt += 2;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	print_packet("Tx-NET_OP_PROXY_SUB_CONFIRM", msg, sizeof(msg));
Packit 34410b
	net_seq = mesh_net_get_seq_num(net);
Packit 34410b
	mesh_net_transport_send(net, frnd->net_key_cur, false,
Packit 34410b
			mesh_net_get_iv_index(net), 0,
Packit 34410b
			net_seq, 0, frnd->dst,
Packit 34410b
			msg, sizeof(msg));
Packit 34410b
	mesh_net_next_seq_num(net);
Packit 34410b
}
Packit 34410b
Packit 34410b
/* Low-Power-Node role */
Packit 34410b
struct frnd_offers {
Packit 34410b
	uint16_t fn_cnt;
Packit 34410b
	uint16_t src;
Packit 34410b
	uint8_t window;
Packit 34410b
	uint8_t cache;
Packit 34410b
	uint8_t sub_list_size;
Packit 34410b
	int8_t local_rssi;
Packit 34410b
	int8_t remote_rssi;
Packit 34410b
};
Packit 34410b
Packit 34410b
#define MAX_POLL_RETRIES	5
Packit 34410b
static bool quick_pick;
Packit 34410b
static uint8_t poll_cnt;
Packit 34410b
static struct l_queue *offers;
Packit 34410b
static uint16_t old_friend;
Packit 34410b
static uint16_t fn_cnt, cnt = 0xffff;
Packit 34410b
static uint32_t poll_period_ms;
Packit 34410b
static struct l_timeout *poll_retry_to;
Packit 34410b
static struct l_timeout *poll_period_to;
Packit 34410b
static uint32_t lpn_key_id;
Packit 34410b
static uint32_t new_lpn_id;
Packit 34410b
Packit 34410b
void frnd_offer(struct mesh_net *net, uint16_t src, uint8_t window,
Packit 34410b
			uint8_t cache, uint8_t sub_list_size,
Packit 34410b
			int8_t r_rssi, int8_t l_rssi, uint16_t fn_cnt)
Packit 34410b
{
Packit 34410b
	struct frnd_offers *offer;
Packit 34410b
Packit 34410b
	l_debug("RSSI of Offer: %d dbm", l_rssi);
Packit 34410b
Packit 34410b
	/* Ignore RFU window value 0 */
Packit 34410b
	if (window == 0)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (mesh_net_get_friend(net))
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	if (quick_pick) {
Packit 34410b
		if (mesh_net_set_friend(net, src)) {
Packit 34410b
			old_friend = src;
Packit 34410b
			frnd_poll(net, false);
Packit 34410b
		}
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	offer = l_new(struct frnd_offers, 1);
Packit 34410b
	offer->src = src;
Packit 34410b
	offer->window = window;
Packit 34410b
	offer->cache = cache;
Packit 34410b
	offer->sub_list_size = sub_list_size;
Packit 34410b
	offer->local_rssi = l_rssi;
Packit 34410b
	offer->remote_rssi = r_rssi;
Packit 34410b
	offer->fn_cnt = fn_cnt;
Packit 34410b
Packit 34410b
	l_queue_push_tail(offers, offer);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void frnd_poll_timeout(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct mesh_net *net = user_data;
Packit 34410b
Packit 34410b
	frnd_poll(net, true);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void frnd_negotiated_to(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct mesh_net *net = user_data;
Packit 34410b
Packit 34410b
	l_debug("frnd_negotiated_to");
Packit 34410b
	if (!mesh_net_get_friend(net)) {
Packit 34410b
		l_timeout_remove(poll_period_to);
Packit 34410b
		poll_period_to = NULL;
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (!poll_retry_to)
Packit 34410b
		frnd_poll(net, false);
Packit 34410b
}
Packit 34410b
Packit 34410b
void frnd_poll_cancel(struct mesh_net *net)
Packit 34410b
{
Packit 34410b
	l_timeout_remove(poll_retry_to);
Packit 34410b
	poll_retry_to = NULL;
Packit 34410b
}
Packit 34410b
Packit 34410b
void frnd_poll(struct mesh_net *net, bool retry)
Packit 34410b
{
Packit 34410b
	uint32_t key_id = lpn_key_id;
Packit 34410b
	uint32_t net_seq;
Packit 34410b
	uint8_t msg[2] = { NET_OP_FRND_POLL };
Packit 34410b
	bool seq = mesh_net_get_frnd_seq(net);
Packit 34410b
Packit 34410b
	/* Check if we are in Phase 2 of Key Refresh */
Packit 34410b
	if (new_lpn_id) {
Packit 34410b
		uint8_t phase;
Packit 34410b
		uint16_t net_idx = mesh_net_get_primary_idx(net);
Packit 34410b
		uint8_t status =
Packit 34410b
			mesh_net_key_refresh_phase_get(net, net_idx, &phase);
Packit 34410b
Packit 34410b
		if (status == MESH_STATUS_SUCCESS &&
Packit 34410b
				phase == KEY_REFRESH_PHASE_TWO)
Packit 34410b
			key_id = new_lpn_id;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (!retry) {
Packit 34410b
		poll_cnt = MAX_POLL_RETRIES;
Packit 34410b
		seq = !seq;
Packit 34410b
		mesh_net_set_frnd_seq(net, seq);
Packit 34410b
	} else if (!(poll_cnt--)) {
Packit 34410b
		l_debug("Lost Friendship with %4.4x", old_friend);
Packit 34410b
		l_timeout_remove(poll_period_to);
Packit 34410b
		poll_period_to = NULL;
Packit 34410b
		frnd_poll_cancel(net);
Packit 34410b
		net_key_unref(lpn_key_id);
Packit 34410b
		net_key_unref(new_lpn_id);
Packit 34410b
		new_lpn_id = lpn_key_id = 0;
Packit 34410b
		mesh_net_set_friend(net, 0);
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	if (poll_retry_to)
Packit 34410b
		l_timeout_remove(poll_retry_to);
Packit 34410b
Packit 34410b
	l_debug("TX-FRIEND POLL %d", seq);
Packit 34410b
	msg[1] = seq;
Packit 34410b
	net_seq = mesh_net_get_seq_num(net);
Packit 34410b
	mesh_net_transport_send(net, key_id, true,
Packit 34410b
			mesh_net_get_iv_index(net), 0,
Packit 34410b
			net_seq, 0, mesh_net_get_friend(net),
Packit 34410b
			msg, sizeof(msg));
Packit 34410b
	mesh_net_next_seq_num(net);
Packit 34410b
	poll_retry_to = l_timeout_create_ms(1000, frnd_poll_timeout, net, NULL);
Packit 34410b
Packit 34410b
	/* Reset Poll Period for next "Wake Up" */
Packit 34410b
	if (poll_period_to)
Packit 34410b
		l_timeout_modify_ms(poll_period_to, poll_period_ms);
Packit 34410b
	else
Packit 34410b
		poll_period_to = l_timeout_create_ms(poll_period_ms,
Packit 34410b
						frnd_negotiated_to, net, NULL);
Packit 34410b
}
Packit 34410b
Packit 34410b
void frnd_ack_poll(struct mesh_net *net)
Packit 34410b
{
Packit 34410b
	/* Start new POLL, but only if not already Polling */
Packit 34410b
	if (poll_retry_to == NULL)
Packit 34410b
		frnd_poll(net, false);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void req_timeout(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct mesh_net *net = user_data;
Packit 34410b
	struct frnd_offers *best;
Packit 34410b
	struct frnd_offers *offer = l_queue_pop_head(offers);
Packit 34410b
	uint32_t key_id = 0;
Packit 34410b
	bool res;
Packit 34410b
Packit 34410b
	l_timeout_remove(timeout);
Packit 34410b
Packit 34410b
	best = offer;
Packit 34410b
	while (offer) {
Packit 34410b
		/* Screen out clearly inferior RSSI friends first */
Packit 34410b
		if (offer->local_rssi < -40 && offer->remote_rssi < -40) {
Packit 34410b
			if (best->local_rssi + 20 < offer->local_rssi ||
Packit 34410b
				best->remote_rssi + 20 < offer->remote_rssi) {
Packit 34410b
Packit 34410b
				l_free(best);
Packit 34410b
				best = offer;
Packit 34410b
				offer = l_queue_pop_head(offers);
Packit 34410b
				continue;
Packit 34410b
			}
Packit 34410b
		}
Packit 34410b
Packit 34410b
		/* Otherwise use best Windows, with Cache size as tie breaker */
Packit 34410b
		if (best->window > offer->window ||
Packit 34410b
				(best->window == offer->window &&
Packit 34410b
				 best->cache < offer->cache)) {
Packit 34410b
			l_free(best);
Packit 34410b
			best = offer;
Packit 34410b
		} else if (best != offer)
Packit 34410b
			l_free(offer);
Packit 34410b
Packit 34410b
		offer = l_queue_pop_head(offers);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	net_key_unref(lpn_key_id);
Packit 34410b
	net_key_unref(new_lpn_id);
Packit 34410b
	new_lpn_id = lpn_key_id = 0;
Packit 34410b
	if (mesh_net_get_friend(net)) {
Packit 34410b
		l_free(best);
Packit 34410b
		return;
Packit 34410b
	} else if (!best) {
Packit 34410b
		l_debug("No Offers Received");
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	fn_cnt = best->fn_cnt;
Packit 34410b
	res = mesh_net_get_key(net, false, mesh_net_get_primary_idx(net),
Packit 34410b
								&key_id);
Packit 34410b
	if (!res)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	lpn_key_id = net_key_frnd_add(key_id, mesh_net_get_address(net),
Packit 34410b
						best->src, cnt, best->fn_cnt);
Packit 34410b
	if (!lpn_key_id)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	res = mesh_net_get_key(net, true, mesh_net_get_primary_idx(net),
Packit 34410b
								&key_id);
Packit 34410b
Packit 34410b
	if (!res)
Packit 34410b
		goto old_keys_only;
Packit 34410b
Packit 34410b
	new_lpn_id = net_key_frnd_add(key_id, mesh_net_get_address(net),
Packit 34410b
						best->src, cnt, best->fn_cnt);
Packit 34410b
Packit 34410b
old_keys_only:
Packit 34410b
Packit 34410b
	l_debug("Winning offer %4.4x RSSI: %ddb Window: %dms Cache sz: %d",
Packit 34410b
			best->src, best->local_rssi,
Packit 34410b
			best->window, best->cache);
Packit 34410b
Packit 34410b
	if (mesh_net_set_friend(net, best->src)) {
Packit 34410b
		old_friend = best->src;
Packit 34410b
		mesh_net_set_frnd_seq(net, true);
Packit 34410b
		frnd_poll(net, false);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	l_free(best);
Packit 34410b
}
Packit 34410b
Packit 34410b
void frnd_clear(struct mesh_net *net)
Packit 34410b
{
Packit 34410b
	uint8_t msg[12];
Packit 34410b
	uint8_t n = 0;
Packit 34410b
	uint16_t frnd_addr = mesh_net_get_friend(net);
Packit 34410b
	uint16_t my_addr = mesh_net_get_address(net);
Packit 34410b
Packit 34410b
	msg[n++] = NET_OP_FRND_CLEAR;
Packit 34410b
	l_put_be16(my_addr, msg + n);
Packit 34410b
	n += 2;
Packit 34410b
	l_put_be16(cnt, msg + n);
Packit 34410b
	n += 2;
Packit 34410b
Packit 34410b
	net_key_unref(lpn_key_id);
Packit 34410b
	net_key_unref(new_lpn_id);
Packit 34410b
	mesh_net_set_friend(net, 0);
Packit 34410b
Packit 34410b
	mesh_net_transport_send(net, 0, false,
Packit 34410b
			mesh_net_get_iv_index(net), 0,
Packit 34410b
			0, 0, frnd_addr,
Packit 34410b
			msg, n);
Packit 34410b
}
Packit 34410b
Packit 34410b
void frnd_request_friend(struct mesh_net *net, uint8_t cache,
Packit 34410b
			uint8_t offer_delay, uint8_t delay, uint32_t timeout)
Packit 34410b
{
Packit 34410b
	uint8_t msg[12];
Packit 34410b
	uint8_t n = 0;
Packit 34410b
Packit 34410b
	if (offers == NULL)
Packit 34410b
		offers = l_queue_new();
Packit 34410b
Packit 34410b
	msg[n++] = NET_OP_FRND_REQUEST;
Packit 34410b
	msg[n] = cache & 0x07;		/* MinRequirements - Cache */
Packit 34410b
	msg[n++] |= (offer_delay & 0x0f) << 3;	/* Offer Delay */
Packit 34410b
	poll_period_ms = (timeout * 300) / 4; /* 3/4 of the time in ms */
Packit 34410b
	l_put_be32(timeout, msg + n);	/* PollTimeout */
Packit 34410b
	msg[n++] = delay;		/* ReceiveDelay */
Packit 34410b
	n += 3;
Packit 34410b
	l_put_be16(old_friend, msg + n);	/* PreviousAddress */
Packit 34410b
	n += 2;
Packit 34410b
	msg[n++] = mesh_net_get_num_ele(net);	/* NumElements */
Packit 34410b
	l_put_be16(cnt + 1, msg + n);	/* Next counter */
Packit 34410b
	n += 2;
Packit 34410b
	print_packet("Tx-NET_OP_FRND_REQUEST", msg, n);
Packit 34410b
	mesh_net_transport_send(net, 0, false,
Packit 34410b
			mesh_net_get_iv_index(net), 0,
Packit 34410b
			0, 0, FRIENDS_ADDRESS,
Packit 34410b
			msg, n);
Packit 34410b
	l_timeout_create_ms(1000, req_timeout, net, NULL); /* 1000 ms */
Packit 34410b
	mesh_net_set_friend(net, 0);
Packit 34410b
	cnt++;
Packit 34410b
}
Packit 34410b
Packit 34410b
static uint8_t trans_id;
Packit 34410b
void frnd_sub_add(struct mesh_net *net, uint32_t parms[7])
Packit 34410b
{
Packit 34410b
	uint32_t key_id = lpn_key_id;
Packit 34410b
	uint32_t net_seq;
Packit 34410b
	uint8_t msg[15] = { NET_OP_PROXY_SUB_ADD };
Packit 34410b
	uint8_t i, n = 1;
Packit 34410b
Packit 34410b
	/* Check if we are in Phase 2 of Key Refresh */
Packit 34410b
	if (new_lpn_id) {
Packit 34410b
		uint8_t phase;
Packit 34410b
		uint16_t net_idx = mesh_net_get_primary_idx(net);
Packit 34410b
		uint8_t status = mesh_net_key_refresh_phase_get(net,
Packit 34410b
							net_idx, &phase);
Packit 34410b
Packit 34410b
		if (status == MESH_STATUS_SUCCESS &&
Packit 34410b
				phase == KEY_REFRESH_PHASE_TWO)
Packit 34410b
			key_id = new_lpn_id;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	msg[n++] = ++trans_id;
Packit 34410b
	for (i = 0; i < 7; i++) {
Packit 34410b
		if (parms[i] < 0x8000 || parms[i] > 0xffff)
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		l_put_be16(parms[i], msg + n);
Packit 34410b
		n += 2;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	net_seq = mesh_net_get_seq_num(net);
Packit 34410b
	print_packet("Friend Sub Add", msg, n);
Packit 34410b
	mesh_net_transport_send(net, key_id, false,
Packit 34410b
			mesh_net_get_iv_index(net), 0,
Packit 34410b
			net_seq, 0, mesh_net_get_friend(net),
Packit 34410b
			msg, n);
Packit 34410b
	mesh_net_next_seq_num(net);
Packit 34410b
}
Packit 34410b
Packit 34410b
void frnd_sub_del(struct mesh_net *net, uint32_t parms[7])
Packit 34410b
{
Packit 34410b
	uint32_t key_id = lpn_key_id;
Packit 34410b
	uint32_t net_seq;
Packit 34410b
	uint8_t msg[15] = { NET_OP_PROXY_SUB_REMOVE };
Packit 34410b
	uint8_t i, n = 1;
Packit 34410b
Packit 34410b
	/* Check if we are in Phase 2 of Key Refresh */
Packit 34410b
	if (new_lpn_id) {
Packit 34410b
		uint8_t phase;
Packit 34410b
		uint16_t net_idx = mesh_net_get_primary_idx(net);
Packit 34410b
		uint8_t status = mesh_net_key_refresh_phase_get(net,
Packit 34410b
							net_idx, &phase);
Packit 34410b
Packit 34410b
		if (status == MESH_STATUS_SUCCESS &&
Packit 34410b
				phase == KEY_REFRESH_PHASE_TWO)
Packit 34410b
			key_id = new_lpn_id;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	msg[n++] = ++trans_id;
Packit 34410b
	for (i = 0; i < 7; i++) {
Packit 34410b
		if (parms[i] < 0x8000 || parms[i] > 0xffff)
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		l_put_be16(parms[i], msg + n);
Packit 34410b
		n += 2;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	net_seq = mesh_net_get_seq_num(net);
Packit 34410b
	print_packet("Friend Sub Del", msg, n);
Packit 34410b
	mesh_net_transport_send(net, key_id, false,
Packit 34410b
			mesh_net_get_iv_index(net), 0,
Packit 34410b
			net_seq, 0, mesh_net_get_friend(net),
Packit 34410b
			msg, n);
Packit 34410b
	mesh_net_next_seq_num(net);
Packit 34410b
}
Packit 34410b
Packit 34410b
void frnd_key_refresh(struct mesh_net *net, uint8_t phase)
Packit 34410b
{
Packit 34410b
	uint16_t net_idx = mesh_net_get_primary_idx(net);
Packit 34410b
	uint32_t key_id;
Packit 34410b
Packit 34410b
	switch (phase) {
Packit 34410b
	default:
Packit 34410b
	case 0:
Packit 34410b
	case 3:
Packit 34410b
		if (new_lpn_id) {
Packit 34410b
			l_debug("LPN Retiring KeySet %d", lpn_key_id);
Packit 34410b
			net_key_unref(lpn_key_id);
Packit 34410b
			lpn_key_id = new_lpn_id;
Packit 34410b
		}
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	case 1:
Packit 34410b
		net_key_unref(new_lpn_id);
Packit 34410b
		if (!mesh_net_get_key(net, true, net_idx, &key_id)) {
Packit 34410b
			new_lpn_id = 0;
Packit 34410b
			return;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		new_lpn_id = net_key_frnd_add(key_id, mesh_net_get_address(net),
Packit 34410b
						mesh_net_get_friend(net),
Packit 34410b
						cnt, fn_cnt);
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	case 2:
Packit 34410b
		/* Should we do anything here?  Maybe not */
Packit 34410b
		return;
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
uint32_t frnd_get_key(struct mesh_net *net)
Packit 34410b
{
Packit 34410b
	uint8_t idx = mesh_net_get_primary_idx(net);
Packit 34410b
	uint8_t phase = 0;
Packit 34410b
Packit 34410b
	mesh_net_key_refresh_phase_get(net, idx, &phase);
Packit 34410b
Packit 34410b
	if (phase == 2)
Packit 34410b
		return new_lpn_id;
Packit 34410b
	else
Packit 34410b
		return lpn_key_id;
Packit 34410b
}