1 /*
2  * Driver interaction with Linux nl80211/cfg80211 - AP monitor interface
3  * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
4  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
5  * Copyright (c) 2005-2006, Devicescape Software, Inc.
6  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
7  * Copyright (c) 2009-2010, Atheros Communications
8  *
9  * This software may be distributed under the terms of the BSD license.
10  * See README for more details.
11  */
12 
13 #include "includes.h"
14 #include <netpacket/packet.h>
15 #include <linux/filter.h>
16 
17 #include "utils/common.h"
18 #include "utils/eloop.h"
19 #include "common/ieee802_11_defs.h"
20 #include "common/ieee802_11_common.h"
21 #include "linux_ioctl.h"
22 #include "radiotap_iter.h"
23 #include "driver_nl80211.h"
24 
25 
26 #ifdef CONFIG_TESTING_OPTIONS
27 
28 static struct sock_filter msock_filter_insns[] = {
29 	/* return 0 == "DROP", we don't want RX */
30 	BPF_STMT(BPF_RET | BPF_K, 0),
31 };
32 
33 static const struct sock_fprog msock_filter = {
34 	.len = ARRAY_SIZE(msock_filter_insns),
35 	.filter = msock_filter_insns,
36 };
37 
38 
add_monitor_filter(int s)39 static int add_monitor_filter(int s)
40 {
41 	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
42 		       &msock_filter, sizeof(msock_filter))) {
43 		wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
44 			   strerror(errno));
45 		return -1;
46 	}
47 
48 	return 0;
49 }
50 
51 
52 static void
nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data * drv,int ifidx,int sock)53 nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv,
54 				 int ifidx, int sock)
55 {
56 	wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface");
57 
58 	if (ifidx >= 0)
59 		nl80211_remove_iface(drv, ifidx);
60 	if (sock >= 0)
61 		close(sock);
62 }
63 
64 
nl80211_create_monitor_interface(struct wpa_driver_nl80211_data * drv,int * ifidx,int * sock)65 static int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv,
66 					    int *ifidx, int *sock)
67 {
68 	char buf[IFNAMSIZ];
69 	struct sockaddr_ll ll;
70 	int optval;
71 	socklen_t optlen;
72 
73 	*ifidx = -1;
74 	*sock = -1;
75 
76 	if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) {
77 		/*
78 		 * P2P interface name is of the format p2p-%s-%d. For monitor
79 		 * interface name corresponding to P2P GO, replace "p2p-" with
80 		 * "mon-" to retain the same interface name length and to
81 		 * indicate that it is a monitor interface.
82 		 */
83 		snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
84 	} else {
85 		int ret;
86 
87 		/* Non-P2P interface with AP functionality. */
88 		ret = os_snprintf(buf, IFNAMSIZ, "mon.%s",
89 				  drv->first_bss->ifname);
90 		if (ret >= (int) sizeof(buf))
91 			wpa_printf(MSG_DEBUG,
92 				   "nl80211: Monitor interface name has been truncated to %s",
93 				   buf);
94 		else if (ret < 0)
95 			return ret;
96 	}
97 
98 	buf[IFNAMSIZ - 1] = '\0';
99 
100 	*ifidx = nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
101 				      0, NULL, NULL, 0);
102 
103 	if (*ifidx < 0)
104 		return -1;
105 
106 	if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
107 		goto error;
108 
109 	memset(&ll, 0, sizeof(ll));
110 	ll.sll_family = AF_PACKET;
111 	ll.sll_ifindex = *ifidx;
112 	*sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
113 	if (*sock < 0) {
114 		wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
115 			   strerror(errno));
116 		goto error;
117 	}
118 
119 	if (add_monitor_filter(*sock)) {
120 		wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
121 			   "interface; do filtering in user space");
122 		/* This works, but will cost in performance. */
123 	}
124 
125 	if (bind(*sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
126 		wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
127 			   strerror(errno));
128 		goto error;
129 	}
130 
131 	optlen = sizeof(optval);
132 	optval = 20;
133 	if (setsockopt(*sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
134 		wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
135 			   strerror(errno));
136 		goto error;
137 	}
138 
139 	return 0;
140  error:
141 	nl80211_remove_monitor_interface(drv, *ifidx, *sock);
142 	return -1;
143 }
144 
145 
_nl80211_send_monitor(int monitor_sock,const void * data,size_t len,int encrypt,int noack)146 static int _nl80211_send_monitor(int monitor_sock,
147 				 const void *data, size_t len,
148 				 int encrypt, int noack)
149 {
150 	__u8 rtap_hdr[] = {
151 		0x00, 0x00, /* radiotap version */
152 		0x0e, 0x00, /* radiotap length */
153 		0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
154 		IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
155 		0x00,       /* padding */
156 		0x00, 0x00, /* RX and TX flags to indicate that */
157 		0x00, 0x00, /* this is the injected frame directly */
158 	};
159 	struct iovec iov[2] = {
160 		{
161 			.iov_base = &rtap_hdr,
162 			.iov_len = sizeof(rtap_hdr),
163 		},
164 		{
165 			.iov_base = (void *) data,
166 			.iov_len = len,
167 		}
168 	};
169 	struct msghdr msg = {
170 		.msg_name = NULL,
171 		.msg_namelen = 0,
172 		.msg_iov = iov,
173 		.msg_iovlen = 2,
174 		.msg_control = NULL,
175 		.msg_controllen = 0,
176 		.msg_flags = 0,
177 	};
178 	int res;
179 	u16 txflags = 0;
180 
181 	if (encrypt)
182 		rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
183 
184 	if (noack)
185 		txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
186 	WPA_PUT_LE16(&rtap_hdr[12], txflags);
187 
188 	res = sendmsg(monitor_sock, &msg, 0);
189 	if (res < 0) {
190 		wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
191 		return -1;
192 	}
193 	return 0;
194 }
195 
196 
nl80211_send_monitor(struct wpa_driver_nl80211_data * drv,const void * data,size_t len,int encrypt,int noack)197 int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
198 			 const void *data, size_t len,
199 			 int encrypt, int noack)
200 {
201 	int res, ifidx, sock;
202 
203 	res = nl80211_create_monitor_interface(drv, &ifidx, &sock);
204 	if (res < 0)
205 		return res;
206 
207 	res = _nl80211_send_monitor(sock, data, len, encrypt, noack);
208 	nl80211_remove_monitor_interface(drv, ifidx, sock);
209 	return res;
210 }
211 
212 #endif /* CONFIG_TESTING_OPTIONS */
213