Blame mesh/pb-adv.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
#include "mesh/crypto.h"
Packit 34410b
#include "mesh/net.h"
Packit 34410b
#include "mesh/mesh-io.h"
Packit 34410b
#include "mesh/mesh.h"
Packit 34410b
#include "mesh/prov.h"
Packit 34410b
#include "mesh/provision.h"
Packit 34410b
#include "mesh/pb-adv.h"
Packit 34410b
Packit 34410b
Packit 34410b
struct pb_adv_session {
Packit 34410b
	mesh_prov_open_func_t open_cb;
Packit 34410b
	mesh_prov_close_func_t close_cb;
Packit 34410b
	mesh_prov_receive_func_t rx_cb;
Packit 34410b
	mesh_prov_ack_func_t ack_cb;
Packit 34410b
	struct l_timeout *tx_timeout;
Packit 34410b
	uint32_t link_id;
Packit 34410b
	uint16_t exp_len;
Packit 34410b
	uint8_t exp_fcs;
Packit 34410b
	uint8_t exp_segs;
Packit 34410b
	uint8_t got_segs;
Packit 34410b
	uint8_t trans_num;
Packit 34410b
	uint8_t local_acked;
Packit 34410b
	uint8_t local_trans_num;
Packit 34410b
	uint8_t peer_trans_num;
Packit 34410b
	uint8_t last_peer_trans_num;
Packit 34410b
	uint8_t sar[80];
Packit 34410b
	uint8_t uuid[16];
Packit 34410b
	bool initiator;
Packit 34410b
	bool opened;
Packit 34410b
	void *user_data;
Packit 34410b
};
Packit 34410b
Packit 34410b
#define PB_ADV_ACK 0x01
Packit 34410b
#define PB_ADV_OPEN_REQ 0x03
Packit 34410b
#define PB_ADV_OPEN_CFM 0x07
Packit 34410b
#define PB_ADV_CLOSE 0x0B
Packit 34410b
Packit 34410b
#define PB_ADV_MTU	24
Packit 34410b
Packit 34410b
struct pb_ack {
Packit 34410b
	uint8_t ad_type;
Packit 34410b
	uint32_t link_id;
Packit 34410b
	uint8_t trans_num;
Packit 34410b
	uint8_t opcode;
Packit 34410b
} __packed;
Packit 34410b
Packit 34410b
struct pb_open_req{
Packit 34410b
	uint8_t ad_type;
Packit 34410b
	uint32_t link_id;
Packit 34410b
	uint8_t trans_num;
Packit 34410b
	uint8_t opcode;
Packit 34410b
	uint8_t uuid[16];
Packit 34410b
} __packed;
Packit 34410b
Packit 34410b
struct pb_open_cfm{
Packit 34410b
	uint8_t ad_type;
Packit 34410b
	uint32_t link_id;
Packit 34410b
	uint8_t trans_num;
Packit 34410b
	uint8_t opcode;
Packit 34410b
} __packed;
Packit 34410b
Packit 34410b
struct pb_close_ind {
Packit 34410b
	uint8_t ad_type;
Packit 34410b
	uint32_t link_id;
Packit 34410b
	uint8_t trans_num;
Packit 34410b
	uint8_t opcode;
Packit 34410b
	uint8_t reason;
Packit 34410b
} __packed;
Packit 34410b
Packit 34410b
static struct pb_adv_session *pb_session = NULL;
Packit 34410b
Packit 34410b
static const uint8_t filter[1] = { MESH_AD_TYPE_PROVISION };
Packit 34410b
Packit 34410b
static void send_adv_segs(struct pb_adv_session *session, const uint8_t *data,
Packit 34410b
							uint16_t size)
Packit 34410b
{
Packit 34410b
	uint16_t init_size;
Packit 34410b
	uint8_t buf[PB_ADV_MTU + 6] = { MESH_AD_TYPE_PROVISION };
Packit 34410b
	uint8_t max_seg;
Packit 34410b
	uint8_t consumed;
Packit 34410b
	int i;
Packit 34410b
Packit 34410b
	if (!size)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	mesh_send_cancel(filter, sizeof(filter));
Packit 34410b
Packit 34410b
	l_put_be32(session->link_id, buf + 1);
Packit 34410b
	buf[1 + 4] = ++session->local_trans_num;
Packit 34410b
Packit 34410b
	if (size > PB_ADV_MTU - 4) {
Packit 34410b
		max_seg = 1 +
Packit 34410b
			(((size - (PB_ADV_MTU - 4)) - 1) / (PB_ADV_MTU - 1));
Packit 34410b
		init_size = PB_ADV_MTU - 4;
Packit 34410b
	} else {
Packit 34410b
		max_seg = 0;
Packit 34410b
		init_size = size;
Packit 34410b
	}
Packit 34410b
Packit 34410b
	/* print_packet("FULL-TX", data, size); */
Packit 34410b
Packit 34410b
	l_debug("Sending %u fragments for %u octets", max_seg + 1, size);
Packit 34410b
Packit 34410b
	buf[6] = max_seg << 2;
Packit 34410b
	l_put_be16(size, buf + 7);
Packit 34410b
	buf[9] = mesh_crypto_compute_fcs(data, size);
Packit 34410b
	memcpy(buf + 10, data, init_size);
Packit 34410b
Packit 34410b
	l_debug("max_seg: %2.2x", max_seg);
Packit 34410b
	l_debug("size: %2.2x, CRC: %2.2x", size, buf[9]);
Packit 34410b
	/* print_packet("PB-TX", buf + 1, init_size + 9); */
Packit 34410b
	mesh_send_pkt(MESH_IO_TX_COUNT_UNLIMITED, 200, buf, init_size + 10);
Packit 34410b
Packit 34410b
	consumed = init_size;
Packit 34410b
Packit 34410b
	for (i = 1; i <= max_seg; i++) {
Packit 34410b
		uint8_t seg_size; /* Amount of payload data being sent */
Packit 34410b
Packit 34410b
		if (size - consumed > PB_ADV_MTU - 1)
Packit 34410b
			seg_size = PB_ADV_MTU - 1;
Packit 34410b
		else
Packit 34410b
			seg_size = size - consumed;
Packit 34410b
Packit 34410b
		buf[6] = (i << 2) | 0x02;
Packit 34410b
		memcpy(buf + 7, data + consumed, seg_size);
Packit 34410b
Packit 34410b
		/* print_packet("PB-TX", buf + 1, seg_size + 6); */
Packit 34410b
Packit 34410b
		mesh_send_pkt(MESH_IO_TX_COUNT_UNLIMITED, 200,
Packit 34410b
							buf, seg_size + 7);
Packit 34410b
Packit 34410b
		consumed += seg_size;
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
static void tx_timeout(struct l_timeout *timeout, void *user_data)
Packit 34410b
{
Packit 34410b
	struct pb_adv_session *session = user_data;
Packit 34410b
	mesh_prov_close_func_t cb;
Packit 34410b
Packit 34410b
	if (!session || pb_session != session)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	l_timeout_remove(session->tx_timeout);
Packit 34410b
	session->tx_timeout = NULL;
Packit 34410b
Packit 34410b
	mesh_send_cancel(filter, sizeof(filter));
Packit 34410b
Packit 34410b
	l_info("TX timeout");
Packit 34410b
	cb = pb_session->close_cb;
Packit 34410b
	user_data = pb_session->user_data;
Packit 34410b
	l_free(pb_session);
Packit 34410b
	pb_session = NULL;
Packit 34410b
	cb(user_data, 1);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void pb_adv_tx(void *user_data, void *data, uint16_t len)
Packit 34410b
{
Packit 34410b
	struct pb_adv_session *session = user_data;
Packit 34410b
Packit 34410b
	if (!session || pb_session != session)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	l_timeout_remove(session->tx_timeout);
Packit 34410b
	session->tx_timeout = l_timeout_create(30, tx_timeout, session, NULL);
Packit 34410b
Packit 34410b
	send_adv_segs(session, data, len);
Packit 34410b
}
Packit 34410b
Packit 34410b
static void send_open_req(struct pb_adv_session *session)
Packit 34410b
{
Packit 34410b
	struct pb_open_req open_req = { MESH_AD_TYPE_PROVISION };
Packit 34410b
Packit 34410b
	l_put_be32(session->link_id, &open_req.link_id);
Packit 34410b
	open_req.trans_num = 0;
Packit 34410b
	open_req.opcode = PB_ADV_OPEN_REQ;
Packit 34410b
	memcpy(open_req.uuid, session->uuid, 16);
Packit 34410b
Packit 34410b
	mesh_send_cancel(filter, sizeof(filter));
Packit 34410b
	mesh_send_pkt(MESH_IO_TX_COUNT_UNLIMITED, 500, &open_req,
Packit 34410b
							sizeof(open_req));
Packit 34410b
}
Packit 34410b
Packit 34410b
static void send_open_cfm(struct pb_adv_session *session)
Packit 34410b
{
Packit 34410b
	struct pb_open_cfm open_cfm = { MESH_AD_TYPE_PROVISION };
Packit 34410b
Packit 34410b
	l_put_be32(session->link_id, &open_cfm.link_id);
Packit 34410b
	open_cfm.trans_num = 0;
Packit 34410b
	open_cfm.opcode = PB_ADV_OPEN_CFM;
Packit 34410b
Packit 34410b
	mesh_send_cancel(filter, sizeof(filter));
Packit 34410b
	mesh_send_pkt(MESH_IO_TX_COUNT_UNLIMITED, 500, &open_cfm,
Packit 34410b
							sizeof(open_cfm));
Packit 34410b
}
Packit 34410b
Packit 34410b
static void send_ack(struct pb_adv_session *session, uint8_t trans_num)
Packit 34410b
{
Packit 34410b
	struct pb_ack ack = { MESH_AD_TYPE_PROVISION };
Packit 34410b
Packit 34410b
	l_put_be32(session->link_id, &ack.link_id);
Packit 34410b
	ack.trans_num = trans_num;
Packit 34410b
	ack.opcode = PB_ADV_ACK;
Packit 34410b
Packit 34410b
	mesh_send_pkt(1, 100, &ack, sizeof(ack));
Packit 34410b
}
Packit 34410b
Packit 34410b
static void send_close_ind(struct pb_adv_session *session, uint8_t reason)
Packit 34410b
{
Packit 34410b
	struct pb_close_ind close_ind = { MESH_AD_TYPE_PROVISION };
Packit 34410b
Packit 34410b
	if (!pb_session || pb_session != session)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	l_put_be32(session->link_id, &close_ind.link_id);
Packit 34410b
	close_ind.trans_num = 0;
Packit 34410b
	close_ind.opcode = PB_ADV_CLOSE;
Packit 34410b
	close_ind.reason = reason;
Packit 34410b
Packit 34410b
	mesh_send_cancel(filter, sizeof(filter));
Packit 34410b
	mesh_send_pkt(10, 100, &close_ind, sizeof(close_ind));
Packit 34410b
}
Packit 34410b
Packit 34410b
static void pb_adv_packet(void *user_data, const uint8_t *pkt, uint16_t len)
Packit 34410b
{
Packit 34410b
	struct pb_adv_session *session = user_data;
Packit 34410b
	uint32_t link_id;
Packit 34410b
	size_t offset;
Packit 34410b
	uint8_t trans_num;
Packit 34410b
	uint8_t type;
Packit 34410b
	bool first;
Packit 34410b
Packit 34410b
	if (!session || pb_session != session)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	link_id = l_get_be32(pkt + 1);
Packit 34410b
	type = l_get_u8(pkt + 6);
Packit 34410b
Packit 34410b
	/* Validate new or existing Connection ID */
Packit 34410b
	if (session->link_id) {
Packit 34410b
		if (session->link_id != link_id)
Packit 34410b
			return;
Packit 34410b
	} else if (type != 0x03)
Packit 34410b
		return;
Packit 34410b
	else if (!link_id)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	trans_num = l_get_u8(pkt + 5);
Packit 34410b
	pkt += 7;
Packit 34410b
	len -= 7;
Packit 34410b
Packit 34410b
	switch (type) {
Packit 34410b
	case PB_ADV_OPEN_CFM:
Packit 34410b
		/*
Packit 34410b
		 * Ignore if:
Packit 34410b
		 * 1. We are acceptor
Packit 34410b
		 * 2. We are already provisioning on different link_id
Packit 34410b
		 */
Packit 34410b
Packit 34410b
		if (!session->initiator)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		first = !session->opened;
Packit 34410b
		session->opened = true;
Packit 34410b
Packit 34410b
		/* Only call Open callback once */
Packit 34410b
		if (first) {
Packit 34410b
			l_debug("PB-ADV open confirmed");
Packit 34410b
			session->open_cb(session->user_data, pb_adv_tx,
Packit 34410b
							session, PB_ADV);
Packit 34410b
		}
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	case PB_ADV_OPEN_REQ:
Packit 34410b
		/*
Packit 34410b
		 * Ignore if:
Packit 34410b
		 * 1. We are initiator
Packit 34410b
		 * 2. Open request not addressed to us
Packit 34410b
		 * 3. We are already provisioning on different link_id
Packit 34410b
		 */
Packit 34410b
Packit 34410b
		if (session->initiator)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		if (memcmp(pkt, session->uuid, 16))
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		first = !session->link_id;
Packit 34410b
		session->link_id = link_id;
Packit 34410b
		session->last_peer_trans_num = 0xFF;
Packit 34410b
		session->local_acked = 0xFF;
Packit 34410b
		session->peer_trans_num = 0x00;
Packit 34410b
		session->local_trans_num = 0x7F;
Packit 34410b
		session->opened = true;
Packit 34410b
Packit 34410b
		/* Only call Open callback once */
Packit 34410b
		if (first) {
Packit 34410b
			l_debug("PB-ADV open requested");
Packit 34410b
			session->open_cb(session->user_data, pb_adv_tx,
Packit 34410b
							session, PB_ADV);
Packit 34410b
		}
Packit 34410b
Packit 34410b
		/* Send CFM once per received request */
Packit 34410b
		send_open_cfm(session);
Packit 34410b
		break;
Packit 34410b
Packit 34410b
	case PB_ADV_CLOSE:
Packit 34410b
		l_timeout_remove(session->tx_timeout);
Packit 34410b
		l_debug("Link closed notification: %2.2x", pkt[0]);
Packit 34410b
		/* Wrap callback for pre-cleaning */
Packit 34410b
		if (true) {
Packit 34410b
			mesh_prov_close_func_t cb = session->close_cb;
Packit 34410b
			void *user_data = session->user_data;
Packit 34410b
Packit 34410b
			l_free(session);
Packit 34410b
			pb_session = NULL;
Packit 34410b
			cb(user_data, pkt[0]);
Packit 34410b
		}
Packit 34410b
		break;
Packit 34410b
Packit 34410b
	case PB_ADV_ACK:
Packit 34410b
		if (!session->opened)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		if (trans_num != session->local_trans_num)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		if (session->local_acked > trans_num)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		mesh_send_cancel(filter, sizeof(filter));
Packit 34410b
		session->local_acked = trans_num;
Packit 34410b
		session->ack_cb(session->user_data, trans_num);
Packit 34410b
		break;
Packit 34410b
Packit 34410b
	default: /* DATA SEGMENT */
Packit 34410b
		if (!session->opened)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		if (trans_num == session->last_peer_trans_num) {
Packit 34410b
			send_ack(session, trans_num);
Packit 34410b
			return;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		switch(type & 0x03) {
Packit 34410b
		case 0x00:
Packit 34410b
			session->peer_trans_num = trans_num;
Packit 34410b
			session->exp_len = l_get_be16(pkt);
Packit 34410b
Packit 34410b
			l_debug("PB-ADV start with %u fragments, %d octets",
Packit 34410b
						type >> 2, session->exp_len);
Packit 34410b
Packit 34410b
			if (session->exp_len > sizeof(session->sar)) {
Packit 34410b
				l_debug("Incoming length exceeded: %d",
Packit 34410b
							session->exp_len);
Packit 34410b
				return;
Packit 34410b
			}
Packit 34410b
Packit 34410b
			session->exp_fcs = l_get_u8(pkt + 2);
Packit 34410b
			session->exp_segs = 0xff >> (7 - (type >> 2));
Packit 34410b
Packit 34410b
			/* Save first segment */
Packit 34410b
			memcpy(session->sar, pkt + 3, len - 3);
Packit 34410b
			session->got_segs |= 1;
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		case 0x02:
Packit 34410b
			session->peer_trans_num = trans_num;
Packit 34410b
			offset = 20 + (((type >> 2) - 1) * 23);
Packit 34410b
Packit 34410b
			if (offset + len - 3 > sizeof(session->sar)) {
Packit 34410b
				l_debug("Length exceeded: %d",
Packit 34410b
							session->exp_len);
Packit 34410b
				return;
Packit 34410b
			}
Packit 34410b
Packit 34410b
			l_debug("Processing fragment %u", type >> 2);
Packit 34410b
			memcpy(session->sar + offset, pkt, len);
Packit 34410b
			session->got_segs |= 1 << (type >> 2);
Packit 34410b
			break;
Packit 34410b
Packit 34410b
		default:
Packit 34410b
			/* Malformed or unrecognized */
Packit 34410b
			return;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		if (session->got_segs != session->exp_segs)
Packit 34410b
			return;
Packit 34410b
Packit 34410b
		/* Validate RXed packet and pass up to Provisioning */
Packit 34410b
		if (!mesh_crypto_check_fcs(session->sar,
Packit 34410b
					session->exp_len,
Packit 34410b
					session->exp_fcs)) {
Packit 34410b
Packit 34410b
			/* This can be a false negative if first
Packit 34410b
			 * segment missed, and can almost always
Packit 34410b
			 * be ignored.
Packit 34410b
			 */
Packit 34410b
Packit 34410b
			l_debug("Invalid FCS");
Packit 34410b
			return;
Packit 34410b
		}
Packit 34410b
Packit 34410b
		if (session->last_peer_trans_num != session->peer_trans_num) {
Packit 34410b
			session->got_segs = 0;
Packit 34410b
			session->rx_cb(session->user_data, session->sar,
Packit 34410b
							session->exp_len);
Packit 34410b
		}
Packit 34410b
Packit 34410b
		session->last_peer_trans_num = session->peer_trans_num;
Packit 34410b
		send_ack(session, session->last_peer_trans_num);
Packit 34410b
	}
Packit 34410b
}
Packit 34410b
Packit 34410b
bool pb_adv_reg(bool initiator, mesh_prov_open_func_t open_cb,
Packit 34410b
		mesh_prov_close_func_t close_cb,
Packit 34410b
		mesh_prov_receive_func_t rx_cb, mesh_prov_ack_func_t ack_cb,
Packit 34410b
		uint8_t uuid[16], void *user_data)
Packit 34410b
{
Packit 34410b
	if (pb_session)
Packit 34410b
		return false;
Packit 34410b
Packit 34410b
	pb_session = l_new(struct pb_adv_session, 1);
Packit 34410b
	pb_session->open_cb = open_cb;
Packit 34410b
	pb_session->close_cb = close_cb;
Packit 34410b
	pb_session->rx_cb = rx_cb;
Packit 34410b
	pb_session->ack_cb = ack_cb;
Packit 34410b
	pb_session->user_data = user_data;
Packit 34410b
	pb_session->initiator = initiator;
Packit 34410b
	memcpy(pb_session->uuid, uuid, 16);
Packit 34410b
Packit 34410b
	mesh_reg_prov_rx(pb_adv_packet, pb_session);
Packit 34410b
Packit 34410b
	if (initiator) {
Packit 34410b
		l_getrandom(&pb_session->link_id, sizeof(pb_session->link_id));
Packit 34410b
		send_open_req(pb_session);
Packit 34410b
	}
Packit 34410b
Packit 34410b
	return true;
Packit 34410b
}
Packit 34410b
Packit 34410b
void pb_adv_unreg(void *user_data)
Packit 34410b
{
Packit 34410b
	if (!pb_session || pb_session->user_data != user_data)
Packit 34410b
		return;
Packit 34410b
Packit 34410b
	l_timeout_remove(pb_session->tx_timeout);
Packit 34410b
	send_close_ind(pb_session, 0);
Packit 34410b
	l_free(pb_session);
Packit 34410b
	pb_session = NULL;
Packit 34410b
}