xref: /openbsd-src/usr.sbin/hostapd/iapp.c (revision cb21588b789e158a41a4783b1dcf238685d192fc)
1*cb21588bSguenther /*	$OpenBSD: iapp.c,v 1.20 2019/05/10 01:29:31 guenther Exp $	*/
2d2b2a2e3Sreyk 
3d2b2a2e3Sreyk /*
42c56d0d6Sreyk  * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org>
5d2b2a2e3Sreyk  *
6d2b2a2e3Sreyk  * Permission to use, copy, modify, and distribute this software for any
7d2b2a2e3Sreyk  * purpose with or without fee is hereby granted, provided that the above
8d2b2a2e3Sreyk  * copyright notice and this permission notice appear in all copies.
9d2b2a2e3Sreyk  *
10d2b2a2e3Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11d2b2a2e3Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12d2b2a2e3Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13d2b2a2e3Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14d2b2a2e3Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15d2b2a2e3Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16d2b2a2e3Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17d2b2a2e3Sreyk  */
18d2b2a2e3Sreyk 
19d2b2a2e3Sreyk #include <sys/ioctl.h>
20d2b2a2e3Sreyk #include <sys/types.h>
21d2b2a2e3Sreyk #include <sys/socket.h>
22d2b2a2e3Sreyk #include <sys/time.h>
2350ce650fSreyk #include <sys/uio.h>
24d2b2a2e3Sreyk 
25d2b2a2e3Sreyk #include <net/if.h>
26d2b2a2e3Sreyk #include <net/if_media.h>
27d2b2a2e3Sreyk #include <net/if_arp.h>
28d2b2a2e3Sreyk #include <net/if_llc.h>
29d2b2a2e3Sreyk #include <net/bpf.h>
30d2b2a2e3Sreyk 
31d2b2a2e3Sreyk #include <netinet/in.h>
32d2b2a2e3Sreyk #include <netinet/if_ether.h>
33d2b2a2e3Sreyk #include <arpa/inet.h>
34d2b2a2e3Sreyk 
35d2b2a2e3Sreyk #include <fcntl.h>
36d2b2a2e3Sreyk #include <stdlib.h>
37d2b2a2e3Sreyk #include <string.h>
38d2b2a2e3Sreyk #include <unistd.h>
39b9fc9a72Sderaadt #include <limits.h>
40d2b2a2e3Sreyk 
41d2b2a2e3Sreyk #include "hostapd.h"
429b9e1fddSreyk #include "iapp.h"
43d2b2a2e3Sreyk 
44d2b2a2e3Sreyk void
hostapd_iapp_init(struct hostapd_config * cfg)45d2b2a2e3Sreyk hostapd_iapp_init(struct hostapd_config *cfg)
46d2b2a2e3Sreyk {
47d01b6ac2Sreyk 	struct hostapd_apme *apme;
487c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
49d01b6ac2Sreyk 
50d2b2a2e3Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
51d2b2a2e3Sreyk 		return;
52d2b2a2e3Sreyk 
53d01b6ac2Sreyk 	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
54d2b2a2e3Sreyk 		/* Get Host AP's BSSID */
55d01b6ac2Sreyk 		hostapd_priv_apme_bssid(apme);
56814ef235Sreyk 		hostapd_log(HOSTAPD_LOG,
57f83005a6Sreyk 		    "%s/%s: attached Host AP interface with BSSID %s",
587c8c4753Sreyk 		    apme->a_iface, iapp->i_iface,
59d01b6ac2Sreyk 		    etheraddr_string(apme->a_bssid));
60de5ff521Sreyk 
61de5ff521Sreyk 		/* Deauthenticate all stations on startup */
62ae7a93f5Sreyk 		(void)hostapd_apme_deauth(apme);
63d01b6ac2Sreyk 	}
64d2b2a2e3Sreyk }
65d2b2a2e3Sreyk 
66d2b2a2e3Sreyk void
hostapd_iapp_term(struct hostapd_config * cfg)67d2b2a2e3Sreyk hostapd_iapp_term(struct hostapd_config *cfg)
68d2b2a2e3Sreyk {
69d01b6ac2Sreyk 	struct hostapd_apme *apme;
707c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
71d01b6ac2Sreyk 
72d2b2a2e3Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
73d2b2a2e3Sreyk 		return;
74d2b2a2e3Sreyk 
75d01b6ac2Sreyk 	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
76d01b6ac2Sreyk 		hostapd_log(HOSTAPD_LOG_VERBOSE,
77f83005a6Sreyk 		    "%s/%s: detaching from Host AP",
787c8c4753Sreyk 		    apme->a_iface, iapp->i_iface);
79d01b6ac2Sreyk 	}
80d2b2a2e3Sreyk }
81d2b2a2e3Sreyk 
82d2b2a2e3Sreyk int
hostapd_iapp_add_notify(struct hostapd_apme * apme,struct hostapd_node * node)83d01b6ac2Sreyk hostapd_iapp_add_notify(struct hostapd_apme *apme, struct hostapd_node *node)
84d2b2a2e3Sreyk {
85d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
867c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
87d2b2a2e3Sreyk 	struct sockaddr_in *addr;
88d2b2a2e3Sreyk 	struct {
89d2b2a2e3Sreyk 		struct ieee80211_iapp_frame hdr;
90d2b2a2e3Sreyk 		struct ieee80211_iapp_add_notify add;
91d2b2a2e3Sreyk 	} __packed frame;
92d2b2a2e3Sreyk 
933a61e91cSreyk 	if ((iapp->i_flags & HOSTAPD_IAPP_F_ADD_NOTIFY) == 0)
943a61e91cSreyk 		return (0);
953a61e91cSreyk 
96d2b2a2e3Sreyk 	/*
97d2b2a2e3Sreyk 	 * Send an ADD.notify message to other access points to notify
98d2b2a2e3Sreyk 	 * about a new association on our Host AP.
99d2b2a2e3Sreyk 	 */
100d2b2a2e3Sreyk 	bzero(&frame, sizeof(frame));
101d2b2a2e3Sreyk 
102d2b2a2e3Sreyk 	frame.hdr.i_version = IEEE80211_IAPP_VERSION;
103d2b2a2e3Sreyk 	frame.hdr.i_command = IEEE80211_IAPP_FRAME_ADD_NOTIFY;
1047c8c4753Sreyk 	frame.hdr.i_identifier = htons(iapp->i_cnt++);
10550ce650fSreyk 	frame.hdr.i_length = sizeof(struct ieee80211_iapp_add_notify);
106d2b2a2e3Sreyk 
107d2b2a2e3Sreyk 	frame.add.a_length = IEEE80211_ADDR_LEN;
108d2b2a2e3Sreyk 	frame.add.a_seqnum = htons(node->ni_rxseq);
109d2b2a2e3Sreyk 	bcopy(node->ni_macaddr, frame.add.a_macaddr, IEEE80211_ADDR_LEN);
110d2b2a2e3Sreyk 
111d2b2a2e3Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST)
1127c8c4753Sreyk 		addr = &iapp->i_broadcast;
113d2b2a2e3Sreyk 	else
1147c8c4753Sreyk 		addr = &iapp->i_multicast;
115d2b2a2e3Sreyk 
1167c8c4753Sreyk 	if (sendto(iapp->i_udp, &frame, sizeof(frame),
117d2b2a2e3Sreyk 	    0, (struct sockaddr *)addr, sizeof(struct sockaddr_in)) == -1) {
118d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG,
119f83005a6Sreyk 		    "%s: failed to send ADD notification: %s",
1207c8c4753Sreyk 		    iapp->i_iface, strerror(errno));
12150ce650fSreyk 		return (errno);
12250ce650fSreyk 	}
12350ce650fSreyk 
124f83005a6Sreyk 	hostapd_log(HOSTAPD_LOG, "%s/%s: sent ADD notification for %s",
1257c8c4753Sreyk 	    apme->a_iface, iapp->i_iface,
126d01b6ac2Sreyk 	    etheraddr_string(frame.add.a_macaddr));
12750ce650fSreyk 
12850ce650fSreyk 	/* Send a LLC XID frame, see llc.c for details */
12950ce650fSreyk 	return (hostapd_priv_llc_xid(cfg, node));
13050ce650fSreyk }
13150ce650fSreyk 
13250ce650fSreyk int
hostapd_iapp_radiotap(struct hostapd_apme * apme,u_int8_t * buf,const u_int len)133d01b6ac2Sreyk hostapd_iapp_radiotap(struct hostapd_apme *apme, u_int8_t *buf,
13450ce650fSreyk     const u_int len)
13550ce650fSreyk {
136d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
1377c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
13850ce650fSreyk 	struct sockaddr_in *addr;
13950ce650fSreyk 	struct ieee80211_iapp_frame hdr;
14050ce650fSreyk 	struct msghdr msg;
14150ce650fSreyk 	struct iovec iov[2];
14250ce650fSreyk 
14350ce650fSreyk 	/*
144dba892ccSsobrado 	 * Send an HOSTAPD.pcap/radiotap message to other access points
1452dd2df06Sreyk 	 * with an appended network dump. This is an hostapd extension to
14650ce650fSreyk 	 * IAPP.
14750ce650fSreyk 	 */
14850ce650fSreyk 	bzero(&hdr, sizeof(hdr));
14950ce650fSreyk 
15050ce650fSreyk 	hdr.i_version = IEEE80211_IAPP_VERSION;
15150ce650fSreyk 	if (cfg->c_apme_dlt == DLT_IEEE802_11_RADIO)
15250ce650fSreyk 		hdr.i_command = IEEE80211_IAPP_FRAME_HOSTAPD_RADIOTAP;
15350ce650fSreyk 	else if (cfg->c_apme_dlt == DLT_IEEE802_11)
15450ce650fSreyk 		hdr.i_command = IEEE80211_IAPP_FRAME_HOSTAPD_PCAP;
15550ce650fSreyk 	else
15650ce650fSreyk 		return (EINVAL);
1577c8c4753Sreyk 	hdr.i_identifier = htons(iapp->i_cnt++);
15850ce650fSreyk 	hdr.i_length = len;
15950ce650fSreyk 
16050ce650fSreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST)
1617c8c4753Sreyk 		addr = &iapp->i_broadcast;
16250ce650fSreyk 	else
1637c8c4753Sreyk 		addr = &iapp->i_multicast;
16450ce650fSreyk 
16550ce650fSreyk 	iov[0].iov_base = &hdr;
16650ce650fSreyk 	iov[0].iov_len = sizeof(hdr);
16750ce650fSreyk 	iov[1].iov_base = buf;
16850ce650fSreyk 	iov[1].iov_len = len;
16950ce650fSreyk 	msg.msg_name = (caddr_t)addr;
17050ce650fSreyk 	msg.msg_namelen = sizeof(struct sockaddr_in);
17150ce650fSreyk 	msg.msg_iov = iov;
17250ce650fSreyk 	msg.msg_iovlen = 2;
17350ce650fSreyk 	msg.msg_control = 0;
17450ce650fSreyk 	msg.msg_controllen = 0;
17550ce650fSreyk 	msg.msg_flags = 0;
17650ce650fSreyk 
1777c8c4753Sreyk 	if (sendmsg(iapp->i_udp, &msg, 0) == -1) {
17850ce650fSreyk 		hostapd_log(HOSTAPD_LOG,
179f83005a6Sreyk 		    "%s: failed to send HOSTAPD %s: %s",
1807c8c4753Sreyk 		    iapp->i_iface, cfg->c_apme_dlt ==
18150ce650fSreyk 		    DLT_IEEE802_11_RADIO ? "radiotap" : "pcap",
182d2b2a2e3Sreyk 		    strerror(errno));
183d2b2a2e3Sreyk 		return (errno);
184d2b2a2e3Sreyk 	}
185d2b2a2e3Sreyk 
18650ce650fSreyk 	return (0);
187d2b2a2e3Sreyk }
188d2b2a2e3Sreyk 
189d2b2a2e3Sreyk void
hostapd_iapp_input(int fd,short sig,void * arg)190d2b2a2e3Sreyk hostapd_iapp_input(int fd, short sig, void *arg)
191d2b2a2e3Sreyk {
192d2b2a2e3Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)arg;
1937c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
194d01b6ac2Sreyk 	struct hostapd_apme *apme;
195d2b2a2e3Sreyk 	struct sockaddr_in addr;
196d2b2a2e3Sreyk 	socklen_t addr_len;
19750ce650fSreyk 	ssize_t len;
198d2b2a2e3Sreyk 	u_int8_t buf[IAPP_MAXSIZE];
199d2b2a2e3Sreyk 	struct hostapd_node node;
200d2b2a2e3Sreyk 	struct ieee80211_iapp_recv {
201d2b2a2e3Sreyk 		struct ieee80211_iapp_frame hdr;
202d2b2a2e3Sreyk 		union {
203d2b2a2e3Sreyk 			struct ieee80211_iapp_add_notify add;
20450ce650fSreyk 			u_int8_t buf[1];
205d2b2a2e3Sreyk 		} u;
206d2b2a2e3Sreyk 	} __packed *frame;
20750ce650fSreyk 	u_int dlt;
208d2b2a2e3Sreyk 	int ret = 0;
209d2b2a2e3Sreyk 
210d2b2a2e3Sreyk 	/* Ignore invalid signals */
211d2b2a2e3Sreyk 	if (sig != EV_READ)
212d2b2a2e3Sreyk 		return;
213d2b2a2e3Sreyk 
214d2b2a2e3Sreyk 	/*
215d2b2a2e3Sreyk 	 * Listen to possible messages from other IAPP
216d2b2a2e3Sreyk 	 */
217d2b2a2e3Sreyk 	bzero(buf, sizeof(buf));
218d2b2a2e3Sreyk 
21950ce650fSreyk 	if ((len = recvfrom(fd, buf, sizeof(buf), 0,
22050ce650fSreyk 	    (struct sockaddr*)&addr, &addr_len)) < 1)
221d2b2a2e3Sreyk 		return;
222d2b2a2e3Sreyk 
2237c8c4753Sreyk 	if (bcmp(&iapp->i_addr.sin_addr, &addr.sin_addr,
224d2b2a2e3Sreyk 	    sizeof(addr.sin_addr)) == 0)
225d2b2a2e3Sreyk 		return;
226d2b2a2e3Sreyk 
227d2b2a2e3Sreyk 	frame = (struct ieee80211_iapp_recv*)buf;
228d2b2a2e3Sreyk 
229d2b2a2e3Sreyk 	/* Validate the IAPP version */
23050ce650fSreyk 	if (len < (ssize_t)sizeof(struct ieee80211_iapp_frame) ||
23150ce650fSreyk 	    frame->hdr.i_version != IEEE80211_IAPP_VERSION ||
232d2b2a2e3Sreyk 	    addr_len < sizeof(struct sockaddr_in))
233d2b2a2e3Sreyk 		return;
234d2b2a2e3Sreyk 
235d2b2a2e3Sreyk 	cfg->c_stats.cn_rx_iapp++;
236d2b2a2e3Sreyk 
237d2b2a2e3Sreyk 	/*
238d2b2a2e3Sreyk 	 * Process the IAPP frame
239d2b2a2e3Sreyk 	 */
240d2b2a2e3Sreyk 	switch (frame->hdr.i_command) {
241d2b2a2e3Sreyk 	case IEEE80211_IAPP_FRAME_ADD_NOTIFY:
24250ce650fSreyk 		/* Short frame */
24350ce650fSreyk 		if (len < (ssize_t)(sizeof(struct ieee80211_iapp_frame) +
24450ce650fSreyk 		    sizeof(struct ieee80211_iapp_add_notify)))
24550ce650fSreyk 			return;
24650ce650fSreyk 
247d2b2a2e3Sreyk 		/* Don't support non-48bit MAC addresses, yet */
248d2b2a2e3Sreyk 		if (frame->u.add.a_length != IEEE80211_ADDR_LEN)
249d2b2a2e3Sreyk 			return;
250d2b2a2e3Sreyk 
251d2b2a2e3Sreyk 		node.ni_rxseq = frame->u.add.a_seqnum;
252d2b2a2e3Sreyk 		bcopy(frame->u.add.a_macaddr, node.ni_macaddr,
253d2b2a2e3Sreyk 		    IEEE80211_ADDR_LEN);
254d2b2a2e3Sreyk 
255d2b2a2e3Sreyk 		/*
256d2b2a2e3Sreyk 		 * Try to remove a node from our Host AP and to free
257d2b2a2e3Sreyk 		 * any allocated resources. Otherwise the received
258d2b2a2e3Sreyk 		 * ADD.notify message will be ignored.
259d2b2a2e3Sreyk 		 */
26074ff1540Sreyk 		if (iapp->i_flags & HOSTAPD_IAPP_F_ADD &&
26174ff1540Sreyk 		    cfg->c_flags & HOSTAPD_CFG_F_APME) {
262d01b6ac2Sreyk 			TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
26374ff1540Sreyk 				if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING)
264ae7a93f5Sreyk 					(void)hostapd_roaming_del(apme, &node);
26574ff1540Sreyk 				if (iapp->i_flags & HOSTAPD_IAPP_F_ADD_NOTIFY &&
26674ff1540Sreyk 				    (ret = hostapd_apme_delnode(apme,
267d01b6ac2Sreyk 				    &node)) == 0)
26850ce650fSreyk 					cfg->c_stats.cn_tx_apme++;
269d01b6ac2Sreyk 			}
27050ce650fSreyk 		} else
271d2b2a2e3Sreyk 			ret = 0;
272d2b2a2e3Sreyk 
27374ff1540Sreyk 		hostapd_log(iapp->i_flags & HOSTAPD_IAPP_F_ADD ?
27474ff1540Sreyk 		    HOSTAPD_LOG : HOSTAPD_LOG_VERBOSE,
275f83005a6Sreyk 		    "%s: %s ADD notification for %s at %s",
2767c8c4753Sreyk 		    iapp->i_iface, ret == 0 ?
27750ce650fSreyk 		    "received" : "ignored",
27850ce650fSreyk 		    etheraddr_string(node.ni_macaddr),
279d2b2a2e3Sreyk 		    inet_ntoa(addr.sin_addr));
280d2b2a2e3Sreyk 		break;
281d2b2a2e3Sreyk 
28250ce650fSreyk 	case IEEE80211_IAPP_FRAME_HOSTAPD_PCAP:
28350ce650fSreyk 	case IEEE80211_IAPP_FRAME_HOSTAPD_RADIOTAP:
2843a61e91cSreyk 		if ((iapp->i_flags & HOSTAPD_IAPP_F_RADIOTAP) == 0)
2853a61e91cSreyk 			return;
2863a61e91cSreyk 
28750ce650fSreyk 		/* Short frame */
28850ce650fSreyk 		if (len <= (ssize_t)sizeof(struct ieee80211_iapp_frame) ||
28950ce650fSreyk 		    frame->hdr.i_length < sizeof(struct ieee80211_frame))
29050ce650fSreyk 			return;
29150ce650fSreyk 
29250ce650fSreyk 		dlt = frame->hdr.i_command ==
29350ce650fSreyk 		    IEEE80211_IAPP_FRAME_HOSTAPD_PCAP ?
29450ce650fSreyk 		    DLT_IEEE802_11 : DLT_IEEE802_11_RADIO;
29550ce650fSreyk 
29650ce650fSreyk 		hostapd_print_ieee80211(dlt, 1, (u_int8_t *)frame->u.buf,
29750ce650fSreyk 		    len - sizeof(struct ieee80211_iapp_frame));
29850ce650fSreyk 		return;
29950ce650fSreyk 
300d2b2a2e3Sreyk 	case IEEE80211_IAPP_FRAME_MOVE_NOTIFY:
301d2b2a2e3Sreyk 	case IEEE80211_IAPP_FRAME_MOVE_RESPONSE:
302d2b2a2e3Sreyk 	case IEEE80211_IAPP_FRAME_SEND_SECURITY_BLOCK:
303d2b2a2e3Sreyk 	case IEEE80211_IAPP_FRAME_ACK_SECURITY_BLOCK:
304d2b2a2e3Sreyk 	case IEEE80211_IAPP_FRAME_CACHE_NOTIFY:
305d2b2a2e3Sreyk 	case IEEE80211_IAPP_FRAME_CACHE_RESPONSE:
306d2b2a2e3Sreyk 
307d2b2a2e3Sreyk 		/*
308d2b2a2e3Sreyk 		 * XXX TODO
309d2b2a2e3Sreyk 		 */
310d2b2a2e3Sreyk 
311d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG_VERBOSE,
312f83005a6Sreyk 		    "%s: received unsupported IAPP message %d",
3137c8c4753Sreyk 		    iapp->i_iface, frame->hdr.i_command);
314d2b2a2e3Sreyk 		return;
315d2b2a2e3Sreyk 
316d2b2a2e3Sreyk 	default:
317d2b2a2e3Sreyk 		return;
318d2b2a2e3Sreyk 	}
319d2b2a2e3Sreyk }
320