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:
parent
40c57b103c
commit
8d4db62642
13
usr.sbin/bluetooth/btpand/Makefile
Normal file
13
usr.sbin/bluetooth/btpand/Makefile
Normal 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>
|
755
usr.sbin/bluetooth/btpand/bnep.c
Normal file
755
usr.sbin/bluetooth/btpand/bnep.c
Normal 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;
|
||||
}
|
72
usr.sbin/bluetooth/btpand/bnep.h
Normal file
72
usr.sbin/bluetooth/btpand/bnep.h
Normal 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
|
241
usr.sbin/bluetooth/btpand/btpand.8
Normal file
241
usr.sbin/bluetooth/btpand/btpand.8
Normal 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.
|
290
usr.sbin/bluetooth/btpand/btpand.c
Normal file
290
usr.sbin/bluetooth/btpand/btpand.c
Normal 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);
|
||||
}
|
208
usr.sbin/bluetooth/btpand/btpand.h
Normal file
208
usr.sbin/bluetooth/btpand/btpand.h
Normal 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);
|
335
usr.sbin/bluetooth/btpand/channel.c
Normal file
335
usr.sbin/bluetooth/btpand/channel.c
Normal 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);
|
||||
}
|
||||
}
|
192
usr.sbin/bluetooth/btpand/client.c
Normal file
192
usr.sbin/bluetooth/btpand/client.c
Normal 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);
|
||||
}
|
309
usr.sbin/bluetooth/btpand/event.c
Normal file
309
usr.sbin/bluetooth/btpand/event.c
Normal 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(¤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);
|
||||
}
|
||||
|
147
usr.sbin/bluetooth/btpand/event.h
Normal file
147
usr.sbin/bluetooth/btpand/event.h
Normal 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_ */
|
110
usr.sbin/bluetooth/btpand/packet.c
Normal file
110
usr.sbin/bluetooth/btpand/packet.c
Normal 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);
|
||||
}
|
209
usr.sbin/bluetooth/btpand/sdp.c
Normal file
209
usr.sbin/bluetooth/btpand/sdp.c
Normal 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;
|
||||
}
|
38
usr.sbin/bluetooth/btpand/sdp.h
Normal file
38
usr.sbin/bluetooth/btpand/sdp.h
Normal 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 *);
|
286
usr.sbin/bluetooth/btpand/server.c
Normal file
286
usr.sbin/bluetooth/btpand/server.c
Normal 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);
|
||||
}
|
||||
}
|
167
usr.sbin/bluetooth/btpand/tap.c
Normal file
167
usr.sbin/bluetooth/btpand/tap.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user