xref: /freebsd/usr.sbin/bluetooth/btpand/bnep.c (revision 2a63c3be158216222d89a073dcbd6a72ee4aab5a)
17718ced0SMaksim Yevmenkin /*	$NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $	*/
27718ced0SMaksim Yevmenkin 
37718ced0SMaksim Yevmenkin /*-
4*b61a5730SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
51de7b4b8SPedro F. Giffuni  *
67718ced0SMaksim Yevmenkin  * Copyright (c) 2008 Iain Hibbert
77718ced0SMaksim Yevmenkin  * All rights reserved.
87718ced0SMaksim Yevmenkin  *
97718ced0SMaksim Yevmenkin  * Redistribution and use in source and binary forms, with or without
107718ced0SMaksim Yevmenkin  * modification, are permitted provided that the following conditions
117718ced0SMaksim Yevmenkin  * are met:
127718ced0SMaksim Yevmenkin  * 1. Redistributions of source code must retain the above copyright
137718ced0SMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer.
147718ced0SMaksim Yevmenkin  * 2. Redistributions in binary form must reproduce the above copyright
157718ced0SMaksim Yevmenkin  *    notice, this list of conditions and the following disclaimer in the
167718ced0SMaksim Yevmenkin  *    documentation and/or other materials provided with the distribution.
177718ced0SMaksim Yevmenkin  *
187718ced0SMaksim Yevmenkin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
197718ced0SMaksim Yevmenkin  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
207718ced0SMaksim Yevmenkin  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
217718ced0SMaksim Yevmenkin  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
227718ced0SMaksim Yevmenkin  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
237718ced0SMaksim Yevmenkin  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
247718ced0SMaksim Yevmenkin  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
257718ced0SMaksim Yevmenkin  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
267718ced0SMaksim Yevmenkin  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
277718ced0SMaksim Yevmenkin  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
287718ced0SMaksim Yevmenkin  */
297718ced0SMaksim Yevmenkin 
307718ced0SMaksim Yevmenkin 
317718ced0SMaksim Yevmenkin #include <sys/cdefs.h>
327718ced0SMaksim Yevmenkin __RCSID("$NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
337718ced0SMaksim Yevmenkin 
347718ced0SMaksim Yevmenkin #include <sys/uio.h>
358d6f425dSTakanori Watanabe #define L2CAP_SOCKET_CHECKED
367718ced0SMaksim Yevmenkin #include <bluetooth.h>
377718ced0SMaksim Yevmenkin #include <sdp.h>
387718ced0SMaksim Yevmenkin #include <stdarg.h>
397718ced0SMaksim Yevmenkin #include <string.h>
407718ced0SMaksim Yevmenkin #include <unistd.h>
417718ced0SMaksim Yevmenkin 
427718ced0SMaksim Yevmenkin #include "btpand.h"
437718ced0SMaksim Yevmenkin #include "bnep.h"
447718ced0SMaksim Yevmenkin 
457718ced0SMaksim Yevmenkin static bool bnep_recv_extension(packet_t *);
467718ced0SMaksim Yevmenkin static size_t bnep_recv_control(channel_t *, uint8_t *, size_t, bool);
477718ced0SMaksim Yevmenkin static size_t bnep_recv_control_command_not_understood(channel_t *, uint8_t *, size_t);
487718ced0SMaksim Yevmenkin static size_t bnep_recv_setup_connection_req(channel_t *, uint8_t *, size_t);
497718ced0SMaksim Yevmenkin static size_t bnep_recv_setup_connection_rsp(channel_t *, uint8_t *, size_t);
507718ced0SMaksim Yevmenkin static size_t bnep_recv_filter_net_type_set(channel_t *, uint8_t *, size_t);
517718ced0SMaksim Yevmenkin static size_t bnep_recv_filter_net_type_rsp(channel_t *, uint8_t *, size_t);
527718ced0SMaksim Yevmenkin static size_t bnep_recv_filter_multi_addr_set(channel_t *, uint8_t *, size_t);
537718ced0SMaksim Yevmenkin static size_t bnep_recv_filter_multi_addr_rsp(channel_t *, uint8_t *, size_t);
547718ced0SMaksim Yevmenkin 
557718ced0SMaksim Yevmenkin static bool bnep_pfilter(channel_t *, packet_t *);
567718ced0SMaksim Yevmenkin static bool bnep_mfilter(channel_t *, packet_t *);
577718ced0SMaksim Yevmenkin 
587718ced0SMaksim Yevmenkin static uint8_t NAP_UUID[] = {
597718ced0SMaksim Yevmenkin 	0x00, 0x00, 0x11, 0x16,
607718ced0SMaksim Yevmenkin 	0x00, 0x00,
617718ced0SMaksim Yevmenkin 	0x10, 0x00,
627718ced0SMaksim Yevmenkin 	0x80, 0x00,
637718ced0SMaksim Yevmenkin 	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
647718ced0SMaksim Yevmenkin };
657718ced0SMaksim Yevmenkin 
667718ced0SMaksim Yevmenkin static uint8_t GN_UUID[] = {
677718ced0SMaksim Yevmenkin 	0x00, 0x00, 0x11, 0x17,
687718ced0SMaksim Yevmenkin 	0x00, 0x00,
697718ced0SMaksim Yevmenkin 	0x10, 0x00,
707718ced0SMaksim Yevmenkin 	0x80, 0x00,
717718ced0SMaksim Yevmenkin 	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
727718ced0SMaksim Yevmenkin };
737718ced0SMaksim Yevmenkin 
747718ced0SMaksim Yevmenkin static uint8_t PANU_UUID[] = {
757718ced0SMaksim Yevmenkin 	0x00, 0x00, 0x11, 0x15,
767718ced0SMaksim Yevmenkin 	0x00, 0x00,
777718ced0SMaksim Yevmenkin 	0x10, 0x00,
787718ced0SMaksim Yevmenkin 	0x80, 0x00,
797718ced0SMaksim Yevmenkin 	0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
807718ced0SMaksim Yevmenkin };
817718ced0SMaksim Yevmenkin 
827718ced0SMaksim Yevmenkin /*
837718ced0SMaksim Yevmenkin  * receive BNEP packet
847718ced0SMaksim Yevmenkin  * return true if packet is to be forwarded
857718ced0SMaksim Yevmenkin  */
867718ced0SMaksim Yevmenkin bool
bnep_recv(packet_t * pkt)877718ced0SMaksim Yevmenkin bnep_recv(packet_t *pkt)
887718ced0SMaksim Yevmenkin {
897718ced0SMaksim Yevmenkin 	size_t len;
907718ced0SMaksim Yevmenkin 	uint8_t type;
917718ced0SMaksim Yevmenkin 
927718ced0SMaksim Yevmenkin 	if (pkt->len < 1)
937718ced0SMaksim Yevmenkin 		return false;
947718ced0SMaksim Yevmenkin 
957718ced0SMaksim Yevmenkin 	type = pkt->ptr[0];
967718ced0SMaksim Yevmenkin 	packet_adj(pkt, 1);
977718ced0SMaksim Yevmenkin 
987718ced0SMaksim Yevmenkin 	switch (BNEP_TYPE(type)) {
997718ced0SMaksim Yevmenkin 	case BNEP_GENERAL_ETHERNET:
1007718ced0SMaksim Yevmenkin 		if (pkt->len < (ETHER_ADDR_LEN * 2) + ETHER_TYPE_LEN) {
1017718ced0SMaksim Yevmenkin 			log_debug("dropped short packet (type 0x%2.2x)", type);
1027718ced0SMaksim Yevmenkin 			return false;
1037718ced0SMaksim Yevmenkin 		}
1047718ced0SMaksim Yevmenkin 
1057718ced0SMaksim Yevmenkin 		pkt->dst = pkt->ptr;
1067718ced0SMaksim Yevmenkin 		packet_adj(pkt, ETHER_ADDR_LEN);
1077718ced0SMaksim Yevmenkin 		pkt->src = pkt->ptr;
1087718ced0SMaksim Yevmenkin 		packet_adj(pkt, ETHER_ADDR_LEN);
1097718ced0SMaksim Yevmenkin 		pkt->type = pkt->ptr;
1107718ced0SMaksim Yevmenkin 		packet_adj(pkt, ETHER_TYPE_LEN);
1117718ced0SMaksim Yevmenkin 		break;
1127718ced0SMaksim Yevmenkin 
1137718ced0SMaksim Yevmenkin 	case BNEP_CONTROL:
1147718ced0SMaksim Yevmenkin 		len = bnep_recv_control(pkt->chan, pkt->ptr, pkt->len, false);
1157718ced0SMaksim Yevmenkin 		if (len == 0)
1167718ced0SMaksim Yevmenkin 			return false;
1177718ced0SMaksim Yevmenkin 
1187718ced0SMaksim Yevmenkin 		packet_adj(pkt, len);
1197718ced0SMaksim Yevmenkin 		break;
1207718ced0SMaksim Yevmenkin 
1217718ced0SMaksim Yevmenkin 	case BNEP_COMPRESSED_ETHERNET:
1227718ced0SMaksim Yevmenkin 		if (pkt->len < ETHER_TYPE_LEN) {
1237718ced0SMaksim Yevmenkin 			log_debug("dropped short packet (type 0x%2.2x)", type);
1247718ced0SMaksim Yevmenkin 			return false;
1257718ced0SMaksim Yevmenkin 		}
1267718ced0SMaksim Yevmenkin 
1277718ced0SMaksim Yevmenkin 		pkt->dst = pkt->chan->laddr;
1287718ced0SMaksim Yevmenkin 		pkt->src = pkt->chan->raddr;
1297718ced0SMaksim Yevmenkin 		pkt->type = pkt->ptr;
1307718ced0SMaksim Yevmenkin 		packet_adj(pkt, ETHER_TYPE_LEN);
1317718ced0SMaksim Yevmenkin 		break;
1327718ced0SMaksim Yevmenkin 
1337718ced0SMaksim Yevmenkin 	case BNEP_COMPRESSED_ETHERNET_SRC_ONLY:
1347718ced0SMaksim Yevmenkin 		if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) {
1357718ced0SMaksim Yevmenkin 			log_debug("dropped short packet (type 0x%2.2x)", type);
1367718ced0SMaksim Yevmenkin 			return false;
1377718ced0SMaksim Yevmenkin 		}
1387718ced0SMaksim Yevmenkin 
1397718ced0SMaksim Yevmenkin 		pkt->dst = pkt->chan->laddr;
1407718ced0SMaksim Yevmenkin 		pkt->src = pkt->ptr;
1417718ced0SMaksim Yevmenkin 		packet_adj(pkt, ETHER_ADDR_LEN);
1427718ced0SMaksim Yevmenkin 		pkt->type = pkt->ptr;
1437718ced0SMaksim Yevmenkin 		packet_adj(pkt, ETHER_TYPE_LEN);
1447718ced0SMaksim Yevmenkin 		break;
1457718ced0SMaksim Yevmenkin 
1467718ced0SMaksim Yevmenkin 	case BNEP_COMPRESSED_ETHERNET_DST_ONLY:
1477718ced0SMaksim Yevmenkin 		if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) {
1487718ced0SMaksim Yevmenkin 			log_debug("dropped short packet (type 0x%2.2x)", type);
1497718ced0SMaksim Yevmenkin 			return false;
1507718ced0SMaksim Yevmenkin 		}
1517718ced0SMaksim Yevmenkin 
1527718ced0SMaksim Yevmenkin 		pkt->dst = pkt->ptr;
1537718ced0SMaksim Yevmenkin 		packet_adj(pkt, ETHER_ADDR_LEN);
1547718ced0SMaksim Yevmenkin 		pkt->src = pkt->chan->raddr;
1557718ced0SMaksim Yevmenkin 		pkt->type = pkt->ptr;
1567718ced0SMaksim Yevmenkin 		packet_adj(pkt, ETHER_TYPE_LEN);
1577718ced0SMaksim Yevmenkin 		break;
1587718ced0SMaksim Yevmenkin 
1597718ced0SMaksim Yevmenkin 	default:
1607718ced0SMaksim Yevmenkin 		/*
1617718ced0SMaksim Yevmenkin 		 * Any packet containing a reserved BNEP
1627718ced0SMaksim Yevmenkin 		 * header packet type SHALL be dropped.
1637718ced0SMaksim Yevmenkin 		 */
1647718ced0SMaksim Yevmenkin 
1657718ced0SMaksim Yevmenkin 		log_debug("dropped packet with reserved type 0x%2.2x", type);
1667718ced0SMaksim Yevmenkin 		return false;
1677718ced0SMaksim Yevmenkin 	}
1687718ced0SMaksim Yevmenkin 
1697718ced0SMaksim Yevmenkin 	if (BNEP_TYPE_EXT(type)
1707718ced0SMaksim Yevmenkin 	    && !bnep_recv_extension(pkt))
1717718ced0SMaksim Yevmenkin 		return false;	/* invalid extensions */
1727718ced0SMaksim Yevmenkin 
1737718ced0SMaksim Yevmenkin 	if (BNEP_TYPE(type) == BNEP_CONTROL
1747718ced0SMaksim Yevmenkin 	    || pkt->chan->state != CHANNEL_OPEN)
1757718ced0SMaksim Yevmenkin 		return false;	/* no forwarding */
1767718ced0SMaksim Yevmenkin 
1777718ced0SMaksim Yevmenkin 	return true;
1787718ced0SMaksim Yevmenkin }
1797718ced0SMaksim Yevmenkin 
1807718ced0SMaksim Yevmenkin static bool
bnep_recv_extension(packet_t * pkt)1817718ced0SMaksim Yevmenkin bnep_recv_extension(packet_t *pkt)
1827718ced0SMaksim Yevmenkin {
1837718ced0SMaksim Yevmenkin 	exthdr_t *eh;
1847718ced0SMaksim Yevmenkin 	size_t len, size;
1857718ced0SMaksim Yevmenkin 	uint8_t type;
1867718ced0SMaksim Yevmenkin 
1877718ced0SMaksim Yevmenkin 	do {
1887718ced0SMaksim Yevmenkin 		if (pkt->len < 2)
1897718ced0SMaksim Yevmenkin 			return false;
1907718ced0SMaksim Yevmenkin 
1917718ced0SMaksim Yevmenkin 		type = pkt->ptr[0];
1927718ced0SMaksim Yevmenkin 		size = pkt->ptr[1];
1937718ced0SMaksim Yevmenkin 
1947718ced0SMaksim Yevmenkin 		if (pkt->len < size + 2)
1957718ced0SMaksim Yevmenkin 			return false;
1967718ced0SMaksim Yevmenkin 
1977718ced0SMaksim Yevmenkin 		switch (type) {
1987718ced0SMaksim Yevmenkin 		case BNEP_EXTENSION_CONTROL:
1997718ced0SMaksim Yevmenkin 			len = bnep_recv_control(pkt->chan, pkt->ptr + 2, size, true);
2007718ced0SMaksim Yevmenkin 			if (len != size)
2017718ced0SMaksim Yevmenkin 				log_err("ignored spurious data in exthdr");
2027718ced0SMaksim Yevmenkin 
2037718ced0SMaksim Yevmenkin 			break;
2047718ced0SMaksim Yevmenkin 
2057718ced0SMaksim Yevmenkin 		default:
2067718ced0SMaksim Yevmenkin 			/* Unknown extension headers in data packets	 */
2077718ced0SMaksim Yevmenkin 			/* SHALL be forwarded irrespective of any	 */
2087718ced0SMaksim Yevmenkin 			/* network protocol or multicast filter settings */
2097718ced0SMaksim Yevmenkin 			/* and any local filtering policy.		 */
2107718ced0SMaksim Yevmenkin 
2117718ced0SMaksim Yevmenkin 			eh = malloc(sizeof(exthdr_t));
2127718ced0SMaksim Yevmenkin 			if (eh == NULL) {
2137718ced0SMaksim Yevmenkin 				log_err("exthdr malloc() failed: %m");
2147718ced0SMaksim Yevmenkin 				break;
2157718ced0SMaksim Yevmenkin 			}
2167718ced0SMaksim Yevmenkin 
2177718ced0SMaksim Yevmenkin 			eh->ptr = pkt->ptr;
2187718ced0SMaksim Yevmenkin 			eh->len = size;
2197718ced0SMaksim Yevmenkin 			STAILQ_INSERT_TAIL(&pkt->extlist, eh, next);
2207718ced0SMaksim Yevmenkin 			break;
2217718ced0SMaksim Yevmenkin 		}
2227718ced0SMaksim Yevmenkin 
2237718ced0SMaksim Yevmenkin 		packet_adj(pkt, size + 2);
2247718ced0SMaksim Yevmenkin 	} while (BNEP_TYPE_EXT(type));
2257718ced0SMaksim Yevmenkin 
2267718ced0SMaksim Yevmenkin 	return true;
2277718ced0SMaksim Yevmenkin }
2287718ced0SMaksim Yevmenkin 
2297718ced0SMaksim Yevmenkin static size_t
bnep_recv_control(channel_t * chan,uint8_t * ptr,size_t size,bool isext)2307718ced0SMaksim Yevmenkin bnep_recv_control(channel_t *chan, uint8_t *ptr, size_t size, bool isext)
2317718ced0SMaksim Yevmenkin {
2327718ced0SMaksim Yevmenkin 	uint8_t type;
2337718ced0SMaksim Yevmenkin 	size_t len;
2347718ced0SMaksim Yevmenkin 
2357718ced0SMaksim Yevmenkin 	if (size-- < 1)
2367718ced0SMaksim Yevmenkin 		return 0;
2377718ced0SMaksim Yevmenkin 
2387718ced0SMaksim Yevmenkin 	type = *ptr++;
2397718ced0SMaksim Yevmenkin 
2407718ced0SMaksim Yevmenkin 	switch (type) {
2417718ced0SMaksim Yevmenkin 	case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD:
2427718ced0SMaksim Yevmenkin 		len = bnep_recv_control_command_not_understood(chan, ptr, size);
2437718ced0SMaksim Yevmenkin 		break;
2447718ced0SMaksim Yevmenkin 
2457718ced0SMaksim Yevmenkin 	case BNEP_SETUP_CONNECTION_REQUEST:
2467718ced0SMaksim Yevmenkin 		if (isext)
2477718ced0SMaksim Yevmenkin 			return 0;	/* not allowed in extension headers */
2487718ced0SMaksim Yevmenkin 
2497718ced0SMaksim Yevmenkin 		len = bnep_recv_setup_connection_req(chan, ptr, size);
2507718ced0SMaksim Yevmenkin 		break;
2517718ced0SMaksim Yevmenkin 
2527718ced0SMaksim Yevmenkin 	case BNEP_SETUP_CONNECTION_RESPONSE:
2537718ced0SMaksim Yevmenkin 		if (isext)
2547718ced0SMaksim Yevmenkin 			return 0;	/* not allowed in extension headers */
2557718ced0SMaksim Yevmenkin 
2567718ced0SMaksim Yevmenkin 		len = bnep_recv_setup_connection_rsp(chan, ptr, size);
2577718ced0SMaksim Yevmenkin 		break;
2587718ced0SMaksim Yevmenkin 
2597718ced0SMaksim Yevmenkin 	case BNEP_FILTER_NET_TYPE_SET:
2607718ced0SMaksim Yevmenkin 		len = bnep_recv_filter_net_type_set(chan, ptr, size);
2617718ced0SMaksim Yevmenkin 		break;
2627718ced0SMaksim Yevmenkin 
2637718ced0SMaksim Yevmenkin 	case BNEP_FILTER_NET_TYPE_RESPONSE:
2647718ced0SMaksim Yevmenkin 		len = bnep_recv_filter_net_type_rsp(chan, ptr, size);
2657718ced0SMaksim Yevmenkin 		break;
2667718ced0SMaksim Yevmenkin 
2677718ced0SMaksim Yevmenkin 	case BNEP_FILTER_MULTI_ADDR_SET:
2687718ced0SMaksim Yevmenkin 		len = bnep_recv_filter_multi_addr_set(chan, ptr, size);
2697718ced0SMaksim Yevmenkin 		break;
2707718ced0SMaksim Yevmenkin 
2717718ced0SMaksim Yevmenkin 	case BNEP_FILTER_MULTI_ADDR_RESPONSE:
2727718ced0SMaksim Yevmenkin 		len = bnep_recv_filter_multi_addr_rsp(chan, ptr, size);
2737718ced0SMaksim Yevmenkin 		break;
2747718ced0SMaksim Yevmenkin 
2757718ced0SMaksim Yevmenkin 	default:
2767718ced0SMaksim Yevmenkin 		len = 0;
2777718ced0SMaksim Yevmenkin 		break;
2787718ced0SMaksim Yevmenkin 	}
2797718ced0SMaksim Yevmenkin 
2807718ced0SMaksim Yevmenkin 	if (len == 0)
2817718ced0SMaksim Yevmenkin 		bnep_send_control(chan, BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD, type);
2827718ced0SMaksim Yevmenkin 
2837718ced0SMaksim Yevmenkin 	return len;
2847718ced0SMaksim Yevmenkin }
2857718ced0SMaksim Yevmenkin 
2867718ced0SMaksim Yevmenkin static size_t
bnep_recv_control_command_not_understood(channel_t * chan,uint8_t * ptr,size_t size)2877718ced0SMaksim Yevmenkin bnep_recv_control_command_not_understood(channel_t *chan, uint8_t *ptr, size_t size)
2887718ced0SMaksim Yevmenkin {
2897718ced0SMaksim Yevmenkin 	uint8_t type;
2907718ced0SMaksim Yevmenkin 
2917718ced0SMaksim Yevmenkin 	if (size < 1)
2927718ced0SMaksim Yevmenkin 		return 0;
2937718ced0SMaksim Yevmenkin 
2947718ced0SMaksim Yevmenkin 	type = *ptr++;
2957718ced0SMaksim Yevmenkin 	log_err("received Control Command Not Understood (0x%2.2x)", type);
2967718ced0SMaksim Yevmenkin 
2977718ced0SMaksim Yevmenkin 	/* we didn't send any reserved commands, just cut them off */
2987718ced0SMaksim Yevmenkin 	channel_close(chan);
2997718ced0SMaksim Yevmenkin 
3007718ced0SMaksim Yevmenkin 	return 1;
3017718ced0SMaksim Yevmenkin }
3027718ced0SMaksim Yevmenkin 
3037718ced0SMaksim Yevmenkin static size_t
bnep_recv_setup_connection_req(channel_t * chan,uint8_t * ptr,size_t size)3047718ced0SMaksim Yevmenkin bnep_recv_setup_connection_req(channel_t *chan, uint8_t *ptr, size_t size)
3057718ced0SMaksim Yevmenkin {
3067718ced0SMaksim Yevmenkin 	uint8_t off;
3077718ced0SMaksim Yevmenkin 	int src, dst, rsp;
3087718ced0SMaksim Yevmenkin 	size_t len;
3097718ced0SMaksim Yevmenkin 
3107718ced0SMaksim Yevmenkin 	if (size < 1)
3117718ced0SMaksim Yevmenkin 		return 0;
3127718ced0SMaksim Yevmenkin 
3137718ced0SMaksim Yevmenkin 	len = *ptr++;
3147718ced0SMaksim Yevmenkin 	if (size < (len * 2 + 1))
3157718ced0SMaksim Yevmenkin 		return 0;
3167718ced0SMaksim Yevmenkin 
3177718ced0SMaksim Yevmenkin 	if (chan->state != CHANNEL_WAIT_CONNECT_REQ
3187718ced0SMaksim Yevmenkin 	    && chan->state != CHANNEL_OPEN) {
3197718ced0SMaksim Yevmenkin 		log_debug("ignored");
3207718ced0SMaksim Yevmenkin 		return (len * 2 + 1);
3217718ced0SMaksim Yevmenkin 	}
3227718ced0SMaksim Yevmenkin 
3237718ced0SMaksim Yevmenkin 	if (len == 2)
3247718ced0SMaksim Yevmenkin 		off = 2;
3257718ced0SMaksim Yevmenkin 	else if (len == 4)
3267718ced0SMaksim Yevmenkin 		off = 0;
3277718ced0SMaksim Yevmenkin 	else if (len == 16)
3287718ced0SMaksim Yevmenkin 		off = 0;
3297718ced0SMaksim Yevmenkin 	else {
3307718ced0SMaksim Yevmenkin 		rsp = BNEP_SETUP_INVALID_UUID_SIZE;
3317718ced0SMaksim Yevmenkin 		goto done;
3327718ced0SMaksim Yevmenkin 	}
3337718ced0SMaksim Yevmenkin 
3347718ced0SMaksim Yevmenkin 	if (memcmp(ptr, NAP_UUID + off, len) == 0)
3357718ced0SMaksim Yevmenkin 		dst = SDP_SERVICE_CLASS_NAP;
3367718ced0SMaksim Yevmenkin 	else if (memcmp(ptr, GN_UUID + off, len) == 0)
3377718ced0SMaksim Yevmenkin 		dst = SDP_SERVICE_CLASS_GN;
3387718ced0SMaksim Yevmenkin 	else if (memcmp(ptr, PANU_UUID + off, len) == 0)
3397718ced0SMaksim Yevmenkin 		dst = SDP_SERVICE_CLASS_PANU;
3407718ced0SMaksim Yevmenkin 	else
3417718ced0SMaksim Yevmenkin 		dst = 0;
3427718ced0SMaksim Yevmenkin 
3437718ced0SMaksim Yevmenkin 	if (dst != service_class) {
3447718ced0SMaksim Yevmenkin 		rsp = BNEP_SETUP_INVALID_DST_UUID;
3457718ced0SMaksim Yevmenkin 		goto done;
3467718ced0SMaksim Yevmenkin 	}
3477718ced0SMaksim Yevmenkin 
3487718ced0SMaksim Yevmenkin 	ptr += len;
3497718ced0SMaksim Yevmenkin 
3507718ced0SMaksim Yevmenkin 	if (memcmp(ptr, NAP_UUID + off, len) == 0)
3517718ced0SMaksim Yevmenkin 		src = SDP_SERVICE_CLASS_NAP;
3527718ced0SMaksim Yevmenkin 	else if (memcmp(ptr, GN_UUID + off, len) == 0)
3537718ced0SMaksim Yevmenkin 		src = SDP_SERVICE_CLASS_GN;
3547718ced0SMaksim Yevmenkin 	else if (memcmp(ptr, PANU_UUID + off, len) == 0)
3557718ced0SMaksim Yevmenkin 		src = SDP_SERVICE_CLASS_PANU;
3567718ced0SMaksim Yevmenkin 	else
3577718ced0SMaksim Yevmenkin 		src = 0;
3587718ced0SMaksim Yevmenkin 
3597718ced0SMaksim Yevmenkin 	if ((dst != SDP_SERVICE_CLASS_PANU && src != SDP_SERVICE_CLASS_PANU)
3607718ced0SMaksim Yevmenkin 	    || src == 0) {
3617718ced0SMaksim Yevmenkin 		rsp = BNEP_SETUP_INVALID_SRC_UUID;
3627718ced0SMaksim Yevmenkin 		goto done;
3637718ced0SMaksim Yevmenkin 	}
3647718ced0SMaksim Yevmenkin 
3657718ced0SMaksim Yevmenkin 	rsp = BNEP_SETUP_SUCCESS;
3667718ced0SMaksim Yevmenkin 	chan->state = CHANNEL_OPEN;
3677718ced0SMaksim Yevmenkin 	channel_timeout(chan, 0);
3687718ced0SMaksim Yevmenkin 
3697718ced0SMaksim Yevmenkin done:
3707718ced0SMaksim Yevmenkin 	log_debug("addr %s response 0x%2.2x",
3717718ced0SMaksim Yevmenkin 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
3727718ced0SMaksim Yevmenkin 
3737718ced0SMaksim Yevmenkin 	bnep_send_control(chan, BNEP_SETUP_CONNECTION_RESPONSE, rsp);
3747718ced0SMaksim Yevmenkin 	return (len * 2 + 1);
3757718ced0SMaksim Yevmenkin }
3767718ced0SMaksim Yevmenkin 
3777718ced0SMaksim Yevmenkin static size_t
bnep_recv_setup_connection_rsp(channel_t * chan,uint8_t * ptr,size_t size)3787718ced0SMaksim Yevmenkin bnep_recv_setup_connection_rsp(channel_t *chan, uint8_t *ptr, size_t size)
3797718ced0SMaksim Yevmenkin {
3807718ced0SMaksim Yevmenkin 	int rsp;
3817718ced0SMaksim Yevmenkin 
3827718ced0SMaksim Yevmenkin 	if (size < 2)
3837718ced0SMaksim Yevmenkin 		return 0;
3847718ced0SMaksim Yevmenkin 
3857718ced0SMaksim Yevmenkin 	rsp = be16dec(ptr);
3867718ced0SMaksim Yevmenkin 
3877718ced0SMaksim Yevmenkin 	if (chan->state != CHANNEL_WAIT_CONNECT_RSP) {
3887718ced0SMaksim Yevmenkin 		log_debug("ignored");
3897718ced0SMaksim Yevmenkin 		return 2;
3907718ced0SMaksim Yevmenkin 	}
3917718ced0SMaksim Yevmenkin 
3927718ced0SMaksim Yevmenkin 	log_debug("addr %s response 0x%2.2x",
3937718ced0SMaksim Yevmenkin 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
3947718ced0SMaksim Yevmenkin 
3957718ced0SMaksim Yevmenkin 	if (rsp == BNEP_SETUP_SUCCESS) {
3967718ced0SMaksim Yevmenkin 		chan->state = CHANNEL_OPEN;
3977718ced0SMaksim Yevmenkin 		channel_timeout(chan, 0);
3987718ced0SMaksim Yevmenkin 	} else {
3997718ced0SMaksim Yevmenkin 		channel_close(chan);
4007718ced0SMaksim Yevmenkin 	}
4017718ced0SMaksim Yevmenkin 
4027718ced0SMaksim Yevmenkin 	return 2;
4037718ced0SMaksim Yevmenkin }
4047718ced0SMaksim Yevmenkin 
4057718ced0SMaksim Yevmenkin static size_t
bnep_recv_filter_net_type_set(channel_t * chan,uint8_t * ptr,size_t size)4067718ced0SMaksim Yevmenkin bnep_recv_filter_net_type_set(channel_t *chan, uint8_t *ptr, size_t size)
4077718ced0SMaksim Yevmenkin {
4087718ced0SMaksim Yevmenkin 	pfilter_t *pf;
4097718ced0SMaksim Yevmenkin 	int i, nf, rsp;
4107718ced0SMaksim Yevmenkin 	size_t len;
4117718ced0SMaksim Yevmenkin 
4127718ced0SMaksim Yevmenkin 	if (size < 2)
4137718ced0SMaksim Yevmenkin 		return 0;
4147718ced0SMaksim Yevmenkin 
4157718ced0SMaksim Yevmenkin 	len = be16dec(ptr);
4167718ced0SMaksim Yevmenkin 	ptr += 2;
4177718ced0SMaksim Yevmenkin 
4187718ced0SMaksim Yevmenkin 	if (size < (len + 2))
4197718ced0SMaksim Yevmenkin 		return 0;
4207718ced0SMaksim Yevmenkin 
4217718ced0SMaksim Yevmenkin 	if (chan->state != CHANNEL_OPEN) {
4227718ced0SMaksim Yevmenkin 		log_debug("ignored");
4237718ced0SMaksim Yevmenkin 		return (len + 2);
4247718ced0SMaksim Yevmenkin 	}
4257718ced0SMaksim Yevmenkin 
4267718ced0SMaksim Yevmenkin 	nf = len / 4;
4277718ced0SMaksim Yevmenkin 	pf = malloc(nf * sizeof(pfilter_t));
4287718ced0SMaksim Yevmenkin 	if (pf == NULL) {
4297718ced0SMaksim Yevmenkin 		rsp = BNEP_FILTER_TOO_MANY_FILTERS;
4307718ced0SMaksim Yevmenkin 		goto done;
4317718ced0SMaksim Yevmenkin 	}
4327718ced0SMaksim Yevmenkin 
4337718ced0SMaksim Yevmenkin 	log_debug("nf = %d", nf);
4347718ced0SMaksim Yevmenkin 
4357718ced0SMaksim Yevmenkin 	for (i = 0; i < nf; i++) {
4367718ced0SMaksim Yevmenkin 		pf[i].start = be16dec(ptr);
4377718ced0SMaksim Yevmenkin 		ptr += 2;
4387718ced0SMaksim Yevmenkin 		pf[i].end = be16dec(ptr);
4397718ced0SMaksim Yevmenkin 		ptr += 2;
4407718ced0SMaksim Yevmenkin 
4417718ced0SMaksim Yevmenkin 		if (pf[i].start > pf[i].end) {
4427718ced0SMaksim Yevmenkin 			free(pf);
4437718ced0SMaksim Yevmenkin 			rsp = BNEP_FILTER_INVALID_RANGE;
4447718ced0SMaksim Yevmenkin 			goto done;
4457718ced0SMaksim Yevmenkin 		}
4467718ced0SMaksim Yevmenkin 
4477718ced0SMaksim Yevmenkin 		log_debug("pf[%d] = %#4.4x, %#4.4x", i, pf[i].start, pf[i].end);
4487718ced0SMaksim Yevmenkin 	}
4497718ced0SMaksim Yevmenkin 
4507718ced0SMaksim Yevmenkin 	if (chan->pfilter)
4517718ced0SMaksim Yevmenkin 		free(chan->pfilter);
4527718ced0SMaksim Yevmenkin 
4537718ced0SMaksim Yevmenkin 	chan->pfilter = pf;
4547718ced0SMaksim Yevmenkin 	chan->npfilter = nf;
4557718ced0SMaksim Yevmenkin 
4567718ced0SMaksim Yevmenkin 	rsp = BNEP_FILTER_SUCCESS;
4577718ced0SMaksim Yevmenkin 
4587718ced0SMaksim Yevmenkin done:
4597718ced0SMaksim Yevmenkin 	log_debug("addr %s response 0x%2.2x",
4607718ced0SMaksim Yevmenkin 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
4617718ced0SMaksim Yevmenkin 
4627718ced0SMaksim Yevmenkin 	bnep_send_control(chan, BNEP_FILTER_NET_TYPE_RESPONSE, rsp);
4637718ced0SMaksim Yevmenkin 	return (len + 2);
4647718ced0SMaksim Yevmenkin }
4657718ced0SMaksim Yevmenkin 
4667718ced0SMaksim Yevmenkin static size_t
bnep_recv_filter_net_type_rsp(channel_t * chan,uint8_t * ptr,size_t size)4677718ced0SMaksim Yevmenkin bnep_recv_filter_net_type_rsp(channel_t *chan, uint8_t *ptr, size_t size)
4687718ced0SMaksim Yevmenkin {
4697718ced0SMaksim Yevmenkin 	int rsp;
4707718ced0SMaksim Yevmenkin 
4717718ced0SMaksim Yevmenkin 	if (size < 2)
4727718ced0SMaksim Yevmenkin 		return 0;
4737718ced0SMaksim Yevmenkin 
4747718ced0SMaksim Yevmenkin 	if (chan->state != CHANNEL_OPEN) {
4757718ced0SMaksim Yevmenkin 		log_debug("ignored");
4767718ced0SMaksim Yevmenkin 		return 2;
4777718ced0SMaksim Yevmenkin 	}
4787718ced0SMaksim Yevmenkin 
4797718ced0SMaksim Yevmenkin 	rsp = be16dec(ptr);
4807718ced0SMaksim Yevmenkin 
4817718ced0SMaksim Yevmenkin 	log_debug("addr %s response 0x%2.2x",
4827718ced0SMaksim Yevmenkin 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
4837718ced0SMaksim Yevmenkin 
4847718ced0SMaksim Yevmenkin 	/* we did not send any filter_net_type_set message */
4857718ced0SMaksim Yevmenkin 	return 2;
4867718ced0SMaksim Yevmenkin }
4877718ced0SMaksim Yevmenkin 
4887718ced0SMaksim Yevmenkin static size_t
bnep_recv_filter_multi_addr_set(channel_t * chan,uint8_t * ptr,size_t size)4897718ced0SMaksim Yevmenkin bnep_recv_filter_multi_addr_set(channel_t *chan, uint8_t *ptr, size_t size)
4907718ced0SMaksim Yevmenkin {
4917718ced0SMaksim Yevmenkin 	mfilter_t *mf;
4927718ced0SMaksim Yevmenkin 	int i, nf, rsp;
4937718ced0SMaksim Yevmenkin 	size_t len;
4947718ced0SMaksim Yevmenkin 
4957718ced0SMaksim Yevmenkin 	if (size < 2)
4967718ced0SMaksim Yevmenkin 		return 0;
4977718ced0SMaksim Yevmenkin 
4987718ced0SMaksim Yevmenkin 	len = be16dec(ptr);
4997718ced0SMaksim Yevmenkin 	ptr += 2;
5007718ced0SMaksim Yevmenkin 
5017718ced0SMaksim Yevmenkin 	if (size < (len + 2))
5027718ced0SMaksim Yevmenkin 		return 0;
5037718ced0SMaksim Yevmenkin 
5047718ced0SMaksim Yevmenkin 	if (chan->state != CHANNEL_OPEN) {
5057718ced0SMaksim Yevmenkin 		log_debug("ignored");
5067718ced0SMaksim Yevmenkin 		return (len + 2);
5077718ced0SMaksim Yevmenkin 	}
5087718ced0SMaksim Yevmenkin 
5097718ced0SMaksim Yevmenkin 	nf = len / (ETHER_ADDR_LEN * 2);
5107718ced0SMaksim Yevmenkin 	mf = malloc(nf * sizeof(mfilter_t));
5117718ced0SMaksim Yevmenkin 	if (mf == NULL) {
5127718ced0SMaksim Yevmenkin 		rsp = BNEP_FILTER_TOO_MANY_FILTERS;
5137718ced0SMaksim Yevmenkin 		goto done;
5147718ced0SMaksim Yevmenkin 	}
5157718ced0SMaksim Yevmenkin 
5167718ced0SMaksim Yevmenkin 	log_debug("nf = %d", nf);
5177718ced0SMaksim Yevmenkin 
5187718ced0SMaksim Yevmenkin 	for (i = 0; i < nf; i++) {
5197718ced0SMaksim Yevmenkin 		memcpy(mf[i].start, ptr, ETHER_ADDR_LEN);
5207718ced0SMaksim Yevmenkin 		ptr += ETHER_ADDR_LEN;
5217718ced0SMaksim Yevmenkin 
5227718ced0SMaksim Yevmenkin 		memcpy(mf[i].end, ptr, ETHER_ADDR_LEN);
5237718ced0SMaksim Yevmenkin 		ptr += ETHER_ADDR_LEN;
5247718ced0SMaksim Yevmenkin 
5257718ced0SMaksim Yevmenkin 		if (memcmp(mf[i].start, mf[i].end, ETHER_ADDR_LEN) > 0) {
5267718ced0SMaksim Yevmenkin 			free(mf);
5277718ced0SMaksim Yevmenkin 			rsp = BNEP_FILTER_INVALID_RANGE;
5287718ced0SMaksim Yevmenkin 			goto done;
5297718ced0SMaksim Yevmenkin 		}
5307718ced0SMaksim Yevmenkin 
5317718ced0SMaksim Yevmenkin 		log_debug("pf[%d] = "
5327718ced0SMaksim Yevmenkin 		    "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
5337718ced0SMaksim Yevmenkin 		    "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", i,
5347718ced0SMaksim Yevmenkin 		    mf[i].start[0], mf[i].start[1], mf[i].start[2],
5357718ced0SMaksim Yevmenkin 		    mf[i].start[3], mf[i].start[4], mf[i].start[5],
5367718ced0SMaksim Yevmenkin 		    mf[i].end[0], mf[i].end[1], mf[i].end[2],
5377718ced0SMaksim Yevmenkin 		    mf[i].end[3], mf[i].end[4], mf[i].end[5]);
5387718ced0SMaksim Yevmenkin 	}
5397718ced0SMaksim Yevmenkin 
5407718ced0SMaksim Yevmenkin 	if (chan->mfilter)
5417718ced0SMaksim Yevmenkin 		free(chan->mfilter);
5427718ced0SMaksim Yevmenkin 
5437718ced0SMaksim Yevmenkin 	chan->mfilter = mf;
5447718ced0SMaksim Yevmenkin 	chan->nmfilter = nf;
5457718ced0SMaksim Yevmenkin 
5467718ced0SMaksim Yevmenkin 	rsp = BNEP_FILTER_SUCCESS;
5477718ced0SMaksim Yevmenkin 
5487718ced0SMaksim Yevmenkin done:
5497718ced0SMaksim Yevmenkin 	log_debug("addr %s response 0x%2.2x",
5507718ced0SMaksim Yevmenkin 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
5517718ced0SMaksim Yevmenkin 
5527718ced0SMaksim Yevmenkin 	bnep_send_control(chan, BNEP_FILTER_MULTI_ADDR_RESPONSE, rsp);
5537718ced0SMaksim Yevmenkin 	return (len + 2);
5547718ced0SMaksim Yevmenkin }
5557718ced0SMaksim Yevmenkin 
5567718ced0SMaksim Yevmenkin static size_t
bnep_recv_filter_multi_addr_rsp(channel_t * chan,uint8_t * ptr,size_t size)5577718ced0SMaksim Yevmenkin bnep_recv_filter_multi_addr_rsp(channel_t *chan, uint8_t *ptr, size_t size)
5587718ced0SMaksim Yevmenkin {
5597718ced0SMaksim Yevmenkin 	int rsp;
5607718ced0SMaksim Yevmenkin 
5617718ced0SMaksim Yevmenkin 	if (size < 2)
5627718ced0SMaksim Yevmenkin 		return false;
5637718ced0SMaksim Yevmenkin 
5647718ced0SMaksim Yevmenkin 	if (chan->state != CHANNEL_OPEN) {
5657718ced0SMaksim Yevmenkin 		log_debug("ignored");
5667718ced0SMaksim Yevmenkin 		return 2;
5677718ced0SMaksim Yevmenkin 	}
5687718ced0SMaksim Yevmenkin 
5697718ced0SMaksim Yevmenkin 	rsp = be16dec(ptr);
5707718ced0SMaksim Yevmenkin 	log_debug("addr %s response 0x%2.2x",
5717718ced0SMaksim Yevmenkin 	    ether_ntoa((struct ether_addr *)chan->raddr), rsp);
5727718ced0SMaksim Yevmenkin 
5737718ced0SMaksim Yevmenkin 	/* we did not send any filter_multi_addr_set message */
5747718ced0SMaksim Yevmenkin 	return 2;
5757718ced0SMaksim Yevmenkin }
5767718ced0SMaksim Yevmenkin 
5777718ced0SMaksim Yevmenkin void
bnep_send_control(channel_t * chan,unsigned type,...)5787fcdc815SDimitry Andric bnep_send_control(channel_t *chan, unsigned type, ...)
5797718ced0SMaksim Yevmenkin {
5807718ced0SMaksim Yevmenkin 	packet_t *pkt;
5817718ced0SMaksim Yevmenkin 	uint8_t *p;
5827718ced0SMaksim Yevmenkin 	va_list ap;
5837718ced0SMaksim Yevmenkin 
5847718ced0SMaksim Yevmenkin 	assert(chan->state != CHANNEL_CLOSED);
5857718ced0SMaksim Yevmenkin 
5867718ced0SMaksim Yevmenkin 	pkt = packet_alloc(chan);
5877718ced0SMaksim Yevmenkin 	if (pkt == NULL)
5887718ced0SMaksim Yevmenkin 		return;
5897718ced0SMaksim Yevmenkin 
5907718ced0SMaksim Yevmenkin 	p = pkt->ptr;
5917718ced0SMaksim Yevmenkin 	va_start(ap, type);
5927718ced0SMaksim Yevmenkin 
5937718ced0SMaksim Yevmenkin 	*p++ = BNEP_CONTROL;
5947fcdc815SDimitry Andric 	*p++ = (uint8_t)type;
5957718ced0SMaksim Yevmenkin 
5967718ced0SMaksim Yevmenkin 	switch(type) {
5977718ced0SMaksim Yevmenkin 	case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD:
5987718ced0SMaksim Yevmenkin 		*p++ = va_arg(ap, int);
5997718ced0SMaksim Yevmenkin 		break;
6007718ced0SMaksim Yevmenkin 
6017718ced0SMaksim Yevmenkin 	case BNEP_SETUP_CONNECTION_REQUEST:
6027718ced0SMaksim Yevmenkin 		*p++ = va_arg(ap, int);
6037718ced0SMaksim Yevmenkin 		be16enc(p, va_arg(ap, int));
6047718ced0SMaksim Yevmenkin 		p += 2;
6057718ced0SMaksim Yevmenkin 		be16enc(p, va_arg(ap, int));
6067718ced0SMaksim Yevmenkin 		p += 2;
6077718ced0SMaksim Yevmenkin 		break;
6087718ced0SMaksim Yevmenkin 
6097718ced0SMaksim Yevmenkin 	case BNEP_SETUP_CONNECTION_RESPONSE:
6107718ced0SMaksim Yevmenkin 	case BNEP_FILTER_NET_TYPE_RESPONSE:
6117718ced0SMaksim Yevmenkin 	case BNEP_FILTER_MULTI_ADDR_RESPONSE:
6127718ced0SMaksim Yevmenkin 		be16enc(p, va_arg(ap, int));
6137718ced0SMaksim Yevmenkin 		p += 2;
6147718ced0SMaksim Yevmenkin 		break;
6157718ced0SMaksim Yevmenkin 
6167718ced0SMaksim Yevmenkin 	case BNEP_FILTER_NET_TYPE_SET:		/* TODO */
6177718ced0SMaksim Yevmenkin 	case BNEP_FILTER_MULTI_ADDR_SET:	/* TODO */
6187718ced0SMaksim Yevmenkin 	default:
6197718ced0SMaksim Yevmenkin 		log_err("Can't send control type 0x%2.2x", type);
6207718ced0SMaksim Yevmenkin 		break;
6217718ced0SMaksim Yevmenkin 	}
6227718ced0SMaksim Yevmenkin 
6237718ced0SMaksim Yevmenkin 	va_end(ap);
6247718ced0SMaksim Yevmenkin 	pkt->len = p - pkt->ptr;
6257718ced0SMaksim Yevmenkin 
6267718ced0SMaksim Yevmenkin 	channel_put(chan, pkt);
6277718ced0SMaksim Yevmenkin 	packet_free(pkt);
6287718ced0SMaksim Yevmenkin }
6297718ced0SMaksim Yevmenkin 
6307718ced0SMaksim Yevmenkin /*
6317718ced0SMaksim Yevmenkin  * BNEP send packet routine
6327718ced0SMaksim Yevmenkin  * return true if packet can be removed from queue
6337718ced0SMaksim Yevmenkin  */
6347718ced0SMaksim Yevmenkin bool
bnep_send(channel_t * chan,packet_t * pkt)6357718ced0SMaksim Yevmenkin bnep_send(channel_t *chan, packet_t *pkt)
6367718ced0SMaksim Yevmenkin {
6377718ced0SMaksim Yevmenkin 	struct iovec iov[2];
6387718ced0SMaksim Yevmenkin 	uint8_t *p, *type, *proto;
6397718ced0SMaksim Yevmenkin 	exthdr_t *eh;
6407718ced0SMaksim Yevmenkin 	bool src, dst;
6417718ced0SMaksim Yevmenkin 	size_t nw;
6427718ced0SMaksim Yevmenkin 
6437718ced0SMaksim Yevmenkin 	if (pkt->type == NULL) {
6447718ced0SMaksim Yevmenkin 		iov[0].iov_base = pkt->ptr;
6457718ced0SMaksim Yevmenkin 		iov[0].iov_len = pkt->len;
6467718ced0SMaksim Yevmenkin 		iov[1].iov_base = NULL;
6477718ced0SMaksim Yevmenkin 		iov[1].iov_len = 0;
6487718ced0SMaksim Yevmenkin 	} else {
6497718ced0SMaksim Yevmenkin 		p = chan->sendbuf;
6507718ced0SMaksim Yevmenkin 
6517718ced0SMaksim Yevmenkin 		dst = (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) != 0);
6527718ced0SMaksim Yevmenkin 		src = (memcmp(pkt->src, chan->laddr, ETHER_ADDR_LEN) != 0);
6537718ced0SMaksim Yevmenkin 
6547718ced0SMaksim Yevmenkin 		type = p;
6557718ced0SMaksim Yevmenkin 		p += 1;
6567718ced0SMaksim Yevmenkin 
6577718ced0SMaksim Yevmenkin 		if (dst && src)
6587718ced0SMaksim Yevmenkin 			*type = BNEP_GENERAL_ETHERNET;
6597718ced0SMaksim Yevmenkin 		else if (dst && !src)
6607718ced0SMaksim Yevmenkin 			*type = BNEP_COMPRESSED_ETHERNET_DST_ONLY;
6617718ced0SMaksim Yevmenkin 		else if (!dst && src)
6627718ced0SMaksim Yevmenkin 			*type = BNEP_COMPRESSED_ETHERNET_SRC_ONLY;
6637718ced0SMaksim Yevmenkin 		else /* (!dst && !src) */
6647718ced0SMaksim Yevmenkin 			*type = BNEP_COMPRESSED_ETHERNET;
6657718ced0SMaksim Yevmenkin 
6667718ced0SMaksim Yevmenkin 		if (dst) {
6677718ced0SMaksim Yevmenkin 			memcpy(p, pkt->dst, ETHER_ADDR_LEN);
6687718ced0SMaksim Yevmenkin 			p += ETHER_ADDR_LEN;
6697718ced0SMaksim Yevmenkin 		}
6707718ced0SMaksim Yevmenkin 
6717718ced0SMaksim Yevmenkin 		if (src) {
6727718ced0SMaksim Yevmenkin 			memcpy(p, pkt->src, ETHER_ADDR_LEN);
6737718ced0SMaksim Yevmenkin 			p += ETHER_ADDR_LEN;
6747718ced0SMaksim Yevmenkin 		}
6757718ced0SMaksim Yevmenkin 
6767718ced0SMaksim Yevmenkin 		proto = p;
6777718ced0SMaksim Yevmenkin 		memcpy(p, pkt->type, ETHER_TYPE_LEN);
6787718ced0SMaksim Yevmenkin 		p += ETHER_TYPE_LEN;
6797718ced0SMaksim Yevmenkin 
6807718ced0SMaksim Yevmenkin 		STAILQ_FOREACH(eh, &pkt->extlist, next) {
6817718ced0SMaksim Yevmenkin 			if (p + eh->len > chan->sendbuf + chan->mtu)
6827718ced0SMaksim Yevmenkin 				break;
6837718ced0SMaksim Yevmenkin 
6847718ced0SMaksim Yevmenkin 			*type |= BNEP_EXT;
6857718ced0SMaksim Yevmenkin 			type = p;
6867718ced0SMaksim Yevmenkin 
6877718ced0SMaksim Yevmenkin 			memcpy(p, eh->ptr, eh->len);
6887718ced0SMaksim Yevmenkin 			p += eh->len;
6897718ced0SMaksim Yevmenkin 		}
6907718ced0SMaksim Yevmenkin 
6917718ced0SMaksim Yevmenkin 		*type &= ~BNEP_EXT;
6927718ced0SMaksim Yevmenkin 
6937718ced0SMaksim Yevmenkin 		iov[0].iov_base = chan->sendbuf;
6947718ced0SMaksim Yevmenkin 		iov[0].iov_len = (p - chan->sendbuf);
6957718ced0SMaksim Yevmenkin 
6967718ced0SMaksim Yevmenkin 		if ((chan->npfilter == 0 || bnep_pfilter(chan, pkt))
6977718ced0SMaksim Yevmenkin 		    && (chan->nmfilter == 0 || bnep_mfilter(chan, pkt))) {
6987718ced0SMaksim Yevmenkin 			iov[1].iov_base = pkt->ptr;
6997718ced0SMaksim Yevmenkin 			iov[1].iov_len = pkt->len;
7007718ced0SMaksim Yevmenkin 		} else if (be16dec(proto) == ETHERTYPE_VLAN
7017718ced0SMaksim Yevmenkin 		    && pkt->len >= ETHER_VLAN_ENCAP_LEN) {
7027718ced0SMaksim Yevmenkin 			iov[1].iov_base = pkt->ptr;
7037718ced0SMaksim Yevmenkin 			iov[1].iov_len = ETHER_VLAN_ENCAP_LEN;
7047718ced0SMaksim Yevmenkin 		} else {
7057718ced0SMaksim Yevmenkin 			iov[1].iov_base = NULL;
7067718ced0SMaksim Yevmenkin 			iov[1].iov_len = 0;
7077718ced0SMaksim Yevmenkin 			memset(proto, 0, ETHER_TYPE_LEN);
7087718ced0SMaksim Yevmenkin 		}
7097718ced0SMaksim Yevmenkin 	}
7107718ced0SMaksim Yevmenkin 
7117718ced0SMaksim Yevmenkin 	if (iov[0].iov_len + iov[1].iov_len > chan->mtu) {
7127718ced0SMaksim Yevmenkin 		log_err("packet exceeded MTU (dropped)");
7137718ced0SMaksim Yevmenkin 		return false;
7147718ced0SMaksim Yevmenkin 	}
7157718ced0SMaksim Yevmenkin 
7167718ced0SMaksim Yevmenkin 	nw = writev(chan->fd, iov, __arraycount(iov));
7177718ced0SMaksim Yevmenkin 	return (nw > 0);
7187718ced0SMaksim Yevmenkin }
7197718ced0SMaksim Yevmenkin 
7207718ced0SMaksim Yevmenkin static bool
bnep_pfilter(channel_t * chan,packet_t * pkt)7217718ced0SMaksim Yevmenkin bnep_pfilter(channel_t *chan, packet_t *pkt)
7227718ced0SMaksim Yevmenkin {
7237718ced0SMaksim Yevmenkin 	int proto, i;
7247718ced0SMaksim Yevmenkin 
7257718ced0SMaksim Yevmenkin 	proto = be16dec(pkt->type);
7267718ced0SMaksim Yevmenkin 	if (proto == ETHERTYPE_VLAN) {	/* IEEE 802.1Q tag header */
7277718ced0SMaksim Yevmenkin 		if (pkt->len < 4)
7287718ced0SMaksim Yevmenkin 			return false;
7297718ced0SMaksim Yevmenkin 
7307718ced0SMaksim Yevmenkin 		proto = be16dec(pkt->ptr + 2);
7317718ced0SMaksim Yevmenkin 	}
7327718ced0SMaksim Yevmenkin 
7337718ced0SMaksim Yevmenkin 	for (i = 0; i < chan->npfilter; i++) {
7347718ced0SMaksim Yevmenkin 		if (chan->pfilter[i].start <= proto
7357718ced0SMaksim Yevmenkin 		    && chan->pfilter[i].end >=proto)
7367718ced0SMaksim Yevmenkin 			return true;
7377718ced0SMaksim Yevmenkin 	}
7387718ced0SMaksim Yevmenkin 
7397718ced0SMaksim Yevmenkin 	return false;
7407718ced0SMaksim Yevmenkin }
7417718ced0SMaksim Yevmenkin 
7427718ced0SMaksim Yevmenkin static bool
bnep_mfilter(channel_t * chan,packet_t * pkt)7437718ced0SMaksim Yevmenkin bnep_mfilter(channel_t *chan, packet_t *pkt)
7447718ced0SMaksim Yevmenkin {
7457718ced0SMaksim Yevmenkin 	int i;
7467718ced0SMaksim Yevmenkin 
7477718ced0SMaksim Yevmenkin 	if (!ETHER_IS_MULTICAST(pkt->dst))
7487718ced0SMaksim Yevmenkin 		return true;
7497718ced0SMaksim Yevmenkin 
7507718ced0SMaksim Yevmenkin 	for (i = 0; i < chan->nmfilter; i++) {
7517718ced0SMaksim Yevmenkin 		if (memcmp(pkt->dst, chan->mfilter[i].start, ETHER_ADDR_LEN) >= 0
7527718ced0SMaksim Yevmenkin 		    && memcmp(pkt->dst, chan->mfilter[i].end, ETHER_ADDR_LEN) <= 0)
7537718ced0SMaksim Yevmenkin 			return true;
7547718ced0SMaksim Yevmenkin 	}
7557718ced0SMaksim Yevmenkin 
7567718ced0SMaksim Yevmenkin 	return false;
7577718ced0SMaksim Yevmenkin }
758