diff options
| author | Maksim Yevmenkin <emax@FreeBSD.org> | 2009-01-30 22:23:21 +0000 | 
|---|---|---|
| committer | Maksim Yevmenkin <emax@FreeBSD.org> | 2009-01-30 22:23:21 +0000 | 
| commit | 7718ced0ea98ea5d1ece76c36a955eec9d97dc52 (patch) | |
| tree | 4fa9389ed8e1b66c4642e880ab1430668fa57085 | |
| parent | 46f09b77126d023c40a0579f5d6d00e1ed21ac87 (diff) | |
Notes
| -rw-r--r-- | usr.sbin/bluetooth/btpand/Makefile | 13 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/bnep.c | 755 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/bnep.h | 72 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/btpand.8 | 241 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/btpand.c | 290 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/btpand.h | 208 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/channel.c | 335 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/client.c | 192 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/event.c | 309 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/event.h | 147 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/packet.c | 110 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/sdp.c | 209 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/sdp.h | 38 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/server.c | 286 | ||||
| -rw-r--r-- | usr.sbin/bluetooth/btpand/tap.c | 167 | 
15 files changed, 3372 insertions, 0 deletions
| diff --git a/usr.sbin/bluetooth/btpand/Makefile b/usr.sbin/bluetooth/btpand/Makefile new file mode 100644 index 0000000000000..5e4bb0b71674d --- /dev/null +++ b/usr.sbin/bluetooth/btpand/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.2 2008/08/18 08:25:32 plunky Exp $ +# $FreeBSD$ + +PROG=	btpand +MAN=	btpand.8 +SRCS=	btpand.c bnep.c channel.c client.c event.c packet.c server.c sdp.c tap.c + +WARNS?=	3 + +DPADD+=	${LIBBLUETOOTH} ${LIBSDP} ${LIBUTIL} +LDADD+=	-lbluetooth -lsdp -lutil + +.include <bsd.prog.mk> diff --git a/usr.sbin/bluetooth/btpand/bnep.c b/usr.sbin/bluetooth/btpand/bnep.c new file mode 100644 index 0000000000000..200a723011835 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/bnep.c @@ -0,0 +1,755 @@ +/*	$NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); + +#include <sys/uio.h> +#include <bluetooth.h> +#include <sdp.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> + +#include "btpand.h" +#include "bnep.h" + +static bool bnep_recv_extension(packet_t *); +static size_t bnep_recv_control(channel_t *, uint8_t *, size_t, bool); +static size_t bnep_recv_control_command_not_understood(channel_t *, uint8_t *, size_t); +static size_t bnep_recv_setup_connection_req(channel_t *, uint8_t *, size_t); +static size_t bnep_recv_setup_connection_rsp(channel_t *, uint8_t *, size_t); +static size_t bnep_recv_filter_net_type_set(channel_t *, uint8_t *, size_t); +static size_t bnep_recv_filter_net_type_rsp(channel_t *, uint8_t *, size_t); +static size_t bnep_recv_filter_multi_addr_set(channel_t *, uint8_t *, size_t); +static size_t bnep_recv_filter_multi_addr_rsp(channel_t *, uint8_t *, size_t); + +static bool bnep_pfilter(channel_t *, packet_t *); +static bool bnep_mfilter(channel_t *, packet_t *); + +static uint8_t NAP_UUID[] = { +	0x00, 0x00, 0x11, 0x16, +	0x00, 0x00, +	0x10, 0x00, +	0x80, 0x00, +	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb +}; + +static uint8_t GN_UUID[] = { +	0x00, 0x00, 0x11, 0x17, +	0x00, 0x00, +	0x10, 0x00, +	0x80, 0x00, +	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb, +}; + +static uint8_t PANU_UUID[] = { +	0x00, 0x00, 0x11, 0x15, +	0x00, 0x00, +	0x10, 0x00, +	0x80, 0x00, +	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb +}; + +/* + * receive BNEP packet + * return true if packet is to be forwarded + */ +bool +bnep_recv(packet_t *pkt) +{ +	size_t len; +	uint8_t type; + +	if (pkt->len < 1) +		return false; + +	type = pkt->ptr[0]; +	packet_adj(pkt, 1); + +	switch (BNEP_TYPE(type)) { +	case BNEP_GENERAL_ETHERNET: +		if (pkt->len < (ETHER_ADDR_LEN * 2) + ETHER_TYPE_LEN) { +			log_debug("dropped short packet (type 0x%2.2x)", type); +			return false; +		} + +		pkt->dst = pkt->ptr; +		packet_adj(pkt, ETHER_ADDR_LEN); +		pkt->src = pkt->ptr; +		packet_adj(pkt, ETHER_ADDR_LEN); +		pkt->type = pkt->ptr; +		packet_adj(pkt, ETHER_TYPE_LEN); +		break; + +	case BNEP_CONTROL: +		len = bnep_recv_control(pkt->chan, pkt->ptr, pkt->len, false); +		if (len == 0) +			return false; + +		packet_adj(pkt, len); +		break; + +	case BNEP_COMPRESSED_ETHERNET: +		if (pkt->len < ETHER_TYPE_LEN) { +			log_debug("dropped short packet (type 0x%2.2x)", type); +			return false; +		} + +		pkt->dst = pkt->chan->laddr; +		pkt->src = pkt->chan->raddr; +		pkt->type = pkt->ptr; +		packet_adj(pkt, ETHER_TYPE_LEN); +		break; + +	case BNEP_COMPRESSED_ETHERNET_SRC_ONLY: +		if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) { +			log_debug("dropped short packet (type 0x%2.2x)", type); +			return false; +		} + +		pkt->dst = pkt->chan->laddr; +		pkt->src = pkt->ptr; +		packet_adj(pkt, ETHER_ADDR_LEN); +		pkt->type = pkt->ptr; +		packet_adj(pkt, ETHER_TYPE_LEN); +		break; + +	case BNEP_COMPRESSED_ETHERNET_DST_ONLY: +		if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) { +			log_debug("dropped short packet (type 0x%2.2x)", type); +			return false; +		} + +		pkt->dst = pkt->ptr; +		packet_adj(pkt, ETHER_ADDR_LEN); +		pkt->src = pkt->chan->raddr; +		pkt->type = pkt->ptr; +		packet_adj(pkt, ETHER_TYPE_LEN); +		break; + +	default: +		/* +		 * Any packet containing a reserved BNEP +		 * header packet type SHALL be dropped. +		 */ + +		log_debug("dropped packet with reserved type 0x%2.2x", type); +		return false; +	} + +	if (BNEP_TYPE_EXT(type) +	    && !bnep_recv_extension(pkt)) +		return false;	/* invalid extensions */ + +	if (BNEP_TYPE(type) == BNEP_CONTROL +	    || pkt->chan->state != CHANNEL_OPEN) +		return false;	/* no forwarding */ + +	return true; +} + +static bool +bnep_recv_extension(packet_t *pkt) +{ +	exthdr_t *eh; +	size_t len, size; +	uint8_t type; + +	do { +		if (pkt->len < 2) +			return false; + +		type = pkt->ptr[0]; +		size = pkt->ptr[1]; + +		if (pkt->len < size + 2) +			return false; + +		switch (type) { +		case BNEP_EXTENSION_CONTROL: +			len = bnep_recv_control(pkt->chan, pkt->ptr + 2, size, true); +			if (len != size) +				log_err("ignored spurious data in exthdr"); + +			break; + +		default: +			/* Unknown extension headers in data packets	 */ +			/* SHALL be forwarded irrespective of any	 */ +			/* network protocol or multicast filter settings */ +			/* and any local filtering policy.		 */ + +			eh = malloc(sizeof(exthdr_t)); +			if (eh == NULL) { +				log_err("exthdr malloc() failed: %m"); +				break; +			} + +			eh->ptr = pkt->ptr; +			eh->len = size; +			STAILQ_INSERT_TAIL(&pkt->extlist, eh, next); +			break; +		} + +		packet_adj(pkt, size + 2); +	} while (BNEP_TYPE_EXT(type)); + +	return true; +} + +static size_t +bnep_recv_control(channel_t *chan, uint8_t *ptr, size_t size, bool isext) +{ +	uint8_t type; +	size_t len; + +	if (size-- < 1) +		return 0; + +	type = *ptr++; + +	switch (type) { +	case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD: +		len = bnep_recv_control_command_not_understood(chan, ptr, size); +		break; + +	case BNEP_SETUP_CONNECTION_REQUEST: +		if (isext) +			return 0;	/* not allowed in extension headers */ + +		len = bnep_recv_setup_connection_req(chan, ptr, size); +		break; + +	case BNEP_SETUP_CONNECTION_RESPONSE: +		if (isext) +			return 0;	/* not allowed in extension headers */ + +		len = bnep_recv_setup_connection_rsp(chan, ptr, size); +		break; + +	case BNEP_FILTER_NET_TYPE_SET: +		len = bnep_recv_filter_net_type_set(chan, ptr, size); +		break; + +	case BNEP_FILTER_NET_TYPE_RESPONSE: +		len = bnep_recv_filter_net_type_rsp(chan, ptr, size); +		break; + +	case BNEP_FILTER_MULTI_ADDR_SET: +		len = bnep_recv_filter_multi_addr_set(chan, ptr, size); +		break; + +	case BNEP_FILTER_MULTI_ADDR_RESPONSE: +		len = bnep_recv_filter_multi_addr_rsp(chan, ptr, size); +		break; + +	default: +		len = 0; +		break; +	} + +	if (len == 0) +		bnep_send_control(chan, BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD, type); + +	return len; +} + +static size_t +bnep_recv_control_command_not_understood(channel_t *chan, uint8_t *ptr, size_t size) +{ +	uint8_t type; + +	if (size < 1) +		return 0; + +	type = *ptr++; +	log_err("received Control Command Not Understood (0x%2.2x)", type); + +	/* we didn't send any reserved commands, just cut them off */ +	channel_close(chan); + +	return 1; +} + +static size_t +bnep_recv_setup_connection_req(channel_t *chan, uint8_t *ptr, size_t size) +{ +	uint8_t off; +	int src, dst, rsp; +	size_t len; + +	if (size < 1) +		return 0; + +	len = *ptr++; +	if (size < (len * 2 + 1)) +		return 0; + +	if (chan->state != CHANNEL_WAIT_CONNECT_REQ +	    && chan->state != CHANNEL_OPEN) { +		log_debug("ignored"); +		return (len * 2 + 1); +	} + +	if (len == 2) +		off = 2; +	else if (len == 4) +		off = 0; +	else if (len == 16) +		off = 0; +	else { +		rsp = BNEP_SETUP_INVALID_UUID_SIZE; +		goto done; +	} + +	if (memcmp(ptr, NAP_UUID + off, len) == 0) +		dst = SDP_SERVICE_CLASS_NAP; +	else if (memcmp(ptr, GN_UUID + off, len) == 0) +		dst = SDP_SERVICE_CLASS_GN; +	else if (memcmp(ptr, PANU_UUID + off, len) == 0) +		dst = SDP_SERVICE_CLASS_PANU; +	else +		dst = 0; + +	if (dst != service_class) { +		rsp = BNEP_SETUP_INVALID_DST_UUID; +		goto done; +	} + +	ptr += len; + +	if (memcmp(ptr, NAP_UUID + off, len) == 0) +		src = SDP_SERVICE_CLASS_NAP; +	else if (memcmp(ptr, GN_UUID + off, len) == 0) +		src = SDP_SERVICE_CLASS_GN; +	else if (memcmp(ptr, PANU_UUID + off, len) == 0) +		src = SDP_SERVICE_CLASS_PANU; +	else +		src = 0; + +	if ((dst != SDP_SERVICE_CLASS_PANU && src != SDP_SERVICE_CLASS_PANU) +	    || src == 0) { +		rsp = BNEP_SETUP_INVALID_SRC_UUID; +		goto done; +	} + +	rsp = BNEP_SETUP_SUCCESS; +	chan->state = CHANNEL_OPEN; +	channel_timeout(chan, 0); + +done: +	log_debug("addr %s response 0x%2.2x", +	    ether_ntoa((struct ether_addr *)chan->raddr), rsp); + +	bnep_send_control(chan, BNEP_SETUP_CONNECTION_RESPONSE, rsp); +	return (len * 2 + 1); +} + +static size_t +bnep_recv_setup_connection_rsp(channel_t *chan, uint8_t *ptr, size_t size) +{ +	int rsp; + +	if (size < 2) +		return 0; + +	rsp = be16dec(ptr); + +	if (chan->state != CHANNEL_WAIT_CONNECT_RSP) { +		log_debug("ignored"); +		return 2; +	} + +	log_debug("addr %s response 0x%2.2x", +	    ether_ntoa((struct ether_addr *)chan->raddr), rsp); + +	if (rsp == BNEP_SETUP_SUCCESS) { +		chan->state = CHANNEL_OPEN; +		channel_timeout(chan, 0); +	} else { +		channel_close(chan); +	} + +	return 2; +} + +static size_t +bnep_recv_filter_net_type_set(channel_t *chan, uint8_t *ptr, size_t size) +{ +	pfilter_t *pf; +	int i, nf, rsp; +	size_t len; + +	if (size < 2) +		return 0; + +	len = be16dec(ptr); +	ptr += 2; + +	if (size < (len + 2)) +		return 0; + +	if (chan->state != CHANNEL_OPEN) { +		log_debug("ignored"); +		return (len + 2); +	} + +	nf = len / 4; +	pf = malloc(nf * sizeof(pfilter_t)); +	if (pf == NULL) { +		rsp = BNEP_FILTER_TOO_MANY_FILTERS; +		goto done; +	} + +	log_debug("nf = %d", nf); + +	for (i = 0; i < nf; i++) { +		pf[i].start = be16dec(ptr); +		ptr += 2; +		pf[i].end = be16dec(ptr); +		ptr += 2; + +		if (pf[i].start > pf[i].end) { +			free(pf); +			rsp = BNEP_FILTER_INVALID_RANGE; +			goto done; +		} + +		log_debug("pf[%d] = %#4.4x, %#4.4x", i, pf[i].start, pf[i].end); +	} + +	if (chan->pfilter) +		free(chan->pfilter); + +	chan->pfilter = pf; +	chan->npfilter = nf; + +	rsp = BNEP_FILTER_SUCCESS; + +done: +	log_debug("addr %s response 0x%2.2x", +	    ether_ntoa((struct ether_addr *)chan->raddr), rsp); + +	bnep_send_control(chan, BNEP_FILTER_NET_TYPE_RESPONSE, rsp); +	return (len + 2); +} + +static size_t +bnep_recv_filter_net_type_rsp(channel_t *chan, uint8_t *ptr, size_t size) +{ +	int rsp; + +	if (size < 2) +		return 0; + +	if (chan->state != CHANNEL_OPEN) { +		log_debug("ignored"); +		return 2; +	} + +	rsp = be16dec(ptr); + +	log_debug("addr %s response 0x%2.2x", +	    ether_ntoa((struct ether_addr *)chan->raddr), rsp); + +	/* we did not send any filter_net_type_set message */ +	return 2; +} + +static size_t +bnep_recv_filter_multi_addr_set(channel_t *chan, uint8_t *ptr, size_t size) +{ +	mfilter_t *mf; +	int i, nf, rsp; +	size_t len; + +	if (size < 2) +		return 0; + +	len = be16dec(ptr); +	ptr += 2; + +	if (size < (len + 2)) +		return 0; + +	if (chan->state != CHANNEL_OPEN) { +		log_debug("ignored"); +		return (len + 2); +	} + +	nf = len / (ETHER_ADDR_LEN * 2); +	mf = malloc(nf * sizeof(mfilter_t)); +	if (mf == NULL) { +		rsp = BNEP_FILTER_TOO_MANY_FILTERS; +		goto done; +	} + +	log_debug("nf = %d", nf); + +	for (i = 0; i < nf; i++) { +		memcpy(mf[i].start, ptr, ETHER_ADDR_LEN); +		ptr += ETHER_ADDR_LEN; + +		memcpy(mf[i].end, ptr, ETHER_ADDR_LEN); +		ptr += ETHER_ADDR_LEN; + +		if (memcmp(mf[i].start, mf[i].end, ETHER_ADDR_LEN) > 0) { +			free(mf); +			rsp = BNEP_FILTER_INVALID_RANGE; +			goto done; +		} + +		log_debug("pf[%d] = " +		    "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, " +		    "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", i, +		    mf[i].start[0], mf[i].start[1], mf[i].start[2], +		    mf[i].start[3], mf[i].start[4], mf[i].start[5], +		    mf[i].end[0], mf[i].end[1], mf[i].end[2], +		    mf[i].end[3], mf[i].end[4], mf[i].end[5]); +	} + +	if (chan->mfilter) +		free(chan->mfilter); + +	chan->mfilter = mf; +	chan->nmfilter = nf; + +	rsp = BNEP_FILTER_SUCCESS; + +done: +	log_debug("addr %s response 0x%2.2x", +	    ether_ntoa((struct ether_addr *)chan->raddr), rsp); + +	bnep_send_control(chan, BNEP_FILTER_MULTI_ADDR_RESPONSE, rsp); +	return (len + 2); +} + +static size_t +bnep_recv_filter_multi_addr_rsp(channel_t *chan, uint8_t *ptr, size_t size) +{ +	int rsp; + +	if (size < 2) +		return false; + +	if (chan->state != CHANNEL_OPEN) { +		log_debug("ignored"); +		return 2; +	} + +	rsp = be16dec(ptr); +	log_debug("addr %s response 0x%2.2x", +	    ether_ntoa((struct ether_addr *)chan->raddr), rsp); + +	/* we did not send any filter_multi_addr_set message */ +	return 2; +} + +void +bnep_send_control(channel_t *chan, uint8_t type, ...) +{ +	packet_t *pkt; +	uint8_t *p; +	va_list ap; + +	assert(chan->state != CHANNEL_CLOSED); + +	pkt = packet_alloc(chan); +	if (pkt == NULL) +		return; + +	p = pkt->ptr; +	va_start(ap, type); + +	*p++ = BNEP_CONTROL; +	*p++ = type; + +	switch(type) { +	case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD: +		*p++ = va_arg(ap, int); +		break; + +	case BNEP_SETUP_CONNECTION_REQUEST: +		*p++ = va_arg(ap, int); +		be16enc(p, va_arg(ap, int)); +		p += 2; +		be16enc(p, va_arg(ap, int)); +		p += 2; +		break; + +	case BNEP_SETUP_CONNECTION_RESPONSE: +	case BNEP_FILTER_NET_TYPE_RESPONSE: +	case BNEP_FILTER_MULTI_ADDR_RESPONSE: +		be16enc(p, va_arg(ap, int)); +		p += 2; +		break; + +	case BNEP_FILTER_NET_TYPE_SET:		/* TODO */ +	case BNEP_FILTER_MULTI_ADDR_SET:	/* TODO */ +	default: +		log_err("Can't send control type 0x%2.2x", type); +		break; +	} + +	va_end(ap); +	pkt->len = p - pkt->ptr; + +	channel_put(chan, pkt); +	packet_free(pkt); +} + +/* + * BNEP send packet routine + * return true if packet can be removed from queue + */ +bool +bnep_send(channel_t *chan, packet_t *pkt) +{ +	struct iovec iov[2]; +	uint8_t *p, *type, *proto; +	exthdr_t *eh; +	bool src, dst; +	size_t nw; + +	if (pkt->type == NULL) { +		iov[0].iov_base = pkt->ptr; +		iov[0].iov_len = pkt->len; +		iov[1].iov_base = NULL; +		iov[1].iov_len = 0; +	} else { +		p = chan->sendbuf; + +		dst = (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) != 0); +		src = (memcmp(pkt->src, chan->laddr, ETHER_ADDR_LEN) != 0); + +		type = p; +		p += 1; + +		if (dst && src) +			*type = BNEP_GENERAL_ETHERNET; +		else if (dst && !src) +			*type = BNEP_COMPRESSED_ETHERNET_DST_ONLY; +		else if (!dst && src) +			*type = BNEP_COMPRESSED_ETHERNET_SRC_ONLY; +		else /* (!dst && !src) */ +			*type = BNEP_COMPRESSED_ETHERNET; + +		if (dst) { +			memcpy(p, pkt->dst, ETHER_ADDR_LEN); +			p += ETHER_ADDR_LEN; +		} + +		if (src) { +			memcpy(p, pkt->src, ETHER_ADDR_LEN); +			p += ETHER_ADDR_LEN; +		} + +		proto = p; +		memcpy(p, pkt->type, ETHER_TYPE_LEN); +		p += ETHER_TYPE_LEN; + +		STAILQ_FOREACH(eh, &pkt->extlist, next) { +			if (p + eh->len > chan->sendbuf + chan->mtu) +				break; + +			*type |= BNEP_EXT; +			type = p; + +			memcpy(p, eh->ptr, eh->len); +			p += eh->len; +		} + +		*type &= ~BNEP_EXT; + +		iov[0].iov_base = chan->sendbuf; +		iov[0].iov_len = (p - chan->sendbuf); + +		if ((chan->npfilter == 0 || bnep_pfilter(chan, pkt)) +		    && (chan->nmfilter == 0 || bnep_mfilter(chan, pkt))) { +			iov[1].iov_base = pkt->ptr; +			iov[1].iov_len = pkt->len; +		} else if (be16dec(proto) == ETHERTYPE_VLAN +		    && pkt->len >= ETHER_VLAN_ENCAP_LEN) { +			iov[1].iov_base = pkt->ptr; +			iov[1].iov_len = ETHER_VLAN_ENCAP_LEN; +		} else { +			iov[1].iov_base = NULL; +			iov[1].iov_len = 0; +			memset(proto, 0, ETHER_TYPE_LEN); +		} +	} + +	if (iov[0].iov_len + iov[1].iov_len > chan->mtu) { +		log_err("packet exceeded MTU (dropped)"); +		return false; +	} + +	nw = writev(chan->fd, iov, __arraycount(iov)); +	return (nw > 0); +} + +static bool +bnep_pfilter(channel_t *chan, packet_t *pkt) +{ +	int proto, i; + +	proto = be16dec(pkt->type); +	if (proto == ETHERTYPE_VLAN) {	/* IEEE 802.1Q tag header */ +		if (pkt->len < 4) +			return false; + +		proto = be16dec(pkt->ptr + 2); +	} + +	for (i = 0; i < chan->npfilter; i++) { +		if (chan->pfilter[i].start <= proto +		    && chan->pfilter[i].end >=proto) +			return true; +	} + +	return false; +} + +static bool +bnep_mfilter(channel_t *chan, packet_t *pkt) +{ +	int i; + +	if (!ETHER_IS_MULTICAST(pkt->dst)) +		return true; + +	for (i = 0; i < chan->nmfilter; i++) { +		if (memcmp(pkt->dst, chan->mfilter[i].start, ETHER_ADDR_LEN) >= 0 +		    && memcmp(pkt->dst, chan->mfilter[i].end, ETHER_ADDR_LEN) <= 0) +			return true; +	} + +	return false; +} diff --git a/usr.sbin/bluetooth/btpand/bnep.h b/usr.sbin/bluetooth/btpand/bnep.h new file mode 100644 index 0000000000000..2fc6a77ac62dc --- /dev/null +++ b/usr.sbin/bluetooth/btpand/bnep.h @@ -0,0 +1,72 @@ +/*	$NetBSD: bnep.h,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +/* + * Constants defined in the Bluetooth Network Encapsulation + * Protocol (BNEP) specification v1.0 + */ + +#define	BNEP_MTU_MIN		1691 + +#define BNEP_EXT		0x80 +#define	BNEP_TYPE(x)		((x) & 0x7f) +#define BNEP_TYPE_EXT(x)	(((x) & BNEP_EXT) == BNEP_EXT) + +/* BNEP packet types */ +#define	BNEP_GENERAL_ETHERNET			0x00 +#define	BNEP_CONTROL				0x01 +#define	BNEP_COMPRESSED_ETHERNET		0x02 +#define	BNEP_COMPRESSED_ETHERNET_SRC_ONLY	0x03 +#define	BNEP_COMPRESSED_ETHERNET_DST_ONLY	0x04 + +/* BNEP extension header types */ +#define	BNEP_EXTENSION_CONTROL			0x00 + +/* BNEP control types */ +#define	BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD	0x00 +#define	BNEP_SETUP_CONNECTION_REQUEST		0x01 +#define	BNEP_SETUP_CONNECTION_RESPONSE		0x02 +#define	BNEP_FILTER_NET_TYPE_SET		0x03 +#define	BNEP_FILTER_NET_TYPE_RESPONSE		0x04 +#define	BNEP_FILTER_MULTI_ADDR_SET		0x05 +#define	BNEP_FILTER_MULTI_ADDR_RESPONSE		0x06 + +/* BNEP setup response codes */ +#define	BNEP_SETUP_SUCCESS		0x0000 +#define	BNEP_SETUP_INVALID_SRC_UUID	0x0001 +#define	BNEP_SETUP_INVALID_DST_UUID	0x0002 +#define	BNEP_SETUP_INVALID_UUID_SIZE	0x0003 +#define	BNEP_SETUP_NOT_ALLOWED		0x0004 + +/* BNEP filter return codes */ +#define	BNEP_FILTER_SUCCESS		0x0000 +#define	BNEP_FILTER_UNSUPPORTED_REQUEST	0x0001 +#define	BNEP_FILTER_INVALID_RANGE	0x0002 +#define	BNEP_FILTER_TOO_MANY_FILTERS	0x0003 +#define	BNEP_FILTER_SECURITY_FAILURE	0x0004 diff --git a/usr.sbin/bluetooth/btpand/btpand.8 b/usr.sbin/bluetooth/btpand/btpand.8 new file mode 100644 index 0000000000000..ece16c25090a1 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/btpand.8 @@ -0,0 +1,241 @@ +.\" $NetBSD: btpand.8,v 1.3 2008/08/17 14:43:07 plunky Exp $ +.\" $FreeBSD$ +.\" +.\" Copyright (c) 2008 Iain Hibbert +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\"    notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\"    notice, this list of conditions and the following disclaimer in the +.\"    documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd August 17, 2008 +.Dt BTPAND 8 +.Os +.Sh NAME +.Nm btpand +.Nd Bluetooth PAN daemon +.Sh SYNOPSIS +.Nm +.Op Fl i Ar ifname +.Op Fl m Ar mode +.Fl a Ar addr +.Fl d Ar device +.Brq Fl s Ar service | Fl S Ar service Op Fl p Ar psm +.Nm +.Op Fl c Ar path +.Op Fl i Ar ifname +.Op Fl l Ar limit +.Op Fl m Ar mode +.Op Fl p Ar psm +.Fl d Ar device +.Brq Fl s Ar service | Fl S Ar service +.Sh DESCRIPTION +The +.Nm +daemon handles Bluetooth Personal Area Networking services +in the system. +It can operate in client mode as a Personal Area Networking User +.Pq PANU +or in server mode as Network Access Point +.Pq NAP , +Group ad-hoc Network +.Pq GN +or PANU host. +.Nm +connects to the system via a +.Xr tap 4 +virtual Ethernet device and forwards Ethernet packets to +remote Bluetooth devices using the Bluetooth Network Encapsulation +Protocol +.Pq BNEP . +.Pp +The PANU client is the device that uses either the NAP or GN +service, or can talk directly to a PANU host in a crossover +cable fashion. +.Pp +A GN host forwards Ethernet packets to each of the connected PAN +users as needed but does not provide access to any additional networks. +.Pp +The NAP service provides some of the features of an Ethernet bridge, +with the NAP host forwarding Ethernet packets between each of the +connected PAN users, and a different network +media. +.Pp +Note, the only differences between NAP and GN services as implemented by +.Nm +are in the SDP service record. +The bridging of packets by the NAP must be configured separately. +.Pp +The options are as follows: +.Bl -tag -width ".Fl a Ar address" +.It Fl a Ar address +In client mode, address of remote server. +May be given as BDADDR or name, in which case +.Nm +will attempt to resolve the address via the +.Xr bt_gethostbyname 3 +call. +.It Fl c Ar path +In server mode, specify +.Ar path +to the +.Xr sdpd 8 +control socket. +The default path is +.Pa /var/run/sdp . +.It Fl d Ar device +Restrict connections to the local +.Ar device . +May be given as BDADDR or name, in which case +.Nm +will attempt to resolve the address via the +.Xr bt_devaddr 3 +call. +.Nm +will set the +.Xr tap 4 +interface physical address to the BDADDR +of the Bluetooth radio. +.It Fl i Ar ifname +.Nm +uses the +.Xr tap 4 +driver to create a new network interface for use. +Use this option to select a specific +.Xr tap 4 +device interface which must already be created. +.It Fl l Ar limit +In server mode, limit the number of simultaneous connections. +The default limit is 7 for NAP and GN servers, +and 1 for a PANU server. +.It Fl m Ar mode +Set L2CAP connection link mode. +Supported modes are: +.Pp +.Bl -tag -compact +.It auth +require devices to be paired. +.It encrypt +auth, plus enable encryption. +.It secure +encryption, plus change of link key. +.El +.Pp +NOT YET SUPPORTED. +Use global device settings to set authentication and encryption. +.It Fl p Ar psm +Use an alternative L2CAP Protocol/Service Multiplexer +.Pq PSM +for server mode or client mode +.Pq when not using Service Discovery . +The default PSM for BNEP is 15 +.Pq 0x000f . +.It Fl s Ar service +Name of +.Ar service +to provide or connect to, the following services are recognised: +.Pp +.Bl -tag -compact +.It GN +Group ad-hoc Network. +.It NAP +Network Access Point. +.It PANU +Personal Area Networking User. +.El +.Pp +.It Fl S Ar service +As per +.Fl s +except that +.Nm +will not use SDP services for connection setup. +.El +.Pp +When providing networking services, the Bluetooth PAN profile says that the +.Sq Class of Device +property of the bluetooth controller SHALL include Networking capability +.Pq set bit 0x020000 . +See +.Xr hccontrol 8 +for details. +.Pp +After +.Nm +has set up the client or server connection and opened the +.Xr tap 4 +interface, it will create a pid file and detach. +.Sh EXIT STATUS +.Ex -std +.Sh FILES +.Bl -tag -compact +.It Pa /dev/tap +.It Pa /etc/bluetooth/hosts +.It Pa /var/run/sdp +.It Pa /var/run/tap Ns Em N Ns No .pid +.El +.Sh EXAMPLES +.Dl ifconfig tap1 create +.Dl btpand -a host -d mydevice -s NAP -i tap1 +.Dl dhclient tap1 +.Pp +Will create a connection to the NAP on +.Ar host , +and link that to the +.Ar tap1 +interface. +.Pp +.Dl btpand -d mydevice -s GN +.Pp +Will create a Group Network and register the GN service with the local +SDP server. +.Sh SEE ALSO +.Xr bluetooth 3 , +.Xr tap 4 , +.Xr bridge 4 , +.Xr hccontrol 8 , +.Xr dhclient 8 , +.Xr ifconfig 8 , +.Xr sdpd 8 +.Pp +The +.Qq Personal Area Networking Profile +and +.Qq Bluetooth Network Encapsulation Protocol +specifications are available at +.Dl http://www.bluetooth.com/ +.Sh AUTHORS +.An Iain Hibbert +.Sh BUGS +There is no way to supply alternative values for the SDP record. +.Pp +There is no way to set net type or multicast address filters. +.Pp +.Nm +does not do any address routing except to directly connected +unicast addresses. +All other packets are multicast. +.Pp +As +.Nm +uses the BDADDR of the Bluetooth radio as the physical address +of the tap, only one instance can be run per radio. +.Pp +.Nm +can only provide a single service. diff --git a/usr.sbin/bluetooth/btpand/btpand.c b/usr.sbin/bluetooth/btpand/btpand.c new file mode 100644 index 0000000000000..57ee1331b3c22 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/btpand.c @@ -0,0 +1,290 @@ +/*	$NetBSD: btpand.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/cdefs.h> +__COPYRIGHT("@(#) Copyright (c) 2008 Iain Hibbert. All rights reserved."); +__RCSID("$NetBSD: btpand.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); + +#include <sys/wait.h> + +#include <bluetooth.h> +#include <err.h> +#include <fcntl.h> +#include <paths.h> +#include <sdp.h> +#include <stdio.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "btpand.h" + +/* global variables */ +const char *	control_path;		/* -c <path> */ +const char *	interface_name;		/* -i <ifname> */ +const char *	service_name;		/* -s <service> */ +uint16_t	service_class; + +bdaddr_t	local_bdaddr;		/* -d <addr> */ +bdaddr_t	remote_bdaddr;		/* -a <addr> */ +uint16_t	l2cap_psm = 15;		/* -p <psm> */ +int		l2cap_mode = 0;		/* -m <mode> */ + +int		server_limit;		/* -n <limit> */ + +static const struct { +	const char *	name; +	uint16_t	class; +	const char *	desc; +} services[] = { +	{ "PANU", SDP_SERVICE_CLASS_PANU, "Personal Area Networking User" }, +	{ "NAP",  SDP_SERVICE_CLASS_NAP,  "Network Acess Point"		  }, +	{ "GN",	  SDP_SERVICE_CLASS_GN,   "Group Network"		  }, +}; + +static void main_exit(int); +static void main_detach(void); +static void usage(void); + +int +main(int argc, char *argv[]) +{ +	unsigned long	ul; +	char *		ep; +	int		ch, status; + +	while ((ch = getopt(argc, argv, "a:c:d:i:l:m:p:S:s:")) != -1) { +		switch (ch) { +		case 'a': /* remote address */ +			if (!bt_aton(optarg, &remote_bdaddr)) { +				struct hostent  *he; + +				if ((he = bt_gethostbyname(optarg)) == NULL) +					errx(EXIT_FAILURE, "%s: %s", +					    optarg, hstrerror(h_errno)); + +				bdaddr_copy(&remote_bdaddr, +					(bdaddr_t *)he->h_addr); +			} + +			break; + +		case 'c': /* control socket path */ +			control_path = optarg; +			break; + +		case 'd': /* local address */ +			if (!bt_aton(optarg, &local_bdaddr)) { +				struct hostent  *he; + +				if ((he = bt_gethostbyname(optarg)) == NULL) +					errx(EXIT_FAILURE, "%s: %s", +					    optarg, hstrerror(h_errno)); + +				bdaddr_copy(&local_bdaddr, +					(bdaddr_t *)he->h_addr); +			} +			break; + +		case 'i': /* tap interface name */ +			if (strchr(optarg, '/') == NULL) { +				asprintf(&ep, "/dev/%s", optarg); +				interface_name = ep; +			} else +				interface_name = optarg; +			break; + +		case 'l': /* limit server sessions */ +			ul = strtoul(optarg, &ep, 10); +			if (*optarg == '\0' || *ep != '\0' || ul == 0) +				errx(EXIT_FAILURE, "%s: invalid session limit", +					optarg); + +			server_limit = ul; +			break; + +		case 'm': /* link mode */ +			warnx("Setting link mode is not yet supported"); +			break; + +		case 'p': /* protocol/service multiplexer */ +			ul = strtoul(optarg, &ep, 0); +			if (*optarg == '\0' || *ep != '\0' +			    || ul > 0xffff || L2CAP_PSM_INVALID(ul)) +				errx(EXIT_FAILURE, "%s: invalid PSM", optarg); + +			l2cap_psm = ul; +			break; + +		case 's': /* service */ +		case 'S': /* service (no SDP) */ +			for (ul = 0; strcasecmp(optarg, services[ul].name); ul++) { +				if (ul == __arraycount(services)) +					errx(EXIT_FAILURE, "%s: unknown service", optarg); +			} + +			if (ch == 's') +				service_name = services[ul].name; + +			service_class = services[ul].class; +			break; + +		default: +			usage(); +			/* NOTREACHED */ +		} +	} + +	argc -= optind; +	argv += optind; + +	/* validate options */ +	if (bdaddr_any(&local_bdaddr) || service_class == 0) +		usage(); + +	if (!bdaddr_any(&remote_bdaddr) && (server_limit != 0 || +	    control_path != 0 || (service_name != NULL && l2cap_psm != 0))) +		usage(); + +	/* default options */ +	if (interface_name == NULL) +		interface_name = "/dev/tap"; + +	if (bdaddr_any(&remote_bdaddr) && server_limit == 0) { +		if (service_class == SDP_SERVICE_CLASS_PANU) +			server_limit = 1; +		else +			server_limit = 7; +	} + +#ifdef L2CAP_LM_MASTER +	if (server_limit > 1 && service_class != SDP_SERVICE_CLASS_PANU) +		l2cap_mode |= L2CAP_LM_MASTER; +#endif + +	/* +	 * fork() now so that the setup can be done in the child process +	 * (as kqueue is not inherited) but block in the parent until the +	 * setup is finished so we can return an error if necessary. +	 */ +	switch(fork()) { +	case -1: /* bad */ +		err(EXIT_FAILURE, "fork() failed"); + +	case 0:	/* child */ +		signal(SIGPIPE, SIG_IGN); + +		openlog(getprogname(), LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_DAEMON); + +		channel_init(); +		server_init(); +		event_init(); +		client_init(); +		tap_init(); + +		main_detach(); + +		event_dispatch(); +		break; + +	default: /* parent */ +		signal(SIGUSR1, main_exit); +		wait(&status); + +		if (WIFEXITED(status)) +			exit(WEXITSTATUS(status)); + +		break; +	} + +	err(EXIT_FAILURE, "exiting"); +} + +static void +main_exit(int s) +{ + +	/* child is all grown up */ +	_exit(EXIT_SUCCESS); +} + +static void +main_detach(void) +{ +	int fd; + +	if (kill(getppid(), SIGUSR1) == -1) +		log_err("Could not signal main process: %m"); + +	if (setsid() == -1) +		log_err("setsid() failed"); + +	fd = open(_PATH_DEVNULL, O_RDWR, 0); +	if (fd == -1) { +		log_err("Could not open %s", _PATH_DEVNULL); +	} else { +		(void)dup2(fd, STDIN_FILENO); +		(void)dup2(fd, STDOUT_FILENO); +		(void)dup2(fd, STDERR_FILENO); +		close(fd); +	} +} + +static void +usage(void) +{ +	const char *p = getprogname(); +	int n = strlen(p); + +	fprintf(stderr, +	    "usage: %s [-i ifname] [-m mode] -a address -d device\n" +	    "       %*s {-s service | -S service [-p psm]}\n" +	    "       %s [-c path] [-i ifname] [-l limit] [-m mode] [-p psm] -d device\n" +	    "       %*s {-s service | -S service}\n" +	    "\n" +	    "Where:\n" +	    "\t-a address  remote bluetooth device\n" +	    "\t-c path     SDP server socket\n" +	    "\t-d device   local bluetooth device\n" +	    "\t-i ifname   tap interface\n" +	    "\t-l limit    limit server sessions\n" +	    "\t-m mode     L2CAP link mode (NOT YET SUPPORTED)\n" +	    "\t-p psm      L2CAP PSM\n" +	    "\t-S service  service name (no SDP)\n" +	    "\t-s service  service name\n" +	    "\n" +	    "Known services:\n" +	    "", p, n, "", p, n, ""); + +	for (n = 0; n < __arraycount(services); n++) +		fprintf(stderr, "\t%s\t%s\n", services[n].name, services[n].desc); + +	exit(EXIT_FAILURE); +} diff --git a/usr.sbin/bluetooth/btpand/btpand.h b/usr.sbin/bluetooth/btpand/btpand.h new file mode 100644 index 0000000000000..23a9c76b8dae0 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/btpand.h @@ -0,0 +1,208 @@ +/*	$NetBSD: btpand.h,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/types.h> +#include <sys/queue.h> +#include <sys/socket.h> + +#include <net/if.h> +#include <net/ethernet.h> + +#include <assert.h> +#include <bluetooth.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> + +#include "event.h" + +#ifndef __arraycount +#define __arraycount(__x)	(int)(sizeof((__x)) / sizeof((__x)[0])) +#endif + +#ifndef	L2CAP_PSM_INVALID +#define	L2CAP_PSM_INVALID(psm)	(((psm) & 0x0101) != 0x0001) +#endif + +typedef struct channel	channel_t; +typedef struct pfilter	pfilter_t; +typedef struct mfilter	mfilter_t; +typedef struct packet	packet_t; +typedef struct pkthdr	pkthdr_t; +typedef struct pktlist	pktlist_t; +typedef struct exthdr	exthdr_t; +typedef struct extlist	extlist_t; + +LIST_HEAD(chlist, channel); +STAILQ_HEAD(extlist, exthdr); +STAILQ_HEAD(pktlist, pkthdr); + +enum channel_state { +	CHANNEL_CLOSED, +	CHANNEL_WAIT_CONNECT_REQ, +	CHANNEL_WAIT_CONNECT_RSP, +	CHANNEL_OPEN, +}; + +#define CHANNEL_MAXQLEN		128 + +/* BNEP or tap channel */ +struct channel { +	enum channel_state	state; +	bool			oactive; + +	uint8_t			laddr[ETHER_ADDR_LEN]; +	uint8_t			raddr[ETHER_ADDR_LEN]; +	size_t			mru; +	size_t			mtu; + +	int			npfilter; +	pfilter_t *		pfilter; + +	int			nmfilter; +	mfilter_t *		mfilter; + +	pktlist_t		pktlist; +	int			qlen; + +	int			fd; +	struct event		rd_ev; +	struct event		wr_ev; +	uint8_t *		sendbuf; + +	bool			(*send)(channel_t *, packet_t *); +	bool			(*recv)(packet_t *); + +	int			tick; + +	struct pidfh		*pfh; + +	int			refcnt; +	LIST_ENTRY(channel)	next; +}; + +/* network protocol type filter */ +struct pfilter { +	uint16_t	start; +	uint16_t	end; +}; + +/* multicast address filter */ +struct mfilter { +	uint8_t		start[ETHER_ADDR_LEN]; +	uint8_t		end[ETHER_ADDR_LEN]; +}; + +/* packet data buffer */ +struct packet { +	channel_t *		chan;	/* source channel */ +	uint8_t *		dst;	/* dest address */ +	uint8_t *		src;	/* source address */ +	uint8_t *		type;	/* protocol type */ +	uint8_t *		ptr;	/* data pointer */ +	size_t			len;	/* data length */ +	int			refcnt;	/* reference count */ +	extlist_t		extlist;/* extension headers */ +	uint8_t			buf[0];	/* data starts here */ +}; + +/* extension header */ +struct exthdr { +	STAILQ_ENTRY(exthdr)	next; +	uint8_t *		ptr; +	uint8_t			len; +}; + +/* packet header */ +struct pkthdr { +	STAILQ_ENTRY(pkthdr)	next; +	packet_t *		data; +}; + +/* global variables */ +extern const char *	control_path; +extern const char *	service_name; +extern const char *	interface_name; +extern bdaddr_t		local_bdaddr; +extern bdaddr_t		remote_bdaddr; +extern uint16_t		l2cap_psm; +extern int		l2cap_mode; +extern uint16_t		service_class; +extern int		server_limit; + +/* + * Bluetooth addresses are stored the other way around than + * Ethernet addresses even though they are of the same family + */ +static inline void +b2eaddr(void *dst, bdaddr_t *src) +{ +	uint8_t *d = dst; +	int i; + +	for (i = 0; i < ETHER_ADDR_LEN; i++) +		d[i] = src->b[ETHER_ADDR_LEN - i - 1]; +} + +#define log_err(fmt, args...)		syslog(LOG_ERR, fmt , ##args) +#define log_info(fmt, args...)		syslog(LOG_INFO, fmt , ##args) +#define log_notice(fmt, args...)	syslog(LOG_NOTICE, fmt , ##args) +#define log_debug(fmt, args...)		syslog(LOG_DEBUG, "%s: " fmt, __func__ , ##args) + +/* bnep.c */ +bool		bnep_send(channel_t *, packet_t *); +bool		bnep_recv(packet_t *); +void		bnep_send_control(channel_t *, uint8_t, ...); + +/* channel.c */ +void		channel_init(void); +channel_t *	channel_alloc(void); +bool		channel_open(channel_t *, int); +void		channel_close(channel_t *); +void		channel_free(channel_t *); +void		channel_timeout(channel_t *, int); +void		channel_put(channel_t *, packet_t *); + +/* client.c */ +void		client_init(void); + +/* packet.c */ +packet_t *	packet_alloc(channel_t *); +void		packet_free(packet_t *); +void		packet_adj(packet_t *, size_t); +pkthdr_t *	pkthdr_alloc(packet_t *); +void		pkthdr_free(pkthdr_t *); + +/* server.c */ +void		server_init(void); +void		server_update(int); + +/* tap.c */ +void		tap_init(void); diff --git a/usr.sbin/bluetooth/btpand/channel.c b/usr.sbin/bluetooth/btpand/channel.c new file mode 100644 index 0000000000000..b4eb4ab49c8f4 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/channel.c @@ -0,0 +1,335 @@ +/*	$NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); + +#include <sys/param.h> +#include <sys/ioctl.h> + +#include <libutil.h> +#include <unistd.h> + +#include "btpand.h" + +static struct chlist	channel_list; +static int		channel_count; +static int		channel_tick; + +static void channel_start(int, short, void *); +static void channel_read(int, short, void *); +static void channel_dispatch(packet_t *); +static void channel_watchdog(int, short, void *); + +void +channel_init(void) +{ + +	LIST_INIT(&channel_list); +} + +channel_t * +channel_alloc(void) +{ +	channel_t *chan; + +	chan = malloc(sizeof(channel_t)); +	if (chan == NULL) { +		log_err("%s() failed: %m", __func__); +		return NULL; +	} + +	memset(chan, 0, sizeof(channel_t)); +	STAILQ_INIT(&chan->pktlist); +	chan->state = CHANNEL_CLOSED; +	LIST_INSERT_HEAD(&channel_list, chan, next); + +	server_update(++channel_count); + +	return chan; +} + +bool +channel_open(channel_t *chan, int fd) +{ +	int n; + +	assert(chan->refcnt == 0); +	assert(chan->state != CHANNEL_CLOSED); + +	if (chan->mtu > 0) { +		chan->sendbuf = malloc(chan->mtu); +		if (chan->sendbuf == NULL) { +			log_err("Could not malloc channel sendbuf: %m"); +			return false; +		} +	} + +	n = 1; +	if (ioctl(fd, FIONBIO, &n) == -1) { +		log_err("Could not set non-blocking IO: %m"); +		return false; +	} + +	event_set(&chan->rd_ev, fd, EV_READ | EV_PERSIST, channel_read, chan); +	if (event_add(&chan->rd_ev, NULL) == -1) { +		log_err("Could not add channel read event: %m"); +		return false; +	} + +	event_set(&chan->wr_ev, fd, EV_WRITE, channel_start, chan); + +	chan->refcnt++; +	chan->fd = fd; + +	log_debug("(fd#%d)", chan->fd); + +	return true; +} + +void +channel_close(channel_t *chan) +{ +	pkthdr_t *ph; + +	assert(chan->state != CHANNEL_CLOSED); + +	log_debug("(fd#%d)", chan->fd); + +	chan->state = CHANNEL_CLOSED; +	event_del(&chan->rd_ev); +	event_del(&chan->wr_ev); +	close(chan->fd); +	chan->refcnt--; +	chan->tick = 0; + +	while ((ph = STAILQ_FIRST(&chan->pktlist)) != NULL) { +		STAILQ_REMOVE_HEAD(&chan->pktlist, next); +		pkthdr_free(ph); +		chan->qlen--; +	} + +	if (chan->pfh != NULL) { +		pidfile_remove(chan->pfh); +		chan->pfh = NULL; +	} + +	if (chan->refcnt == 0) +		channel_free(chan); +} + +void +channel_free(channel_t *chan) +{ + +	assert(chan->refcnt == 0); +	assert(chan->state == CHANNEL_CLOSED); +	assert(chan->qlen == 0); +	assert(STAILQ_EMPTY(&chan->pktlist)); + +	LIST_REMOVE(chan, next); +	free(chan->pfilter); +	free(chan->mfilter); +	free(chan->sendbuf); +	free(chan); + +	server_update(--channel_count); + +	if (server_limit == 0) { +		log_info("connection closed, exiting"); +		exit(EXIT_SUCCESS); +	} +} + +static void +channel_start(int fd, short ev, void *arg) +{ +	channel_t *chan = arg; +	pkthdr_t *ph; + +	chan->oactive = true; + +	while (chan->qlen > 0) { +		ph = STAILQ_FIRST(&chan->pktlist); + +		channel_timeout(chan, 10); +		if (chan->send(chan, ph->data) == false) { +			if (event_add(&chan->wr_ev, NULL) == -1) { +				log_err("Could not add channel write event: %m"); +				channel_close(chan); +			} +			return; +		} + +		STAILQ_REMOVE_HEAD(&chan->pktlist, next); +		pkthdr_free(ph); +		chan->qlen--; +	} + +	channel_timeout(chan, 0); +	chan->oactive = false; +} + +static void +channel_read(int fd, short ev, void *arg) +{ +	channel_t *chan = arg; +	packet_t *pkt; +	ssize_t nr; + +	pkt = packet_alloc(chan); +	if (pkt == NULL) { +		channel_close(chan); +		return; +	} + +	nr = read(fd, pkt->buf, chan->mru); +	if (nr == -1) { +		log_err("channel read error: %m"); +		packet_free(pkt); +		channel_close(chan); +		return; +	} +	if (nr == 0) {	/* EOF */ +		log_debug("(fd#%d) EOF", fd); +		packet_free(pkt); +		channel_close(chan); +		return; +	} +	pkt->len = nr; + +	if (chan->recv(pkt) == true) +		channel_dispatch(pkt); + +	packet_free(pkt); +} + +static void +channel_dispatch(packet_t *pkt) +{ +	channel_t *chan; + +	/* +	 * This is simple routing. I'm not sure if its allowed by +	 * the PAN or BNEP specifications, but it seems logical +	 * to send unicast packets to connected destinations where +	 * possible. +	 */ +	if (!ETHER_IS_MULTICAST(pkt->dst)) { +		LIST_FOREACH(chan, &channel_list, next) { +			if (chan == pkt->chan +			    || chan->state != CHANNEL_OPEN) +				continue; + +			if (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) == 0) { +				if (chan->qlen > CHANNEL_MAXQLEN) +					log_notice("Queue overflow"); +				else +					channel_put(chan, pkt); + +				return; +			} +		} +	} + +	LIST_FOREACH(chan, &channel_list, next) { +		if (chan == pkt->chan +		    || chan->state != CHANNEL_OPEN) +			continue; + +		if (chan->qlen > CHANNEL_MAXQLEN) { +			log_notice("Queue overflow"); +			continue; +		} + +		channel_put(chan, pkt); +	} +} + +void +channel_put(channel_t *chan, packet_t *pkt) +{ +	pkthdr_t *ph; + +	ph = pkthdr_alloc(pkt); +	if (ph == NULL) +		return; + +	chan->qlen++; +	STAILQ_INSERT_TAIL(&chan->pktlist, ph, next); + +	if (!chan->oactive) +		channel_start(chan->fd, EV_WRITE, chan); +} + +/* + * Simple watchdog timer, only ticks when it is required and + * closes the channel down if it times out. + */ +void +channel_timeout(channel_t *chan, int to) +{ +	static struct event ev; + +	if (to == 0) +		chan->tick = 0; +	else +		chan->tick = (channel_tick + to) % 60; + +	if (channel_tick == 0) { +		evtimer_set(&ev, channel_watchdog, &ev); +		channel_watchdog(0, 0, &ev); +	} +} + +static void +channel_watchdog(int fd, short ev, void *arg) +{ +	static struct timeval tv = { .tv_sec = 1 }; +	channel_t *chan, *next; +	int tick; + +	tick = (channel_tick % 60) + 1; +	channel_tick = 0; + +	next = LIST_FIRST(&channel_list); +	while ((chan = next) != NULL) { +		next = LIST_NEXT(chan, next); + +		if (chan->tick == tick) +			channel_close(chan); +		else if (chan->tick != 0) +			channel_tick = tick; +	} + +	if (channel_tick != 0 && evtimer_add(arg, &tv) < 0) { +		log_err("Could not add watchdog event: %m"); +		exit(EXIT_FAILURE); +	} +} diff --git a/usr.sbin/bluetooth/btpand/client.c b/usr.sbin/bluetooth/btpand/client.c new file mode 100644 index 0000000000000..97064dbbbddc6 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/client.c @@ -0,0 +1,192 @@ +/*	$NetBSD: client.c,v 1.2 2008/12/06 20:01:14 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: client.c,v 1.2 2008/12/06 20:01:14 plunky Exp $"); + +#include <bluetooth.h> +#include <errno.h> +#include <sdp.h> +#include <unistd.h> + +#include "btpand.h" +#include "bnep.h" +#include "sdp.h" + +static void client_query(void); + +void +client_init(void) +{ +	struct sockaddr_l2cap sa; +	channel_t *chan; +	socklen_t len; +	int fd; +	uint16_t mru, mtu; + +	if (bdaddr_any(&remote_bdaddr)) +		return; + +	if (service_name) +		client_query(); + +	fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); +	if (fd == -1) { +		log_err("Could not open L2CAP socket: %m"); +		exit(EXIT_FAILURE); +	} + +	memset(&sa, 0, sizeof(sa)); +	sa.l2cap_family = AF_BLUETOOTH; +	sa.l2cap_len = sizeof(sa); +	bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr); +	if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { +		log_err("Could not bind client socket: %m"); +		exit(EXIT_FAILURE); +	} + +	mru = BNEP_MTU_MIN; +	if (setsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) { +		log_err("Could not set L2CAP IMTU (%d): %m", mru); +		exit(EXIT_FAILURE); +	} + +	log_info("Opening connection to service 0x%4.4x at %s", +	    service_class, bt_ntoa(&remote_bdaddr, NULL)); + +	sa.l2cap_psm = htole16(l2cap_psm); +	bdaddr_copy(&sa.l2cap_bdaddr, &remote_bdaddr); +	if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { +		log_err("Could not connect: %m"); +		exit(EXIT_FAILURE); +	} + +	len = sizeof(mru); +	if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) { +		log_err("Could not get IMTU: %m"); +		exit(EXIT_FAILURE); +	} +	if (mru < BNEP_MTU_MIN) { +		log_err("L2CAP IMTU too small (%d)", mru); +		exit(EXIT_FAILURE); +	} + +	len = sizeof(mtu); +	if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) { +		log_err("Could not get L2CAP OMTU: %m"); +		exit(EXIT_FAILURE); +	} +	if (mtu < BNEP_MTU_MIN) { +		log_err("L2CAP OMTU too small (%d)", mtu); +		exit(EXIT_FAILURE); +	} + +	chan = channel_alloc(); +	if (chan == NULL) +		exit(EXIT_FAILURE); + +	chan->send = bnep_send; +	chan->recv = bnep_recv; +	chan->mru = mru; +	chan->mtu = mtu; +	b2eaddr(chan->raddr, &remote_bdaddr); +	b2eaddr(chan->laddr, &local_bdaddr); +	chan->state = CHANNEL_WAIT_CONNECT_RSP; +	channel_timeout(chan, 10); +	if (!channel_open(chan, fd)) +		exit(EXIT_FAILURE); + +	bnep_send_control(chan, BNEP_SETUP_CONNECTION_REQUEST, +	    2, service_class, SDP_SERVICE_CLASS_PANU); +} + +static void +client_query(void) +{ +	uint8_t buffer[512]; +	sdp_attr_t attr; +	uint32_t range; +	void *ss; +	int rv; +	uint8_t *seq0, *seq1; + +	attr.flags = SDP_ATTR_INVALID; +	attr.attr = 0; +	attr.vlen = sizeof(buffer); +	attr.value = buffer; + +	range = SDP_ATTR_RANGE(SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST, +			       SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST); + +	ss = sdp_open(&local_bdaddr, &remote_bdaddr); +	if (ss == NULL || (errno = sdp_error(ss)) != 0) { +		log_err("%s: %m", service_name); +		exit(EXIT_FAILURE); +	} + +	log_info("Searching for %s service at %s", +	    service_name, bt_ntoa(&remote_bdaddr, NULL)); + +	rv = sdp_search(ss, 1, &service_class, 1, &range, 1, &attr); +	if (rv != 0) { +		log_err("%s: %s", service_name, strerror(sdp_error(ss))); +		exit(EXIT_FAILURE); +	} + +	sdp_close(ss); + +	if (attr.flags != SDP_ATTR_OK +	    || attr.attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST) { +		log_err("%s service not found", service_name); +		exit(EXIT_FAILURE); +	} + +	/* +	 * we expect the following protocol descriptor list +	 * +	 *	seq len +	 *	  seq len +	 *	    uuid value == L2CAP +	 *	    uint16 value16 => PSM +	 *	  seq len +	 *	    uuid value == BNEP +	 */ +	if (_sdp_get_seq(&attr.value, attr.value + attr.vlen, &seq0) +	    && _sdp_get_seq(&seq0, attr.value, &seq1) +	    && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_L2CAP) +	    && _sdp_get_uint16(&seq1, seq0, &l2cap_psm) +	    && _sdp_get_seq(&seq0, attr.value, &seq1) +	    && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_BNEP)) { +		log_info("Found PSM %d for service %s", l2cap_psm, service_name); +		return; +	} + +	log_err("%s query failed", service_name); +	exit(EXIT_FAILURE); +} diff --git a/usr.sbin/bluetooth/btpand/event.c b/usr.sbin/bluetooth/btpand/event.c new file mode 100644 index 0000000000000..253084e90c6ef --- /dev/null +++ b/usr.sbin/bluetooth/btpand/event.c @@ -0,0 +1,309 @@ +/* + * event.h + */ + +/*- + * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +/* + * Hack to provide libevent (see devel/libevent port) like API. + * Should be removed if FreeBSD ever decides to import libevent into base. + */ + +#include <sys/select.h> +#include <sys/time.h> +#include <sys/queue.h> +#include <assert.h> +#include <stdarg.h> +#include <stdio.h> +#include <string.h> +#include <syslog.h> + +#include "event.h" +#include "btpand.h" + +#define		__event_link(ev) \ +do { \ +		TAILQ_INSERT_TAIL(&pending, ev, next); \ +		ev->flags |= EV_PENDING; \ +} while (0) + +static void	tv_add(struct timeval *, struct timeval const *); +static void	tv_sub(struct timeval *, struct timeval const *); +static int	tv_cmp(struct timeval const *, struct timeval const *); +static int	__event_dispatch(void); +static void	__event_add_current(struct event *); +static void	__event_del_current(struct event *); + + +static TAILQ_HEAD(, event)	pending; +static TAILQ_HEAD(, event)	current; + +void +event_init(void) +{ +	TAILQ_INIT(&pending); +} + +int +event_dispatch(void) +{ +	while (__event_dispatch() == 0) +		; + +	return (-1); +} + +static int +__event_dispatch(void) +{ +	fd_set			r, w; +	int			nfd; +	struct event		*ev; +	struct timeval		now, timeout, t; + +	FD_ZERO(&r); +	FD_ZERO(&w); + +	nfd = 0; + +	gettimeofday(&now, NULL); + +	timeout.tv_sec = 10;	/* arbitrary */ +	timeout.tv_usec = 0; + +	TAILQ_INIT(¤t); + +	/* +	 * Build fd_set's +	 */ + +	event_log_debug("%s: building fd set...", __func__); + +	while (!TAILQ_EMPTY(&pending)) { +		ev = TAILQ_FIRST(&pending); +		event_del(ev); + +		if (ev->flags & EV_HAS_TIMEOUT) { +			t = now; + +			if (tv_cmp(&t, &ev->expire) <= 0) +				t.tv_sec = t.tv_usec = 0; +			else +				tv_sub(&t, &ev->expire); + +			if (tv_cmp(&t, &timeout) < 0) +				timeout = t; +		} + +		if (ev->fd >= 0) { +			if (ev->flags & EV_READ) { +				FD_SET(ev->fd, &r); +				nfd = (nfd > ev->fd) ? nfd : ev->fd; +			} + +			if (ev->flags & EV_WRITE) { +				FD_SET(ev->fd, &w); +				nfd = (nfd > ev->fd) ? nfd : ev->fd; +			} +		} + +		__event_add_current(ev); +	} + +	event_log_debug("%s: waiting for events...", __func__); + +	nfd = select(nfd + 1, &r, &w, NULL, &timeout); +	if (nfd < 0) +		return (-1); + +	/* +	 * Process current pending +	 */ + +	event_log_debug("%s: processing events...", __func__); + +	gettimeofday(&now, NULL); + +	while (!TAILQ_EMPTY(¤t)) { +		ev = TAILQ_FIRST(¤t); +		__event_del_current(ev); + +		/* check if fd is ready for reading/writing */ +		if (nfd > 0 && ev->fd >= 0) { +			if (FD_ISSET(ev->fd, &r) || FD_ISSET(ev->fd, &w)) { +				if (ev->flags & EV_PERSIST) { +					if (ev->flags & EV_HAS_TIMEOUT) +						event_add(ev, &ev->timeout); +					else +						event_add(ev, NULL); +				} + +				nfd --; + +				event_log_debug("%s: calling %p(%d, %p), " \ +					"ev=%p", __func__, ev->cb, ev->fd, +					ev->cbarg, ev); + +				(ev->cb)(ev->fd, +					(ev->flags & (EV_READ|EV_WRITE)), +					ev->cbarg); + +				continue; +			} +		} + +		/* if event has no timeout - just requeue */ +		if ((ev->flags & EV_HAS_TIMEOUT) == 0) { +			event_add(ev, NULL); +			continue; +		} + +		/* check if event has expired */ +		if (tv_cmp(&now, &ev->expire) >= 0) { +			if (ev->flags & EV_PERSIST)  +				event_add(ev, &ev->timeout); + +			event_log_debug("%s: calling %p(%d, %p), ev=%p", +				__func__, ev->cb, ev->fd, ev->cbarg, ev); + +			(ev->cb)(ev->fd, +				(ev->flags & (EV_READ|EV_WRITE)), +				ev->cbarg); + +			continue; +		} + +		assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); +		__event_link(ev); +	} + +	return (0); +} + +void +__event_set(struct event *ev, int fd, short flags, +	void (*cb)(int, short, void *), void *cbarg) +{ +	ev->fd = fd; +	ev->flags = flags; +	ev->cb = cb; +	ev->cbarg = cbarg; +} + +int +__event_add(struct event *ev, const struct timeval *timeout) +{ +	assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); + +	if (timeout != NULL) { +		gettimeofday(&ev->expire, NULL); +		tv_add(&ev->expire, timeout); +		ev->timeout = *timeout; +		ev->flags |= EV_HAS_TIMEOUT; +	} else +		ev->flags &= ~EV_HAS_TIMEOUT; + +	__event_link(ev); + +	return (0); +} + +int +__event_del(struct event *ev) +{ +	assert((ev->flags & EV_CURRENT) == 0); + +	if ((ev->flags & EV_PENDING) != 0) { +		TAILQ_REMOVE(&pending, ev, next); +		ev->flags &= ~EV_PENDING; +	} + +	return (0); +} + +static void +__event_add_current(struct event *ev) +{ +	assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0); + +	TAILQ_INSERT_TAIL(¤t, ev, next); +	ev->flags |= EV_CURRENT; +} + +static void +__event_del_current(struct event *ev) +{ +	assert((ev->flags & (EV_CURRENT|EV_PENDING)) == EV_CURRENT); + +	TAILQ_REMOVE(¤t, ev, next); +	ev->flags &= ~EV_CURRENT; +} + +static void +tv_add(struct timeval *a, struct timeval const *b) +{ +	a->tv_sec += b->tv_sec; +	a->tv_usec += b->tv_usec; + +	if(a->tv_usec >= 1000000) { +		a->tv_usec -= 1000000; +		a->tv_sec += 1; +	} +} + +static void +tv_sub(struct timeval *a, struct timeval const *b) +{ +	if (a->tv_usec < b->tv_usec) { +		a->tv_usec += 1000000; +		a->tv_sec -= 1; +	} + +	a->tv_usec -= b->tv_usec; +	a->tv_sec -= b->tv_sec; +} + +static int +tv_cmp(struct timeval const *a, struct timeval const *b) +{ + 	if (a->tv_sec > b->tv_sec) +		return (1); + +	if (a->tv_sec < b->tv_sec) +		return (-1); + +	if (a->tv_usec > b->tv_usec) +		return (1); + +	if (a->tv_usec < b->tv_usec) +		return (-1); + +	return (0); +} + diff --git a/usr.sbin/bluetooth/btpand/event.h b/usr.sbin/bluetooth/btpand/event.h new file mode 100644 index 0000000000000..75515e38f77b9 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/event.h @@ -0,0 +1,147 @@ +/* + * event.h + */ + +/*- + * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +/* + * Hack to provide libevent (see devel/libevent port) like API. + * Should be removed if FreeBSD ever decides to import libevent into base. + */ + +#ifndef	_EVENT_H_ +#define	_EVENT_H_	1 + +#define	EV_READ         0x02 +#define	EV_WRITE        0x04 +#define	EV_PERSIST      0x10		/* Persistant event */ +#define	EV_PENDING	(1 << 13)	/* internal use only! */ +#define	EV_HAS_TIMEOUT	(1 << 14)	/* internal use only! */ +#define	EV_CURRENT	(1 << 15)	/* internal use only! */ + +struct event +{ +	int			fd; +	short			flags; +	void			(*cb)(int, short, void *); +	void			*cbarg; +	struct timeval		timeout; +	struct timeval		expire; + +#ifdef	EVENT_DEBUG +	char const		*files[3]; +	int			lines[3]; +#endif + +	TAILQ_ENTRY(event)	next; +}; + +void	event_init	(void); +int	event_dispatch	(void); + +void	__event_set	(struct event *, int, short, +			 void (*)(int, short, void *), void *); +int	__event_add	(struct event *, struct timeval const *); +int	__event_del	(struct event *); + +#ifdef	EVENT_DEBUG +#define event_log_err(fmt, args...)	syslog(LOG_ERR, fmt, ##args) +#define event_log_info(fmt, args...)	syslog(LOG_INFO, fmt, ##args) +#define event_log_notice(fmt, args...)	syslog(LOG_NOTICE, fmt, ##args) +#define event_log_debug(fmt, args...)	syslog(LOG_DEBUG, fmt, ##args) + +#define	event_set(ev, fd, flags, cb, cbarg) \ +	_event_set(__FILE__, __LINE__, ev, fd, flags, cb, cbarg) +#define	event_add(ev, timeout) \ +	_event_add(__FILE__, __LINE__, ev, timeout) +#define	event_del(ev) \ +	_event_del(__FILE__, __LINE__, ev) + +#define	evtimer_set(ev, cb, cbarg) \ +	_event_set(__FILE__, __LINE__, ev, -1, 0, cb, cbarg) +#define	evtimer_add(ev, timeout) \ +	_event_add(__FILE__, __LINE__, ev, timeout) + +static inline void +_event_set(char const *file, int line, struct event *ev, int fd, short flags, +		void (*cb)(int, short, void *), void *cbarg) +{ +	event_log_debug("set %s:%d ev=%p, fd=%d, flags=%#x, cb=%p, cbarg=%p", +		file, line, ev, fd, flags, cb, cbarg); + +	ev->files[0] = file; +	ev->lines[0] = line; + +	__event_set(ev, fd, flags, cb, cbarg); +} + +static inline int +_event_add(char const *file, int line, struct event *ev, +		struct timeval const *timeout) { +	event_log_debug("add %s:%d ev=%p, fd=%d, flags=%#x, cb=%p, cbarg=%p, " \ +		"timeout=%p", file, line, ev, ev->fd, ev->flags, ev->cb, +		ev->cbarg, timeout); + +	ev->files[1] = file; +	ev->lines[1] = line; + +	return (__event_add(ev, timeout)); +} + +static inline int +_event_del(char const *file, int line, struct event *ev) +{ +	event_log_debug("del %s:%d ev=%p, fd=%d, flags=%#x, cb=%p, cbarg=%p", +		file, line, ev, ev->fd, ev->flags, ev->cb, ev->cbarg); + +	ev->files[2] = file; +	ev->lines[2] = line; + +	return (__event_del(ev)); +} +#else +#define event_log_err(fmt, args...) +#define event_log_info(fmt, args...) +#define event_log_notice(fmt, args...) +#define event_log_debug(fmt, args...) + +#define	event_set(ev, fd, flags, cb, cbarg) \ +	__event_set(ev, fd, flags, cb, cbarg) +#define	event_add(ev, timeout) \ +	__event_add(ev, timeout) +#define	event_del(ev) \ +	__event_del(ev) + +#define	evtimer_set(ev, cb, cbarg) \ +	__event_set(ev, -1, 0, cb, cbarg) +#define	evtimer_add(ev, timeout) \ +	__event_add(ev, timeout) +#endif	/* EVENT_DEBUG */ + +#endif	/* ndef _EVENT_H_ */ diff --git a/usr.sbin/bluetooth/btpand/packet.c b/usr.sbin/bluetooth/btpand/packet.c new file mode 100644 index 0000000000000..e42e5c5b30459 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/packet.c @@ -0,0 +1,110 @@ +/*	$NetBSD: packet.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: packet.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); + +#include "btpand.h" + +packet_t * +packet_alloc(channel_t *chan) +{ +	packet_t *pkt; + +	pkt = malloc(sizeof(packet_t) + chan->mru); +	if (pkt == NULL) { +		log_err("%s() failed: %m", __func__); +		return NULL; +	} + +	memset(pkt, 0, sizeof(packet_t)); +	STAILQ_INIT(&pkt->extlist); +	pkt->ptr = pkt->buf; + +	pkt->chan = chan; +	chan->refcnt++; + +	return pkt; +} + +void +packet_free(packet_t *pkt) +{ +	exthdr_t *eh; + +	if (pkt->refcnt-- > 0) +		return; + +	while ((eh = STAILQ_FIRST(&pkt->extlist)) != NULL) { +		STAILQ_REMOVE_HEAD(&pkt->extlist, next); +		free(eh); +	} + +	pkt->chan->refcnt--; +	if (pkt->chan->refcnt == 0) +		channel_free(pkt->chan); + +	free(pkt); +} + +void +packet_adj(packet_t *pkt, size_t size) +{ + +	assert(pkt->refcnt == 0); +	assert(pkt->len >= size); + +	pkt->ptr += size; +	pkt->len -= size; +} + +pkthdr_t * +pkthdr_alloc(packet_t *pkt) +{ +	pkthdr_t *ph; + +	ph = malloc(sizeof(pkthdr_t)); +	if (ph == NULL) { +		log_err("%s() failed: %m", __func__); +		return NULL; +	} + +	ph->data = pkt; +	pkt->refcnt++; + +	return ph; +} + +void +pkthdr_free(pkthdr_t *ph) +{ + +	packet_free(ph->data); +	free(ph); +} diff --git a/usr.sbin/bluetooth/btpand/sdp.c b/usr.sbin/bluetooth/btpand/sdp.c new file mode 100644 index 0000000000000..e5aec1c6bbba7 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/sdp.c @@ -0,0 +1,209 @@ +/*	$NetBSD: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $"); + +#include <string.h> + +#include "sdp.h" + +/* + * SDP data stream manipulation routines + */ + +/* Bluetooth Base UUID */ +static const uuid_t BASE_UUID = { +	0x00000000, +	0x0000, +	0x1000, +	0x80, +	0x00, +	{ 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb } +}; + +/* + * _sdp_match_uuid16(ptr, limit, uuid) + * + *	examine SDP data stream at ptr for a UUID, and return + *	true if it matches the supplied short alias bluetooth UUID. + *	limit is the first address past the end of valid data. + */ +bool +_sdp_match_uuid16(uint8_t **ptr, uint8_t *limit, uint16_t uuid) +{ +	uint8_t *p = *ptr; +	uuid_t u1, u2; + +	memcpy(&u1, &BASE_UUID, sizeof(uuid_t)); +	u1.time_low = uuid; + +	if (!_sdp_get_uuid(&p, limit, &u2) +	    || !uuid_equal(&u1, &u2, NULL)) +		return false; + +	*ptr = p; +	return true; +} + +/* + * _sdp_get_uuid(ptr, limit, uuid) + * + *	examine SDP data stream at ptr for a UUID, and extract + *	to given storage, advancing ptr. + *	limit is the first address past the end of valid data. + */ +bool +_sdp_get_uuid(uint8_t **ptr, uint8_t *limit, uuid_t *uuid) +{ +	uint8_t *p = *ptr; + +	if (p + 1 > limit) +		return false; + +	switch (*p++) { +	case SDP_DATA_UUID16: +		if (p + 2 > limit) +			return false; + +		memcpy(uuid, &BASE_UUID, sizeof(uuid_t)); +		uuid->time_low = be16dec(p); +		p += 2; +		break; + +	case SDP_DATA_UUID32: +		if (p + 4 > limit) +			return false; + +		memcpy(uuid, &BASE_UUID, sizeof(uuid_t)); +		uuid->time_low = be32dec(p); +		p += 4; +		break; + +	case SDP_DATA_UUID128: +		if (p + 16 > limit) +			return false; + +		uuid_dec_be(p, uuid); +		p += 16; +		break; + +	default: +		return false; +	} + +	*ptr = p; +	return true; +} + +/* + * _sdp_get_seq(ptr, limit, seq) + * + *	examine SDP data stream at ptr for a sequence. return + *	seq pointer if found and advance ptr to next object. + *	limit is the first address past the end of valid data. + */ +bool +_sdp_get_seq(uint8_t **ptr, uint8_t *limit, uint8_t **seq) +{ +	uint8_t *p = *ptr; +	int32_t l; + +	if (p + 1 > limit) +		return false; + +	switch (*p++) { +	case SDP_DATA_SEQ8: +		if (p + 1 > limit) +			return false; + +		l = *p; +		p += 1; +		break; + +	case SDP_DATA_SEQ16: +		if (p + 2 > limit) +			return false; + +		l = be16dec(p); +		p += 2; +		break; + +	case SDP_DATA_SEQ32: +		if (p + 4 > limit) +			return false; + +		l = be32dec(p); +		p += 4; +		break; + +	default: +		return false; +	} +	if (p + l > limit) +		return false; + +	*seq = p; +	*ptr = p + l; +	return true; +} + +/* + * _sdp_get_uint16(ptr, limit, value) + * + *	examine SDP data stream at ptr for a uint16_t, and + *	extract to given storage, advancing ptr. + *	limit is the first address past the end of valid data. + */ +bool +_sdp_get_uint16(uint8_t **ptr, uint8_t *limit, uint16_t *value) +{ +	uint8_t *p = *ptr; +	uint16_t v; + +	if (p + 1 > limit) +		return false; + +	switch (*p++) { +	case SDP_DATA_UINT16: +		if (p + 2 > limit) +			return false; + +		v = be16dec(p); +		p += 2; +		break; + +	default: +		return false; +	} + +	*value = v; +	*ptr = p; +	return true; +} diff --git a/usr.sbin/bluetooth/btpand/sdp.h b/usr.sbin/bluetooth/btpand/sdp.h new file mode 100644 index 0000000000000..32dd95d474c35 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/sdp.h @@ -0,0 +1,38 @@ +/*	$NetBSD: sdp.h,v 1.2 2008/12/06 20:01:15 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <bluetooth.h> +#include <sdp.h> +#include <stdbool.h> +#include <uuid.h> + +bool _sdp_match_uuid16(uint8_t **, uint8_t *, uint16_t); +bool _sdp_get_uuid(uint8_t **, uint8_t *, uuid_t *); +bool _sdp_get_seq(uint8_t **, uint8_t *, uint8_t **); +bool _sdp_get_uint16(uint8_t **, uint8_t *, uint16_t *); diff --git a/usr.sbin/bluetooth/btpand/server.c b/usr.sbin/bluetooth/btpand/server.c new file mode 100644 index 0000000000000..5600c3b144033 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/server.c @@ -0,0 +1,286 @@ +/*	$NetBSD: server.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: server.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); + +#include <sys/ioctl.h> + +#include <bluetooth.h> +#include <errno.h> +#include <sdp.h> +#include <unistd.h> + +#include "btpand.h" +#include "bnep.h" + +static struct event	server_ev; +static int		server_fd; +static int		server_load; + +static void *		server_ss; +static uint32_t		server_handle; + +static void server_open(void); +static void server_close(void); +static void server_read(int, short, void *); +static void server_register(void); + +void +server_init(void) +{ + +	server_fd = -1; +} + +/* + * The server_update() function is called whenever the channel count is + * changed. We maintain the SDP record and open or close the server socket + * as required. + */ +void +server_update(int count) +{ + +	if (server_limit == 0) +		return; + +	log_debug("count %d", count); + +	server_load = (count - 1) * 100 / server_limit; +	log_info("server_load: %d%%", server_load); + +	if (server_load > 99 && server_fd != -1) +		server_close(); + +	if (server_load < 100 && server_fd == -1) +		server_open(); + +	if (service_name) +		server_register(); +} + +static void +server_open(void) +{ +	struct sockaddr_l2cap sa; +	uint16_t mru; + +	server_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP); +	if (server_fd == -1) { +		log_err("Could not open L2CAP socket: %m"); +		exit(EXIT_FAILURE); +	} + +	memset(&sa, 0, sizeof(sa)); +	sa.l2cap_family = AF_BLUETOOTH; +	sa.l2cap_len = sizeof(sa); +	sa.l2cap_psm = htole16(l2cap_psm); +	bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr); +	if (bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) { +		log_err("Could not bind server socket: %m"); +		exit(EXIT_FAILURE); +	} + +	mru = BNEP_MTU_MIN; +	if (setsockopt(server_fd, SOL_L2CAP, +	    SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) { +		log_err("Could not set L2CAP IMTU (%d): %m", mru); +		exit(EXIT_FAILURE); +	} + +	if (listen(server_fd, 0) == -1) { +		log_err("Could not listen on server socket: %m"); +		exit(EXIT_FAILURE); +	} + +	event_set(&server_ev, server_fd, EV_READ | EV_PERSIST, server_read, NULL); +	if (event_add(&server_ev, NULL) == -1) { +		log_err("Could not add server event: %m"); +		exit(EXIT_FAILURE); +	} + +	log_info("server socket open"); +} + +static void +server_close(void) +{ + +	event_del(&server_ev); +	close(server_fd); +	server_fd = -1; + +	log_info("server socket closed"); +} + +/* + * handle connection request + */ +static void +server_read(int s, short ev, void *arg) +{ +	struct sockaddr_l2cap ra, la; +	channel_t *chan; +	socklen_t len; +	int fd, n; +	uint16_t mru, mtu; + +	len = sizeof(ra); +	fd = accept(s, (struct sockaddr *)&ra, &len); +	if (fd == -1) +		return; + +	n = 1; +	if (ioctl(fd, FIONBIO, &n) == -1) { +		log_err("Could not set NonBlocking IO: %m"); +		close(fd); +		return; +	} + +	len = sizeof(mru); +	if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) { +		log_err("Could not get L2CAP IMTU: %m"); +		close(fd); +		return; +	} +	if(mru < BNEP_MTU_MIN) { +		log_err("L2CAP IMTU too small (%d)", mru); +		close(fd); +		return; +	} + +	len = sizeof(mtu); +	if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) { +		log_err("Could not get L2CAP OMTU: %m"); +		close(fd); +		return; +	} +	if (mtu < BNEP_MTU_MIN) { +		log_err("L2CAP OMTU too small (%d)", mtu); +		close(fd); +		return; +	} + +	len = sizeof(n); +	if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) { +		log_err("Could not get socket send buffer size: %m"); +		close(fd); +		return; +	} + +	if (n < (mtu * 2)) { +		n = mtu * 2; +		if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) { +			log_err("Could not set socket send buffer size (%d): %m", n); +			close(fd); +			return; +		} +	} + +	n = mtu; +	if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) { +		log_err("Could not set socket low water mark (%d): %m", n); +		close(fd); +		return; +	} + +	len = sizeof(la); +	if (getsockname(fd, (struct sockaddr *)&la, &len) == -1) { +		log_err("Could not get socket address: %m"); +		close(fd); +		return; +	} + +	log_info("Accepted connection from %s", bt_ntoa(&ra.l2cap_bdaddr, NULL)); + +	chan = channel_alloc(); +	if (chan == NULL) { +		close(fd); +		return; +	} + +	chan->send = bnep_send; +	chan->recv = bnep_recv; +	chan->mru = mru; +	chan->mtu = mtu; +	b2eaddr(chan->raddr, &ra.l2cap_bdaddr); +	b2eaddr(chan->laddr, &la.l2cap_bdaddr); +	chan->state = CHANNEL_WAIT_CONNECT_REQ; +	channel_timeout(chan, 10); +	if (!channel_open(chan, fd)) { +		chan->state = CHANNEL_CLOSED; +		channel_free(chan); +		close(fd); +		return; +	} +} + +static void +server_register(void) +{ +	sdp_nap_profile_t p; +	int rv; + +	if (server_ss == NULL) { +		server_ss = sdp_open_local(control_path); +		if (server_ss == NULL || sdp_error(server_ss) != 0) { +			log_err("failed to contact SDP server"); +			return; +		} +	} + +	memset(&p, 0, sizeof(p)); + +	p.psm = l2cap_psm; + +	if (server_load < 1)		p.load_factor = 0; +	else if (server_load <= 17)	p.load_factor = 1; +	else if (server_load <= 33)	p.load_factor = 2; +	else if (server_load <= 50)	p.load_factor = 3; +	else if (server_load <= 67)	p.load_factor = 4; +	else if (server_load <= 83)	p.load_factor = 5; +	else if (server_load <= 99)	p.load_factor = 6; +	else				p.load_factor = 7; + +	if (l2cap_mode != 0)		p.security_description = 0x0001; + +	if (server_handle) +		rv = sdp_change_service(server_ss, server_handle, +		    (uint8_t *)&p, sizeof(p)); +	else +		rv = sdp_register_service(server_ss, service_class, +		    &local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle); + +	if (rv != 0) { +		errno = sdp_error(server_ss); +		log_err("%s: %m", service_name); +		exit(EXIT_FAILURE); +	} +} diff --git a/usr.sbin/bluetooth/btpand/tap.c b/usr.sbin/bluetooth/btpand/tap.c new file mode 100644 index 0000000000000..c96563338ea20 --- /dev/null +++ b/usr.sbin/bluetooth/btpand/tap.c @@ -0,0 +1,167 @@ +/*	$NetBSD: tap.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/ + +/*- + * Copyright (c) 2008 Iain Hibbert + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* $FreeBSD$ */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: tap.c,v 1.1 2008/08/17 13:20:57 plunky Exp $"); + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/uio.h> + +#include <net/if_tap.h> + +#include <fcntl.h> +#include <libutil.h> +#include <paths.h> +#include <stdio.h> +#include <unistd.h> + +#include "btpand.h" + +static bool tap_send(channel_t *, packet_t *); +static bool tap_recv(packet_t *); + +void +tap_init(void) +{ +	channel_t *chan; +	struct ifreq ifr; +	int fd, s; +	char pidfile[PATH_MAX]; + +	fd = open(interface_name, O_RDWR); +	if (fd == -1) { +		log_err("Could not open \"%s\": %m", interface_name); +		exit(EXIT_FAILURE); +	} + +	memset(&ifr, 0, sizeof(ifr)); +	if (ioctl(fd, TAPGIFNAME, &ifr) == -1) { +		log_err("Could not get interface name: %m"); +		exit(EXIT_FAILURE); +	} + +	s = socket(AF_INET, SOCK_DGRAM, 0); +	if (s == -1) { +		log_err("Could not open PF_LINK socket: %m"); +		exit(EXIT_FAILURE); +	} + +	ifr.ifr_addr.sa_family = AF_LINK; +	ifr.ifr_addr.sa_len = ETHER_ADDR_LEN; +	b2eaddr(ifr.ifr_addr.sa_data, &local_bdaddr); + +	if (ioctl(s, SIOCSIFLLADDR, &ifr) == -1) { +		log_err("Could not set %s physical address: %m", ifr.ifr_name); +		exit(EXIT_FAILURE); +	} + +	if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) { +		log_err("Could not get interface flags: %m"); +		exit(EXIT_FAILURE); +	} + +	if ((ifr.ifr_flags & IFF_UP) == 0) { +		ifr.ifr_flags |= IFF_UP; + +		if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) { +			log_err("Could not set IFF_UP: %m"); +			exit(EXIT_FAILURE); +		} +	} + +	close(s); + +	log_info("Using interface %s with addr %s", ifr.ifr_name, +		ether_ntoa((struct ether_addr *)&ifr.ifr_addr.sa_data)); + +	chan = channel_alloc(); +	if (chan == NULL) +		exit(EXIT_FAILURE); + +	chan->send = tap_send; +	chan->recv = tap_recv; +	chan->mru = ETHER_HDR_LEN + ETHER_MAX_LEN; +	memcpy(chan->raddr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN); +	memcpy(chan->laddr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN); +	chan->state = CHANNEL_OPEN; +	if (!channel_open(chan, fd)) +		exit(EXIT_FAILURE); + +	snprintf(pidfile, sizeof(pidfile), "%s/%s.pid", +		_PATH_VARRUN, ifr.ifr_name); +	chan->pfh = pidfile_open(pidfile, 0600, NULL); +	if (chan->pfh == NULL) +		log_err("can't create pidfile"); +	else if (pidfile_write(chan->pfh) < 0) { +		log_err("can't write pidfile"); +		pidfile_remove(chan->pfh); +		chan->pfh = NULL; +	} +} + +static bool +tap_send(channel_t *chan, packet_t *pkt) +{ +	struct iovec iov[4]; +	ssize_t nw; + +	iov[0].iov_base = pkt->dst; +	iov[0].iov_len = ETHER_ADDR_LEN; +	iov[1].iov_base = pkt->src; +	iov[1].iov_len = ETHER_ADDR_LEN; +	iov[2].iov_base = pkt->type; +	iov[2].iov_len = ETHER_TYPE_LEN; +	iov[3].iov_base = pkt->ptr; +	iov[3].iov_len = pkt->len; + +	/* tap device write never fails */ +	nw = writev(chan->fd, iov, __arraycount(iov)); +	assert(nw > 0); + +	return true; +} + +static bool +tap_recv(packet_t *pkt) +{ + +	if (pkt->len < ETHER_HDR_LEN) +		return false; + +	pkt->dst = pkt->ptr; +	packet_adj(pkt, ETHER_ADDR_LEN); +	pkt->src = pkt->ptr; +	packet_adj(pkt, ETHER_ADDR_LEN); +	pkt->type = pkt->ptr; +	packet_adj(pkt, ETHER_TYPE_LEN); + +	return true; +} | 
