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