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
This commit is contained in:
emax 2009-01-30 22:23:21 +00:00
parent 40c57b103c
commit 8d4db62642
15 changed files with 3372 additions and 0 deletions

View File

@ -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>

View File

@ -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;
}

View File

@ -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

View File

@ -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.

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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(&current);
/*
* 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(&current)) {
ev = TAILQ_FIRST(&current);
__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(&current, 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(&current, 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);
}

View File

@ -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_ */

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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 *);

View File

@ -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);
}
}

View File

@ -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;
}