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