xref: /openbsd-src/usr.sbin/hostapd/iapp.c (revision a28daedfc357b214be5c701aa8ba8adb29a7f1c2)
1 /*	$OpenBSD: iapp.c,v 1.18 2009/04/16 20:13:13 sobrado Exp $	*/
2 
3 /*
4  * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/ioctl.h>
21 #include <sys/types.h>
22 #include <sys/socket.h>
23 #include <sys/time.h>
24 #include <sys/uio.h>
25 
26 #include <net/if.h>
27 #include <net/if_dl.h>
28 #include <net/if_media.h>
29 #include <net/if_arp.h>
30 #include <net/if_llc.h>
31 #include <net/bpf.h>
32 
33 #include <netinet/in.h>
34 #include <netinet/if_ether.h>
35 #include <arpa/inet.h>
36 
37 #include <fcntl.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 
42 #include "hostapd.h"
43 #include "iapp.h"
44 
45 void
46 hostapd_iapp_init(struct hostapd_config *cfg)
47 {
48 	struct hostapd_apme *apme;
49 	struct hostapd_iapp *iapp = &cfg->c_iapp;
50 
51 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
52 		return;
53 
54 	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
55 		/* Get Host AP's BSSID */
56 		hostapd_priv_apme_bssid(apme);
57 		hostapd_log(HOSTAPD_LOG,
58 		    "%s/%s: attached Host AP interface with BSSID %s",
59 		    apme->a_iface, iapp->i_iface,
60 		    etheraddr_string(apme->a_bssid));
61 
62 		/* Deauthenticate all stations on startup */
63 		(void)hostapd_apme_deauth(apme);
64 	}
65 }
66 
67 void
68 hostapd_iapp_term(struct hostapd_config *cfg)
69 {
70 	struct hostapd_apme *apme;
71 	struct hostapd_iapp *iapp = &cfg->c_iapp;
72 
73 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
74 		return;
75 
76 	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
77 		hostapd_log(HOSTAPD_LOG_VERBOSE,
78 		    "%s/%s: detaching from Host AP",
79 		    apme->a_iface, iapp->i_iface);
80 	}
81 }
82 
83 int
84 hostapd_iapp_add_notify(struct hostapd_apme *apme, struct hostapd_node *node)
85 {
86 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
87 	struct hostapd_iapp *iapp = &cfg->c_iapp;
88 	struct sockaddr_in *addr;
89 	struct {
90 		struct ieee80211_iapp_frame hdr;
91 		struct ieee80211_iapp_add_notify add;
92 	} __packed frame;
93 
94 	if ((iapp->i_flags & HOSTAPD_IAPP_F_ADD_NOTIFY) == 0)
95 		return (0);
96 
97 	/*
98 	 * Send an ADD.notify message to other access points to notify
99 	 * about a new association on our Host AP.
100 	 */
101 	bzero(&frame, sizeof(frame));
102 
103 	frame.hdr.i_version = IEEE80211_IAPP_VERSION;
104 	frame.hdr.i_command = IEEE80211_IAPP_FRAME_ADD_NOTIFY;
105 	frame.hdr.i_identifier = htons(iapp->i_cnt++);
106 	frame.hdr.i_length = sizeof(struct ieee80211_iapp_add_notify);
107 
108 	frame.add.a_length = IEEE80211_ADDR_LEN;
109 	frame.add.a_seqnum = htons(node->ni_rxseq);
110 	bcopy(node->ni_macaddr, frame.add.a_macaddr, IEEE80211_ADDR_LEN);
111 
112 	if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST)
113 		addr = &iapp->i_broadcast;
114 	else
115 		addr = &iapp->i_multicast;
116 
117 	if (sendto(iapp->i_udp, &frame, sizeof(frame),
118 	    0, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) == -1) {
119 		hostapd_log(HOSTAPD_LOG,
120 		    "%s: failed to send ADD notification: %s",
121 		    iapp->i_iface, strerror(errno));
122 		return (errno);
123 	}
124 
125 	hostapd_log(HOSTAPD_LOG, "%s/%s: sent ADD notification for %s",
126 	    apme->a_iface, iapp->i_iface,
127 	    etheraddr_string(frame.add.a_macaddr));
128 
129 	/* Send a LLC XID frame, see llc.c for details */
130 	return (hostapd_priv_llc_xid(cfg, node));
131 }
132 
133 int
134 hostapd_iapp_radiotap(struct hostapd_apme *apme, u_int8_t *buf,
135     const u_int len)
136 {
137 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
138 	struct hostapd_iapp *iapp = &cfg->c_iapp;
139 	struct sockaddr_in *addr;
140 	struct ieee80211_iapp_frame hdr;
141 	struct msghdr msg;
142 	struct iovec iov[2];
143 
144 	/*
145 	 * Send an HOSTAPD.pcap/radiotap message to other access points
146 	 * with an appended network dump. This is an hostapd extension to
147 	 * IAPP.
148 	 */
149 	bzero(&hdr, sizeof(hdr));
150 
151 	hdr.i_version = IEEE80211_IAPP_VERSION;
152 	if (cfg->c_apme_dlt == DLT_IEEE802_11_RADIO)
153 		hdr.i_command = IEEE80211_IAPP_FRAME_HOSTAPD_RADIOTAP;
154 	else if (cfg->c_apme_dlt == DLT_IEEE802_11)
155 		hdr.i_command = IEEE80211_IAPP_FRAME_HOSTAPD_PCAP;
156 	else
157 		return (EINVAL);
158 	hdr.i_identifier = htons(iapp->i_cnt++);
159 	hdr.i_length = len;
160 
161 	if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST)
162 		addr = &iapp->i_broadcast;
163 	else
164 		addr = &iapp->i_multicast;
165 
166 	iov[0].iov_base = &hdr;
167 	iov[0].iov_len = sizeof(hdr);
168 	iov[1].iov_base = buf;
169 	iov[1].iov_len = len;
170 	msg.msg_name = (caddr_t)addr;
171 	msg.msg_namelen = sizeof(struct sockaddr_in);
172 	msg.msg_iov = iov;
173 	msg.msg_iovlen = 2;
174 	msg.msg_control = 0;
175 	msg.msg_controllen = 0;
176 	msg.msg_flags = 0;
177 
178 	if (sendmsg(iapp->i_udp, &msg, 0) == -1) {
179 		hostapd_log(HOSTAPD_LOG,
180 		    "%s: failed to send HOSTAPD %s: %s",
181 		    iapp->i_iface, cfg->c_apme_dlt ==
182 		    DLT_IEEE802_11_RADIO ? "radiotap" : "pcap",
183 		    strerror(errno));
184 		return (errno);
185 	}
186 
187 	return (0);
188 }
189 
190 void
191 hostapd_iapp_input(int fd, short sig, void *arg)
192 {
193 	struct hostapd_config *cfg = (struct hostapd_config *)arg;
194 	struct hostapd_iapp *iapp = &cfg->c_iapp;
195 	struct hostapd_apme *apme;
196 	struct sockaddr_in addr;
197 	socklen_t addr_len;
198 	ssize_t len;
199 	u_int8_t buf[IAPP_MAXSIZE];
200 	struct hostapd_node node;
201 	struct ieee80211_iapp_recv {
202 		struct ieee80211_iapp_frame hdr;
203 		union {
204 			struct ieee80211_iapp_add_notify add;
205 			u_int8_t buf[1];
206 		} u;
207 	} __packed *frame;
208 	u_int dlt;
209 	int ret = 0;
210 
211 	/* Ignore invalid signals */
212 	if (sig != EV_READ)
213 		return;
214 
215 	/*
216 	 * Listen to possible messages from other IAPP
217 	 */
218 	bzero(buf, sizeof(buf));
219 
220 	if ((len = recvfrom(fd, buf, sizeof(buf), 0,
221 	    (struct sockaddr*)&addr, &addr_len)) < 1)
222 		return;
223 
224 	if (bcmp(&iapp->i_addr.sin_addr, &addr.sin_addr,
225 	    sizeof(addr.sin_addr)) == 0)
226 		return;
227 
228 	frame = (struct ieee80211_iapp_recv*)buf;
229 
230 	/* Validate the IAPP version */
231 	if (len < (ssize_t)sizeof(struct ieee80211_iapp_frame) ||
232 	    frame->hdr.i_version != IEEE80211_IAPP_VERSION ||
233 	    addr_len < sizeof(struct sockaddr_in))
234 		return;
235 
236 	cfg->c_stats.cn_rx_iapp++;
237 
238 	/*
239 	 * Process the IAPP frame
240 	 */
241 	switch (frame->hdr.i_command) {
242 	case IEEE80211_IAPP_FRAME_ADD_NOTIFY:
243 		/* Short frame */
244 		if (len < (ssize_t)(sizeof(struct ieee80211_iapp_frame) +
245 		    sizeof(struct ieee80211_iapp_add_notify)))
246 			return;
247 
248 		/* Don't support non-48bit MAC addresses, yet */
249 		if (frame->u.add.a_length != IEEE80211_ADDR_LEN)
250 			return;
251 
252 		node.ni_rxseq = frame->u.add.a_seqnum;
253 		bcopy(frame->u.add.a_macaddr, node.ni_macaddr,
254 		    IEEE80211_ADDR_LEN);
255 
256 		/*
257 		 * Try to remove a node from our Host AP and to free
258 		 * any allocated resources. Otherwise the received
259 		 * ADD.notify message will be ignored.
260 		 */
261 		if (iapp->i_flags & HOSTAPD_IAPP_F_ADD &&
262 		    cfg->c_flags & HOSTAPD_CFG_F_APME) {
263 			TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
264 				if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING)
265 					(void)hostapd_roaming_del(apme, &node);
266 				if (iapp->i_flags & HOSTAPD_IAPP_F_ADD_NOTIFY &&
267 				    (ret = hostapd_apme_delnode(apme,
268 				    &node)) == 0)
269 					cfg->c_stats.cn_tx_apme++;
270 			}
271 		} else
272 			ret = 0;
273 
274 		hostapd_log(iapp->i_flags & HOSTAPD_IAPP_F_ADD ?
275 		    HOSTAPD_LOG : HOSTAPD_LOG_VERBOSE,
276 		    "%s: %s ADD notification for %s at %s",
277 		    iapp->i_iface, ret == 0 ?
278 		    "received" : "ignored",
279 		    etheraddr_string(node.ni_macaddr),
280 		    inet_ntoa(addr.sin_addr));
281 		break;
282 
283 	case IEEE80211_IAPP_FRAME_HOSTAPD_PCAP:
284 	case IEEE80211_IAPP_FRAME_HOSTAPD_RADIOTAP:
285 		if ((iapp->i_flags & HOSTAPD_IAPP_F_RADIOTAP) == 0)
286 			return;
287 
288 		/* Short frame */
289 		if (len <= (ssize_t)sizeof(struct ieee80211_iapp_frame) ||
290 		    frame->hdr.i_length < sizeof(struct ieee80211_frame))
291 			return;
292 
293 		dlt = frame->hdr.i_command ==
294 		    IEEE80211_IAPP_FRAME_HOSTAPD_PCAP ?
295 		    DLT_IEEE802_11 : DLT_IEEE802_11_RADIO;
296 
297 		hostapd_print_ieee80211(dlt, 1, (u_int8_t *)frame->u.buf,
298 		    len - sizeof(struct ieee80211_iapp_frame));
299 		return;
300 
301 	case IEEE80211_IAPP_FRAME_MOVE_NOTIFY:
302 	case IEEE80211_IAPP_FRAME_MOVE_RESPONSE:
303 	case IEEE80211_IAPP_FRAME_SEND_SECURITY_BLOCK:
304 	case IEEE80211_IAPP_FRAME_ACK_SECURITY_BLOCK:
305 	case IEEE80211_IAPP_FRAME_CACHE_NOTIFY:
306 	case IEEE80211_IAPP_FRAME_CACHE_RESPONSE:
307 
308 		/*
309 		 * XXX TODO
310 		 */
311 
312 		hostapd_log(HOSTAPD_LOG_VERBOSE,
313 		    "%s: received unsupported IAPP message %d",
314 		    iapp->i_iface, frame->hdr.i_command);
315 		return;
316 
317 	default:
318 		return;
319 	}
320 }
321