diff options
author | emax <emax@FreeBSD.org> | 2009-01-30 22:23:21 +0000 |
---|---|---|
committer | emax <emax@FreeBSD.org> | 2009-01-30 22:23:21 +0000 |
commit | 8d4db6264215552d797a29eecb20e98ee8ef8624 (patch) | |
tree | 4fa9389ed8e1b66c4642e880ab1430668fa57085 | |
parent | 40c57b103cbbac9193f3cfcc5bd0d87e7936534f (diff) | |
download | FreeBSD-src-8d4db6264215552d797a29eecb20e98ee8ef8624.zip FreeBSD-src-8d4db6264215552d797a29eecb20e98ee8ef8624.tar.gz |
Add btpand(8) daemon from NetBSD. This daemon provides support for
Bluetooth Network Access Point (NAP), Group Ad-hoc Network (GN) and
Personal Area Network User (PANU) profiles.
Obtained from: NetBSD
MFC after: 1 month
-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 0000000..5e4bb0b --- /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 0000000..200a723 --- /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 0000000..2fc6a77 --- /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 0000000..ece16c2 --- /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 0000000..57ee133 --- /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 0000000..23a9c76 --- /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 0000000..b4eb4ab --- /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 0000000..97064db --- /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 0000000..253084e --- /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 0000000..75515e3 --- /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 0000000..e42e5c5 --- /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 0000000..e5aec1c --- /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 0000000..32dd95d --- /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 0000000..5600c3b --- /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 0000000..c965633 --- /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; +} |