Home | History | Annotate | Line # | Download | only in btpand
bnep.c revision 1.1
      1 /*	$NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2008 Iain Hibbert
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __RCSID("$NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
     30 
     31 #include <bluetooth.h>
     32 #include <sdp.h>
     33 #include <stdarg.h>
     34 #include <string.h>
     35 
     36 #include "btpand.h"
     37 #include "bnep.h"
     38 
     39 static bool bnep_recv_extension(packet_t *);
     40 static size_t bnep_recv_control(channel_t *, uint8_t *, size_t, bool);
     41 static size_t bnep_recv_control_command_not_understood(channel_t *, uint8_t *, size_t);
     42 static size_t bnep_recv_setup_connection_req(channel_t *, uint8_t *, size_t);
     43 static size_t bnep_recv_setup_connection_rsp(channel_t *, uint8_t *, size_t);
     44 static size_t bnep_recv_filter_net_type_set(channel_t *, uint8_t *, size_t);
     45 static size_t bnep_recv_filter_net_type_rsp(channel_t *, uint8_t *, size_t);
     46 static size_t bnep_recv_filter_multi_addr_set(channel_t *, uint8_t *, size_t);
     47 static size_t bnep_recv_filter_multi_addr_rsp(channel_t *, uint8_t *, size_t);
     48 
     49 static bool bnep_pfilter(channel_t *, packet_t *);
     50 static bool bnep_mfilter(channel_t *, packet_t *);
     51 
     52 static uint8_t NAP_UUID[] = {
     53 	0x00, 0x00, 0x11, 0x16,
     54 	0x00, 0x00,
     55 	0x10, 0x00,
     56 	0x80, 0x00,
     57 	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
     58 };
     59 
     60 static uint8_t GN_UUID[] = {
     61 	0x00, 0x00, 0x11, 0x17,
     62 	0x00, 0x00,
     63 	0x10, 0x00,
     64 	0x80, 0x00,
     65 	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
     66 };
     67 
     68 static uint8_t PANU_UUID[] = {
     69 	0x00, 0x00, 0x11, 0x15,
     70 	0x00, 0x00,
     71 	0x10, 0x00,
     72 	0x80, 0x00,
     73 	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
     74 };
     75 
     76 /*
     77  * receive BNEP packet
     78  * return true if packet is to be forwarded
     79  */
     80 bool
     81 bnep_recv(packet_t *pkt)
     82 {
     83 	size_t len;
     84 	uint8_t type;
     85 
     86 	if (pkt->len < 1)
     87 		return false;
     88 
     89 	type = pkt->ptr[0];
     90 	packet_adj(pkt, 1);
     91 
     92 	switch (BNEP_TYPE(type)) {
     93 	case BNEP_GENERAL_ETHERNET:
     94 		if (pkt->len < (ETHER_ADDR_LEN * 2) + ETHER_TYPE_LEN) {
     95 			log_debug("dropped short packet (type 0x%2.2x)", type);
     96 			return false;
     97 		}
     98 
     99 		pkt->dst = pkt->ptr;
    100 		packet_adj(pkt, ETHER_ADDR_LEN);
    101 		pkt->src = pkt->ptr;
    102 		packet_adj(pkt, ETHER_ADDR_LEN);
    103 		pkt->type = pkt->ptr;
    104 		packet_adj(pkt, ETHER_TYPE_LEN);
    105 		break;
    106 
    107 	case BNEP_CONTROL:
    108 		len = bnep_recv_control(pkt->chan, pkt->ptr, pkt->len, false);
    109 		if (len == 0)
    110 			return false;
    111 
    112 		packet_adj(pkt, len);
    113 		break;
    114 
    115 	case BNEP_COMPRESSED_ETHERNET:
    116 		if (pkt->len < ETHER_TYPE_LEN) {
    117 			log_debug("dropped short packet (type 0x%2.2x)", type);
    118 			return false;
    119 		}
    120 
    121 		pkt->dst = pkt->chan->laddr;
    122 		pkt->src = pkt->chan->raddr;
    123 		pkt->type = pkt->ptr;
    124 		packet_adj(pkt, ETHER_TYPE_LEN);
    125 		break;
    126 
    127 	case BNEP_COMPRESSED_ETHERNET_SRC_ONLY:
    128 		if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) {
    129 			log_debug("dropped short packet (type 0x%2.2x)", type);
    130 			return false;
    131 		}
    132 
    133 		pkt->dst = pkt->chan->laddr;
    134 		pkt->src = pkt->ptr;
    135 		packet_adj(pkt, ETHER_ADDR_LEN);
    136 		pkt->type = pkt->ptr;
    137 		packet_adj(pkt, ETHER_TYPE_LEN);
    138 		break;
    139 
    140 	case BNEP_COMPRESSED_ETHERNET_DST_ONLY:
    141 		if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) {
    142 			log_debug("dropped short packet (type 0x%2.2x)", type);
    143 			return false;
    144 		}
    145 
    146 		pkt->dst = pkt->ptr;
    147 		packet_adj(pkt, ETHER_ADDR_LEN);
    148 		pkt->src = pkt->chan->raddr;
    149 		pkt->type = pkt->ptr;
    150 		packet_adj(pkt, ETHER_TYPE_LEN);
    151 		break;
    152 
    153 	default:
    154 		/*
    155 		 * Any packet containing a reserved BNEP
    156 		 * header packet type SHALL be dropped.
    157 		 */
    158 
    159 		log_debug("dropped packet with reserved type 0x%2.2x", type);
    160 		return false;
    161 	}
    162 
    163 	if (BNEP_TYPE_EXT(type)
    164 	    && !bnep_recv_extension(pkt))
    165 		return false;	/* invalid extensions */
    166 
    167 	if (BNEP_TYPE(type) == BNEP_CONTROL
    168 	    || pkt->chan->state != CHANNEL_OPEN)
    169 		return false;	/* no forwarding */
    170 
    171 	return true;
    172 }
    173 
    174 static bool
    175 bnep_recv_extension(packet_t *pkt)
    176 {
    177 	exthdr_t *eh;
    178 	size_t len;
    179 	uint8_t type, size;
    180 
    181 	do {
    182 		if (pkt->len < 2)
    183 			return false;
    184 
    185 		type = pkt->ptr[0];
    186 		size = pkt->ptr[1];
    187 
    188 		if (pkt->len < size + 2)
    189 			return false;
    190 
    191 		switch (type) {
    192 		case BNEP_EXTENSION_CONTROL:
    193 			len = bnep_recv_control(pkt->chan, pkt->ptr + 2, size, true);
    194 			if (len != size)
    195 				log_err("ignored spurious data in exthdr");
    196 
    197 			break;
    198 
    199 		default:
    200 			/* Unknown extension headers in data packets	 */
    201 			/* SHALL be forwarded irrespective of any	 */
    202 			/* network protocol or multicast filter settings */
    203 			/* and any local filtering policy.		 */
    204 
    205 			eh = malloc(sizeof(exthdr_t));
    206 			if (eh == NULL) {
    207 				log_err("exthdr malloc() failed: %m");
    208 				break;
    209 			}
    210 
    211 			eh->ptr = pkt->ptr;
    212 			eh->len = size;
    213 			STAILQ_INSERT_TAIL(&pkt->extlist, eh, next);
    214 			break;
    215 		}
    216 
    217 		packet_adj(pkt, size + 2);
    218 	} while (BNEP_TYPE_EXT(type));
    219 
    220 	return true;
    221 }
    222 
    223 static size_t
    224 bnep_recv_control(channel_t *chan, uint8_t *ptr, size_t size, bool isext)
    225 {
    226 	uint8_t type;
    227 	size_t len;
    228 
    229 	if (size-- < 1)
    230 		return 0;
    231 
    232 	type = *ptr++;
    233 
    234 	switch (type) {
    235 	case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD:
    236 		len = bnep_recv_control_command_not_understood(chan, ptr, size);
    237 		break;
    238 
    239 	case BNEP_SETUP_CONNECTION_REQUEST:
    240 		if (isext)
    241 			return 0;	/* not allowed in extension headers */
    242 
    243 		len = bnep_recv_setup_connection_req(chan, ptr, size);
    244 		break;
    245 
    246 	case BNEP_SETUP_CONNECTION_RESPONSE:
    247 		if (isext)
    248 			return 0;	/* not allowed in extension headers */
    249 
    250 		len = bnep_recv_setup_connection_rsp(chan, ptr, size);
    251 		break;
    252 
    253 	case BNEP_FILTER_NET_TYPE_SET:
    254 		len = bnep_recv_filter_net_type_set(chan, ptr, size);
    255 		break;
    256 
    257 	case BNEP_FILTER_NET_TYPE_RESPONSE:
    258 		len = bnep_recv_filter_net_type_rsp(chan, ptr, size);
    259 		break;
    260 
    261 	case BNEP_FILTER_MULTI_ADDR_SET:
    262 		len = bnep_recv_filter_multi_addr_set(chan, ptr, size);
    263 		break;
    264 
    265 	case BNEP_FILTER_MULTI_ADDR_RESPONSE:
    266 		len = bnep_recv_filter_multi_addr_rsp(chan, ptr, size);
    267 		break;
    268 
    269 	default:
    270 		len = 0;
    271 		break;
    272 	}
    273 
    274 	if (len == 0)
    275 		bnep_send_control(chan, BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD, type);
    276 
    277 	return len;
    278 }
    279 
    280 static size_t
    281 bnep_recv_control_command_not_understood(channel_t *chan, uint8_t *ptr, size_t size)
    282 {
    283 	uint8_t type;
    284 
    285 	if (size < 1)
    286 		return 0;
    287 
    288 	type = *ptr++;
    289 	log_err("received Control Command Not Understood (0x%2.2x)", type);
    290 
    291 	/* we didn't send any reserved commands, just cut them off */
    292 	channel_close(chan);
    293 
    294 	return 1;
    295 }
    296 
    297 static size_t
    298 bnep_recv_setup_connection_req(channel_t *chan, uint8_t *ptr, size_t size)
    299 {
    300 	uint8_t len, off;
    301 	int src, dst, rsp;
    302 
    303 	if (size < 1)
    304 		return 0;
    305 
    306 	len = *ptr++;
    307 	if (size < (len * 2 + 1))
    308 		return 0;
    309 
    310 	if (chan->state != CHANNEL_WAIT_CONNECT_REQ
    311 	    && chan->state != CHANNEL_OPEN) {
    312 		log_debug("ignored");
    313 		return (len * 2 + 1);
    314 	}
    315 
    316 	if (len == 2)
    317 		off = 2;
    318 	else if (len == 4)
    319 		off = 0;
    320 	else if (len == 16)
    321 		off = 0;
    322 	else {
    323 		rsp = BNEP_SETUP_INVALID_UUID_SIZE;
    324 		goto done;
    325 	}
    326 
    327 	if (memcmp(ptr, NAP_UUID + off, len) == 0)
    328 		dst = SDP_SERVICE_CLASS_NAP;
    329 	else if (memcmp(ptr, GN_UUID + off, len) == 0)
    330 		dst = SDP_SERVICE_CLASS_GN;
    331 	else if (memcmp(ptr, PANU_UUID + off, len) == 0)
    332 		dst = SDP_SERVICE_CLASS_PANU;
    333 	else
    334 		dst = 0;
    335 
    336 	if (dst != service_class) {
    337 		rsp = BNEP_SETUP_INVALID_DST_UUID;
    338 		goto done;
    339 	}
    340 
    341 	ptr += len;
    342 
    343 	if (memcmp(ptr, PANU_UUID + off, len) == 0)
    344 		src = SDP_SERVICE_CLASS_NAP;
    345 	else if (memcmp(ptr, GN_UUID + off, len) == 0)
    346 		src = SDP_SERVICE_CLASS_GN;
    347 	else if (memcmp(ptr, NAP_UUID + off, len) == 0)
    348 		src = SDP_SERVICE_CLASS_PANU;
    349 	else
    350 		src = 0;
    351 
    352 	if ((dst != SDP_SERVICE_CLASS_PANU && src != SDP_SERVICE_CLASS_PANU)
    353 	    || src == 0) {
    354 		rsp = BNEP_SETUP_INVALID_SRC_UUID;
    355 		goto done;
    356 	}
    357 
    358 	rsp = BNEP_SETUP_SUCCESS;
    359 	chan->state = CHANNEL_OPEN;
    360 	channel_timeout(chan, 0);
    361 
    362 done:
    363 	log_debug("addr %s response 0x%2.2x",
    364 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
    365 
    366 	bnep_send_control(chan, BNEP_SETUP_CONNECTION_RESPONSE, rsp);
    367 	return (len * 2 + 1);
    368 }
    369 
    370 static size_t
    371 bnep_recv_setup_connection_rsp(channel_t *chan, uint8_t *ptr, size_t size)
    372 {
    373 	int rsp;
    374 
    375 	if (size < 2)
    376 		return 0;
    377 
    378 	rsp = be16dec(ptr);
    379 
    380 	if (chan->state != CHANNEL_WAIT_CONNECT_RSP) {
    381 		log_debug("ignored");
    382 		return 2;
    383 	}
    384 
    385 	log_debug("addr %s response 0x%2.2x",
    386 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
    387 
    388 	if (rsp == BNEP_SETUP_SUCCESS) {
    389 		chan->state = CHANNEL_OPEN;
    390 		channel_timeout(chan, 0);
    391 	} else {
    392 		channel_close(chan);
    393 	}
    394 
    395 	return 2;
    396 }
    397 
    398 static size_t
    399 bnep_recv_filter_net_type_set(channel_t *chan, uint8_t *ptr, size_t size)
    400 {
    401 	pfilter_t *pf;
    402 	int i, len, nf, rsp;
    403 
    404 	if (size < 2)
    405 		return 0;
    406 
    407 	len = be16dec(ptr);
    408 	ptr += 2;
    409 
    410 	if (size < (len + 2))
    411 		return 0;
    412 
    413 	if (chan->state != CHANNEL_OPEN) {
    414 		log_debug("ignored");
    415 		return (len + 2);
    416 	}
    417 
    418 	nf = len / 4;
    419 	pf = malloc(nf * sizeof(pfilter_t));
    420 	if (pf == NULL) {
    421 		rsp = BNEP_FILTER_TOO_MANY_FILTERS;
    422 		goto done;
    423 	}
    424 
    425 	log_debug("nf = %d", nf);
    426 
    427 	for (i = 0; i < nf; i++) {
    428 		pf[i].start = be16dec(ptr);
    429 		ptr += 2;
    430 		pf[i].end = be16dec(ptr);
    431 		ptr += 2;
    432 
    433 		if (pf[i].start > pf[i].end) {
    434 			free(pf);
    435 			rsp = BNEP_FILTER_INVALID_RANGE;
    436 			goto done;
    437 		}
    438 
    439 		log_debug("pf[%d] = %#4.4x, %#4.4x", i, pf[i].start, pf[i].end);
    440 	}
    441 
    442 	if (chan->pfilter)
    443 		free(chan->pfilter);
    444 
    445 	chan->pfilter = pf;
    446 	chan->npfilter = nf;
    447 
    448 	rsp = BNEP_FILTER_SUCCESS;
    449 
    450 done:
    451 	log_debug("addr %s response 0x%2.2x",
    452 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
    453 
    454 	bnep_send_control(chan, BNEP_FILTER_NET_TYPE_RESPONSE, rsp);
    455 	return (len + 2);
    456 }
    457 
    458 static size_t
    459 bnep_recv_filter_net_type_rsp(channel_t *chan, uint8_t *ptr, size_t size)
    460 {
    461 	int rsp;
    462 
    463 	if (size < 2)
    464 		return 0;
    465 
    466 	if (chan->state != CHANNEL_OPEN) {
    467 		log_debug("ignored");
    468 		return 2;
    469 	}
    470 
    471 	rsp = be16dec(ptr);
    472 
    473 	log_debug("addr %s response 0x%2.2x",
    474 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
    475 
    476 	/* we did not send any filter_net_type_set message */
    477 	return 2;
    478 }
    479 
    480 static size_t
    481 bnep_recv_filter_multi_addr_set(channel_t *chan, uint8_t *ptr, size_t size)
    482 {
    483 	mfilter_t *mf;
    484 	int i, len, nf, rsp;
    485 
    486 	if (size < 2)
    487 		return 0;
    488 
    489 	len = be16dec(ptr);
    490 	ptr += 2;
    491 
    492 	if (size < (len + 2))
    493 		return 0;
    494 
    495 	if (chan->state != CHANNEL_OPEN) {
    496 		log_debug("ignored");
    497 		return (len + 2);
    498 	}
    499 
    500 	nf = len / (ETHER_ADDR_LEN * 2);
    501 	mf = malloc(nf * sizeof(mfilter_t));
    502 	if (mf == NULL) {
    503 		rsp = BNEP_FILTER_TOO_MANY_FILTERS;
    504 		goto done;
    505 	}
    506 
    507 	log_debug("nf = %d", nf);
    508 
    509 	for (i = 0; i < nf; i++) {
    510 		memcpy(mf[i].start, ptr, ETHER_ADDR_LEN);
    511 		ptr += ETHER_ADDR_LEN;
    512 
    513 		memcpy(mf[i].end, ptr, ETHER_ADDR_LEN);
    514 		ptr += ETHER_ADDR_LEN;
    515 
    516 		if (memcmp(mf[i].start, mf[i].end, ETHER_ADDR_LEN) > 0) {
    517 			free(mf);
    518 			rsp = BNEP_FILTER_INVALID_RANGE;
    519 			goto done;
    520 		}
    521 
    522 		log_debug("pf[%d] = "
    523 		    "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
    524 		    "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", i,
    525 		    mf[i].start[0], mf[i].start[1], mf[i].start[2],
    526 		    mf[i].start[3], mf[i].start[4], mf[i].start[5],
    527 		    mf[i].end[0], mf[i].end[1], mf[i].end[2],
    528 		    mf[i].end[3], mf[i].end[4], mf[i].end[5]);
    529 	}
    530 
    531 	if (chan->mfilter)
    532 		free(chan->mfilter);
    533 
    534 	chan->mfilter = mf;
    535 	chan->nmfilter = nf;
    536 
    537 	rsp = BNEP_FILTER_SUCCESS;
    538 
    539 done:
    540 	log_debug("addr %s response 0x%2.2x",
    541 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
    542 
    543 	bnep_send_control(chan, BNEP_FILTER_MULTI_ADDR_RESPONSE, rsp);
    544 	return (len + 2);
    545 }
    546 
    547 static size_t
    548 bnep_recv_filter_multi_addr_rsp(channel_t *chan, uint8_t *ptr, size_t size)
    549 {
    550 	int rsp;
    551 
    552 	if (size < 2)
    553 		return false;
    554 
    555 	if (chan->state != CHANNEL_OPEN) {
    556 		log_debug("ignored");
    557 		return 2;
    558 	}
    559 
    560 	rsp = be16dec(ptr);
    561 	log_debug("addr %s response 0x%2.2x",
    562 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
    563 
    564 	/* we did not send any filter_multi_addr_set message */
    565 	return 2;
    566 }
    567 
    568 void
    569 bnep_send_control(channel_t *chan, uint8_t type, ...)
    570 {
    571 	packet_t *pkt;
    572 	uint8_t *p;
    573 	va_list ap;
    574 
    575 	_DIAGASSERT(chan->state != CHANNEL_CLOSED);
    576 
    577 	pkt = packet_alloc(chan);
    578 	if (pkt == NULL)
    579 		return;
    580 
    581 	p = pkt->ptr;
    582 	va_start(ap, type);
    583 
    584 	*p++ = BNEP_CONTROL;
    585 	*p++ = type;
    586 
    587 	switch(type) {
    588 	case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD:
    589 		*p++ = va_arg(ap, int);
    590 		break;
    591 
    592 	case BNEP_SETUP_CONNECTION_REQUEST:
    593 		*p++ = va_arg(ap, int);
    594 		be16enc(p, va_arg(ap, int));
    595 		p += 2;
    596 		be16enc(p, va_arg(ap, int));
    597 		p += 2;
    598 		break;
    599 
    600 	case BNEP_SETUP_CONNECTION_RESPONSE:
    601 	case BNEP_FILTER_NET_TYPE_RESPONSE:
    602 	case BNEP_FILTER_MULTI_ADDR_RESPONSE:
    603 		be16enc(p, va_arg(ap, int));
    604 		p += 2;
    605 		break;
    606 
    607 	case BNEP_FILTER_NET_TYPE_SET:		/* TODO */
    608 	case BNEP_FILTER_MULTI_ADDR_SET:	/* TODO */
    609 	default:
    610 		log_err("Can't send control type 0x%2.2x", type);
    611 		break;
    612 	}
    613 
    614 	va_end(ap);
    615 	pkt->len = p - pkt->ptr;
    616 
    617 	channel_put(chan, pkt);
    618 	packet_free(pkt);
    619 }
    620 
    621 /*
    622  * BNEP send packet routine
    623  * return true if packet can be removed from queue
    624  */
    625 bool
    626 bnep_send(channel_t *chan, packet_t *pkt)
    627 {
    628 	struct iovec iov[2];
    629 	uint8_t *p, *type, *proto;
    630 	exthdr_t *eh;
    631 	bool src, dst;
    632 	size_t nw;
    633 
    634 	if (pkt->type == NULL) {
    635 		iov[0].iov_base = pkt->ptr;
    636 		iov[0].iov_len = pkt->len;
    637 		iov[1].iov_base = NULL;
    638 		iov[1].iov_len = 0;
    639 	} else {
    640 		p = chan->sendbuf;
    641 
    642 		dst = (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) != 0);
    643 		src = (memcmp(pkt->src, chan->laddr, ETHER_ADDR_LEN) != 0);
    644 
    645 		type = p;
    646 		p += 1;
    647 
    648 		if (dst && src)
    649 			*type = BNEP_GENERAL_ETHERNET;
    650 		else if (dst && !src)
    651 			*type = BNEP_COMPRESSED_ETHERNET_DST_ONLY;
    652 		else if (!dst && src)
    653 			*type = BNEP_COMPRESSED_ETHERNET_SRC_ONLY;
    654 		else /* (!dst && !src) */
    655 			*type = BNEP_COMPRESSED_ETHERNET;
    656 
    657 		if (dst) {
    658 			memcpy(p, pkt->dst, ETHER_ADDR_LEN);
    659 			p += ETHER_ADDR_LEN;
    660 		}
    661 
    662 		if (src) {
    663 			memcpy(p, pkt->src, ETHER_ADDR_LEN);
    664 			p += ETHER_ADDR_LEN;
    665 		}
    666 
    667 		proto = p;
    668 		memcpy(p, pkt->type, ETHER_TYPE_LEN);
    669 		p += ETHER_TYPE_LEN;
    670 
    671 		STAILQ_FOREACH(eh, &pkt->extlist, next) {
    672 			if (p + eh->len > chan->sendbuf + chan->mtu)
    673 				break;
    674 
    675 			*type |= BNEP_EXT;
    676 			type = p;
    677 
    678 			memcpy(p, eh->ptr, eh->len);
    679 			p += eh->len;
    680 		}
    681 
    682 		*type &= ~BNEP_EXT;
    683 
    684 		iov[0].iov_base = chan->sendbuf;
    685 		iov[0].iov_len = (p - chan->sendbuf);
    686 
    687 		if ((chan->npfilter == 0 || bnep_pfilter(chan, pkt))
    688 		    && (chan->nmfilter == 0 || bnep_mfilter(chan, pkt))) {
    689 			iov[1].iov_base = pkt->ptr;
    690 			iov[1].iov_len = pkt->len;
    691 		} else if (be16dec(proto) == ETHERTYPE_VLAN
    692 		    && pkt->len >= ETHER_VLAN_ENCAP_LEN) {
    693 			iov[1].iov_base = pkt->ptr;
    694 			iov[1].iov_len = ETHER_VLAN_ENCAP_LEN;
    695 		} else {
    696 			iov[1].iov_base = NULL;
    697 			iov[1].iov_len = 0;
    698 			memset(proto, 0, ETHER_TYPE_LEN);
    699 		}
    700 	}
    701 
    702 	if (iov[0].iov_len + iov[1].iov_len > chan->mtu) {
    703 		log_err("packet exceeded MTU (dropped)");
    704 		return false;
    705 	}
    706 
    707 	nw = writev(chan->fd, iov, __arraycount(iov));
    708 	return (nw > 0);
    709 }
    710 
    711 static bool
    712 bnep_pfilter(channel_t *chan, packet_t *pkt)
    713 {
    714 	int proto, i;
    715 
    716 	proto = be16dec(pkt->type);
    717 	if (proto == ETHERTYPE_VLAN) {	/* IEEE 802.1Q tag header */
    718 		if (pkt->len < 4)
    719 			return false;
    720 
    721 		proto = be16dec(pkt->ptr + 2);
    722 	}
    723 
    724 	for (i = 0; i < chan->npfilter; i++) {
    725 		if (chan->pfilter[i].start <= proto
    726 		    && chan->pfilter[i].end >=proto)
    727 			return true;
    728 	}
    729 
    730 	return false;
    731 }
    732 
    733 static bool
    734 bnep_mfilter(channel_t *chan, packet_t *pkt)
    735 {
    736 	int i;
    737 
    738 	if (!ETHER_IS_MULTICAST(pkt->dst))
    739 		return true;
    740 
    741 	for (i = 0; i < chan->nmfilter; i++) {
    742 		if (memcmp(pkt->dst, chan->mfilter[i].start, ETHER_ADDR_LEN) >= 0
    743 		    && memcmp(pkt->dst, chan->mfilter[i].end, ETHER_ADDR_LEN) <= 0)
    744 			return true;
    745 	}
    746 
    747 	return false;
    748 }
    749