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