xref: /openbsd-src/usr.sbin/hostapd/apme.c (revision cb21588b789e158a41a4783b1dcf238685d192fc)
1*cb21588bSguenther /*	$OpenBSD: apme.c,v 1.17 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 
19b9fc9a72Sderaadt #include <sys/param.h>	/* roundup isclr */
20d2b2a2e3Sreyk #include <sys/ioctl.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 
3550ce650fSreyk #include <net80211/ieee80211_radiotap.h>
3650ce650fSreyk 
37d2b2a2e3Sreyk #include <fcntl.h>
38d2b2a2e3Sreyk #include <stdlib.h>
39d2b2a2e3Sreyk #include <stdio.h>
40d2b2a2e3Sreyk #include <string.h>
41d2b2a2e3Sreyk #include <unistd.h>
42b9fc9a72Sderaadt #include <limits.h>
43d2b2a2e3Sreyk 
44d2b2a2e3Sreyk #include "hostapd.h"
459b9e1fddSreyk #include "iapp.h"
46d2b2a2e3Sreyk 
47d01b6ac2Sreyk void	 hostapd_apme_frame(struct hostapd_apme *, u_int8_t *, u_int);
4801908219Sreyk void	 hostapd_apme_hopper(int, short, void *);
49d01b6ac2Sreyk 
50d01b6ac2Sreyk int
hostapd_apme_add(struct hostapd_config * cfg,const char * name)51d01b6ac2Sreyk hostapd_apme_add(struct hostapd_config *cfg, const char *name)
52d01b6ac2Sreyk {
53d01b6ac2Sreyk 	struct hostapd_apme *apme;
54d01b6ac2Sreyk 
55d01b6ac2Sreyk 	if (hostapd_apme_lookup(cfg, name) != NULL)
56d01b6ac2Sreyk 		return (EEXIST);
57d01b6ac2Sreyk 	if ((apme = (struct hostapd_apme *)
58d01b6ac2Sreyk 	    calloc(1, sizeof(struct hostapd_apme))) == NULL)
59d01b6ac2Sreyk 		return (ENOMEM);
60ae7a93f5Sreyk 	if (strlcpy(apme->a_iface, name, sizeof(apme->a_iface)) >=
61ae7a93f5Sreyk 	    sizeof(apme->a_iface)) {
62ae7a93f5Sreyk 		free(apme);
63ae7a93f5Sreyk 		return (EINVAL);
64ae7a93f5Sreyk 	}
65d01b6ac2Sreyk 
66d01b6ac2Sreyk 	apme->a_cfg = cfg;
6701908219Sreyk 	apme->a_chanavail = NULL;
68d01b6ac2Sreyk 
69d01b6ac2Sreyk 	TAILQ_INSERT_TAIL(&cfg->c_apmes, apme, a_entries);
70d01b6ac2Sreyk 
71d01b6ac2Sreyk 	hostapd_log(HOSTAPD_LOG_DEBUG,
72f83005a6Sreyk 	    "%s: Host AP interface added", apme->a_iface);
73d01b6ac2Sreyk 
74d01b6ac2Sreyk 	return (0);
75d01b6ac2Sreyk }
76d01b6ac2Sreyk 
77de5ff521Sreyk int
hostapd_apme_deauth(struct hostapd_apme * apme)78de5ff521Sreyk hostapd_apme_deauth(struct hostapd_apme *apme)
79de5ff521Sreyk {
80de5ff521Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
817c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
82de5ff521Sreyk 	u_int8_t buf[sizeof(struct ieee80211_frame) + sizeof(u_int16_t)];
83de5ff521Sreyk 	struct ieee80211_frame *wh;
84de5ff521Sreyk 
85de5ff521Sreyk 	bzero(&buf, sizeof(buf));
86de5ff521Sreyk 	wh = (struct ieee80211_frame *)&buf[0];
87de5ff521Sreyk 	wh->i_fc[0] = IEEE80211_FC0_VERSION_0 | IEEE80211_FC0_TYPE_MGT |
88de5ff521Sreyk 	    IEEE80211_FC0_SUBTYPE_DEAUTH;
89de5ff521Sreyk 	wh->i_fc[1] = IEEE80211_FC1_DIR_NODS;
90de5ff521Sreyk 	memset(&wh->i_addr1, 0xff, IEEE80211_ADDR_LEN);
91de5ff521Sreyk 	bcopy(apme->a_bssid, wh->i_addr2, IEEE80211_ADDR_LEN);
92de5ff521Sreyk 	bcopy(apme->a_bssid, wh->i_addr3, IEEE80211_ADDR_LEN);
93de5ff521Sreyk 	*(u_int16_t *)(wh + 1) = htole16(IEEE80211_REASON_AUTH_EXPIRE);
94de5ff521Sreyk 
95de5ff521Sreyk 	if (write(apme->a_raw, buf, sizeof(buf)) == -1) {
96de5ff521Sreyk 		hostapd_log(HOSTAPD_LOG_VERBOSE,
97f83005a6Sreyk 		    "%s/%s: failed to deauthenticate all stations: %s",
987c8c4753Sreyk 		    iapp->i_iface, apme->a_iface,
99de5ff521Sreyk 		    strerror(errno));
100de5ff521Sreyk 		return (EIO);
101de5ff521Sreyk 	}
102de5ff521Sreyk 
103de5ff521Sreyk 	hostapd_log(HOSTAPD_LOG_VERBOSE,
104f83005a6Sreyk 	    "%s/%s: deauthenticated all stations",
1057c8c4753Sreyk 	    apme->a_iface, iapp->i_iface);
106de5ff521Sreyk 
107de5ff521Sreyk 	return (0);
108de5ff521Sreyk }
109de5ff521Sreyk 
110d01b6ac2Sreyk struct hostapd_apme *
hostapd_apme_lookup(struct hostapd_config * cfg,const char * name)111d01b6ac2Sreyk hostapd_apme_lookup(struct hostapd_config *cfg, const char *name)
112d01b6ac2Sreyk {
113d01b6ac2Sreyk 	struct hostapd_apme *apme;
114d01b6ac2Sreyk 
115d01b6ac2Sreyk 	TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
116d01b6ac2Sreyk 		if (strcmp(name, apme->a_iface) == 0)
117d01b6ac2Sreyk 			return (apme);
118d01b6ac2Sreyk 	}
119d01b6ac2Sreyk 
120d01b6ac2Sreyk 	return (NULL);
121d01b6ac2Sreyk }
122d01b6ac2Sreyk 
12301908219Sreyk struct hostapd_apme *
hostapd_apme_addhopper(struct hostapd_config * cfg,const char * name)12401908219Sreyk hostapd_apme_addhopper(struct hostapd_config *cfg, const char *name)
12501908219Sreyk {
12601908219Sreyk 	struct hostapd_apme *apme;
12701908219Sreyk 
12801908219Sreyk 	if ((apme = hostapd_apme_lookup(cfg, name)) == NULL)
12901908219Sreyk 		return (NULL);
13001908219Sreyk 	if (apme->a_chanavail != NULL)
13101908219Sreyk 		return (NULL);
13201908219Sreyk 	apme->a_curchan = IEEE80211_CHAN_MAX;
13301908219Sreyk 	apme->a_maxchan = roundup(IEEE80211_CHAN_MAX, NBBY);
13401908219Sreyk 	if ((apme->a_chanavail = (u_int8_t *)
13501908219Sreyk 	    calloc(apme->a_maxchan, sizeof(u_int8_t))) == NULL)
13601908219Sreyk 		return (NULL);
13701908219Sreyk 	memset(apme->a_chanavail, 0xff,
13801908219Sreyk 	    apme->a_maxchan * sizeof(u_int8_t));
139ae7a93f5Sreyk 	(void)strlcpy(apme->a_chanreq.i_name, apme->a_iface, IFNAMSIZ);
14001908219Sreyk 
14101908219Sreyk 	return (apme);
14201908219Sreyk }
14301908219Sreyk 
14401908219Sreyk void
hostapd_apme_sethopper(struct hostapd_apme * apme,int now)14501908219Sreyk hostapd_apme_sethopper(struct hostapd_apme *apme, int now)
14601908219Sreyk {
14701908219Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
14801908219Sreyk 	struct timeval tv;
14901908219Sreyk 
15001908219Sreyk 	bzero(&tv, sizeof(tv));
15101908219Sreyk 	if (!now)
15201908219Sreyk 		bcopy(&cfg->c_apme_hopdelay, &tv, sizeof(tv));
15301908219Sreyk 
15401908219Sreyk 	if (!evtimer_initialized(&apme->a_chanev))
15501908219Sreyk 		evtimer_set(&apme->a_chanev, hostapd_apme_hopper, apme);
156ae7a93f5Sreyk 	if (evtimer_add(&apme->a_chanev, &tv) == -1)
157ae7a93f5Sreyk 		hostapd_fatal("failed to add hopper event");
15801908219Sreyk }
15901908219Sreyk 
16001908219Sreyk void
hostapd_apme_hopper(int fd,short sig,void * arg)16101908219Sreyk hostapd_apme_hopper(int fd, short sig, void *arg)
16201908219Sreyk {
16301908219Sreyk 	struct hostapd_apme *apme = (struct hostapd_apme *)arg;
16401908219Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
16501908219Sreyk 	int ret;
16601908219Sreyk 
16701908219Sreyk 	if (apme->a_curchan >= IEEE80211_CHAN_MAX)
16801908219Sreyk 		apme->a_curchan = 0;
16901908219Sreyk 
17001908219Sreyk 	do {
17101908219Sreyk 		if (apme->a_curchan >= IEEE80211_CHAN_MAX)
17201908219Sreyk 			return;
17301908219Sreyk 		apme->a_curchan %= IEEE80211_CHAN_MAX;
17401908219Sreyk 		apme->a_curchan++;
17501908219Sreyk 	} while (isclr(apme->a_chanavail, apme->a_curchan));
17601908219Sreyk 
17701908219Sreyk 	apme->a_chanreq.i_channel = apme->a_curchan;
17801908219Sreyk 	if ((ret = ioctl(cfg->c_apme_ctl, SIOCS80211CHANNEL,
17901908219Sreyk 	    &apme->a_chanreq)) != 0) {
18001908219Sreyk 		hostapd_apme_sethopper(apme, 1);
18101908219Sreyk 		return;
18201908219Sreyk 	}
18301908219Sreyk 
18401908219Sreyk 	hostapd_log(HOSTAPD_LOG_DEBUG,
18501908219Sreyk 	    "[priv]: %s setting to channel %d",
18601908219Sreyk 	    apme->a_iface, apme->a_curchan);
18701908219Sreyk 
18801908219Sreyk 	hostapd_apme_sethopper(apme, 0);
18901908219Sreyk }
19001908219Sreyk 
191d01b6ac2Sreyk void
hostapd_apme_term(struct hostapd_apme * apme)192d01b6ac2Sreyk hostapd_apme_term(struct hostapd_apme *apme)
193d01b6ac2Sreyk {
194d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
195d01b6ac2Sreyk 
19601908219Sreyk 	/* Remove the channel hopper, if active */
19701908219Sreyk 	if (apme->a_chanavail != NULL) {
198ae7a93f5Sreyk 		(void)event_del(&apme->a_chanev);
19901908219Sreyk 		free(apme->a_chanavail);
20001908219Sreyk 		apme->a_chanavail = NULL;
20101908219Sreyk 	}
20201908219Sreyk 
203d01b6ac2Sreyk 	/* Kick a specified Host AP interface */
204ae7a93f5Sreyk 	(void)event_del(&apme->a_ev);
205ae7a93f5Sreyk 	if (close(apme->a_raw))
206ae7a93f5Sreyk 		hostapd_fatal("failed to close: %s\n",
207ae7a93f5Sreyk 		    strerror(errno));
208d01b6ac2Sreyk 
209d01b6ac2Sreyk 	TAILQ_REMOVE(&cfg->c_apmes, apme, a_entries);
2107c8c4753Sreyk 
21174ff1540Sreyk 	/* Remove all dynamic roaming addresses */
21274ff1540Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_PRIV)
21374ff1540Sreyk 		hostapd_roaming_term(apme);
21474ff1540Sreyk 
2157c8c4753Sreyk 	hostapd_log(HOSTAPD_LOG_DEBUG,
216f83005a6Sreyk 	    "%s: Host AP interface removed", apme->a_iface);
2177c8c4753Sreyk 
218d01b6ac2Sreyk 	free(apme);
219d01b6ac2Sreyk }
220d2b2a2e3Sreyk 
221d2b2a2e3Sreyk void
hostapd_apme_input(int fd,short sig,void * arg)222d2b2a2e3Sreyk hostapd_apme_input(int fd, short sig, void *arg)
223d2b2a2e3Sreyk {
224d01b6ac2Sreyk 	struct hostapd_apme *apme = (struct hostapd_apme *)arg;
225d2b2a2e3Sreyk 	u_int8_t buf[IAPP_MAXSIZE], *bp, *ep;
226d2b2a2e3Sreyk 	struct bpf_hdr *bph;
227d2b2a2e3Sreyk 	ssize_t len;
228d2b2a2e3Sreyk 
229d2b2a2e3Sreyk 	/* Ignore invalid signals */
230d2b2a2e3Sreyk 	if (sig != EV_READ)
231d2b2a2e3Sreyk 		return;
232d2b2a2e3Sreyk 
233d2b2a2e3Sreyk 	bzero(&buf, sizeof(buf));
234d2b2a2e3Sreyk 
235d2b2a2e3Sreyk 	if ((len = read(fd, buf, sizeof(buf))) <
236d2b2a2e3Sreyk 	    (ssize_t)sizeof(struct ieee80211_frame))
237d2b2a2e3Sreyk 		return;
238d2b2a2e3Sreyk 
239d2b2a2e3Sreyk 	/*
240d2b2a2e3Sreyk 	 * Loop through each frame.
241d2b2a2e3Sreyk 	 */
242d2b2a2e3Sreyk 
243d2b2a2e3Sreyk 	bp = (u_int8_t *)&buf;
244d2b2a2e3Sreyk 	ep = bp + len;
245d2b2a2e3Sreyk 
246d2b2a2e3Sreyk 	while (bp < ep) {
247442bc206Sreyk 		register u_int caplen, hdrlen;
2488018a276Sreyk 
2498018a276Sreyk 		bph = (struct bpf_hdr *)bp;
250d2b2a2e3Sreyk 		caplen = bph->bh_caplen;
251d2b2a2e3Sreyk 		hdrlen = bph->bh_hdrlen;
252d2b2a2e3Sreyk 
253d2b2a2e3Sreyk 		/* Process frame */
254d01b6ac2Sreyk 		hostapd_apme_frame(apme, bp + hdrlen, caplen);
255d2b2a2e3Sreyk 
256d2b2a2e3Sreyk 		bp += BPF_WORDALIGN(caplen + hdrlen);
257d2b2a2e3Sreyk 	}
258d2b2a2e3Sreyk }
259d2b2a2e3Sreyk 
26050ce650fSreyk int
hostapd_apme_output(struct hostapd_apme * apme,struct hostapd_ieee80211_frame * frame)261d01b6ac2Sreyk hostapd_apme_output(struct hostapd_apme *apme,
26250ce650fSreyk     struct hostapd_ieee80211_frame *frame)
26350ce650fSreyk {
26450ce650fSreyk 	struct iovec iov[2];
26550ce650fSreyk 	int iovcnt;
26650ce650fSreyk 	struct ieee80211_frame wh;
26750ce650fSreyk 
26850ce650fSreyk 	bzero(&wh, sizeof(wh));
26950ce650fSreyk 
27050ce650fSreyk 	switch (frame->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
27150ce650fSreyk 	case IEEE80211_FC1_DIR_NODS:
27250ce650fSreyk 		bcopy(frame->i_from, wh.i_addr2, IEEE80211_ADDR_LEN);
27350ce650fSreyk 		bcopy(frame->i_to, wh.i_addr1, IEEE80211_ADDR_LEN);
27450ce650fSreyk 		bcopy(frame->i_bssid, wh.i_addr3, IEEE80211_ADDR_LEN);
27550ce650fSreyk 		break;
27650ce650fSreyk 	case IEEE80211_FC1_DIR_TODS:
27750ce650fSreyk 		bcopy(frame->i_from, wh.i_addr2, IEEE80211_ADDR_LEN);
27850ce650fSreyk 		bcopy(frame->i_to, wh.i_addr3, IEEE80211_ADDR_LEN);
27950ce650fSreyk 		bcopy(frame->i_bssid, wh.i_addr1, IEEE80211_ADDR_LEN);
28050ce650fSreyk 		break;
28150ce650fSreyk 	case IEEE80211_FC1_DIR_FROMDS:
28250ce650fSreyk 		bcopy(frame->i_from, wh.i_addr3, IEEE80211_ADDR_LEN);
28350ce650fSreyk 		bcopy(frame->i_to, wh.i_addr1, IEEE80211_ADDR_LEN);
28450ce650fSreyk 		bcopy(frame->i_bssid, wh.i_addr2, IEEE80211_ADDR_LEN);
28550ce650fSreyk 		break;
28650ce650fSreyk 	default:
28750ce650fSreyk 	case IEEE80211_FC1_DIR_DSTODS:
28850ce650fSreyk 		return (EINVAL);
28950ce650fSreyk 	}
29050ce650fSreyk 
29150ce650fSreyk 	wh.i_fc[0] = IEEE80211_FC0_VERSION_0 | frame->i_fc[0];
29250ce650fSreyk 	wh.i_fc[1] = frame->i_fc[1];
29350ce650fSreyk 	bcopy(frame->i_dur, wh.i_dur, sizeof(wh.i_dur));
29450ce650fSreyk 	bcopy(frame->i_seq, wh.i_seq, sizeof(wh.i_seq));
29550ce650fSreyk 
29650ce650fSreyk 	iovcnt = 1;
29750ce650fSreyk 	iov[0].iov_base = &wh;
29850ce650fSreyk 	iov[0].iov_len = sizeof(struct ieee80211_frame);
29950ce650fSreyk 
30050ce650fSreyk 	if (frame->i_data != NULL && frame->i_data_len > 0) {
30150ce650fSreyk 		iovcnt = 2;
30250ce650fSreyk 		iov[1].iov_base = frame->i_data;
30350ce650fSreyk 		iov[1].iov_len = frame->i_data_len;
30450ce650fSreyk 	}
30550ce650fSreyk 
306d01b6ac2Sreyk 	if (writev(apme->a_raw, iov, iovcnt) == -1)
30750ce650fSreyk 		return (errno);
30850ce650fSreyk 
30950ce650fSreyk 	return (0);
31050ce650fSreyk }
31150ce650fSreyk 
31250ce650fSreyk int
hostapd_apme_offset(struct hostapd_apme * apme,u_int8_t * buf,const u_int len)313d01b6ac2Sreyk hostapd_apme_offset(struct hostapd_apme *apme,
31450ce650fSreyk     u_int8_t *buf, const u_int len)
31550ce650fSreyk {
316d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
31750ce650fSreyk 	struct ieee80211_radiotap_header *rh;
31850ce650fSreyk 	u_int rh_len;
31950ce650fSreyk 
32050ce650fSreyk 	if (cfg->c_apme_dlt == DLT_IEEE802_11)
32150ce650fSreyk 		return (0);
32250ce650fSreyk 	else if (cfg->c_apme_dlt != DLT_IEEE802_11_RADIO)
32350ce650fSreyk 		return (-1);
32450ce650fSreyk 
32550ce650fSreyk 	if (len < sizeof(struct ieee80211_radiotap_header))
32650ce650fSreyk 		return (-1);
32750ce650fSreyk 
32850ce650fSreyk 	rh = (struct ieee80211_radiotap_header*)buf;
32950ce650fSreyk 	rh_len = letoh16(rh->it_len);
33050ce650fSreyk 
33150ce650fSreyk 	if (rh->it_version != 0)
33250ce650fSreyk 		return (-1);
33350ce650fSreyk 	if (len <= rh_len)
33450ce650fSreyk 		return (-1);
33550ce650fSreyk 
33650ce650fSreyk 	return ((int)rh_len);
33750ce650fSreyk }
33850ce650fSreyk 
339d2b2a2e3Sreyk void
hostapd_apme_frame(struct hostapd_apme * apme,u_int8_t * buf,u_int len)340d01b6ac2Sreyk hostapd_apme_frame(struct hostapd_apme *apme, u_int8_t *buf, u_int len)
341d2b2a2e3Sreyk {
342d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
34374ff1540Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
344d01b6ac2Sreyk 	struct hostapd_apme *other_apme;
345d2b2a2e3Sreyk 	struct hostapd_node node;
34650ce650fSreyk 	struct ieee80211_frame *wh;
34750ce650fSreyk 	int offset;
34850ce650fSreyk 
349d01b6ac2Sreyk 	if ((offset = hostapd_apme_offset(apme, buf, len)) < 0)
35050ce650fSreyk 		return;
35150ce650fSreyk 	wh = (struct ieee80211_frame *)(buf + offset);
352d2b2a2e3Sreyk 
353d2b2a2e3Sreyk 	/* Ignore short frames or fragments */
354d2b2a2e3Sreyk 	if (len < sizeof(struct ieee80211_frame))
355d2b2a2e3Sreyk 		return;
356d2b2a2e3Sreyk 
35750ce650fSreyk 	/* Handle received frames */
358d01b6ac2Sreyk 	if ((hostapd_handle_input(apme, buf, len) ==
35950ce650fSreyk 	    (HOSTAPD_FRAME_F_RET_SKIP >> HOSTAPD_FRAME_F_RET_S)) ||
36050ce650fSreyk 	    cfg->c_flags & HOSTAPD_CFG_F_IAPP_PASSIVE)
36150ce650fSreyk 		return;
36250ce650fSreyk 
363d2b2a2e3Sreyk 	/*
364d2b2a2e3Sreyk 	 * Only accept local association response frames, ...
365d2b2a2e3Sreyk 	 */
366d2b2a2e3Sreyk 	if (!((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
367d2b2a2e3Sreyk 	    IEEE80211_FC1_DIR_NODS &&
368d2b2a2e3Sreyk 	    (wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
369d2b2a2e3Sreyk 	    IEEE80211_FC0_TYPE_MGT &&
370d2b2a2e3Sreyk 	    (wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
371d2b2a2e3Sreyk 	    IEEE80211_FC0_SUBTYPE_ASSOC_RESP))
372d2b2a2e3Sreyk 		return;
373d2b2a2e3Sreyk 
374d2b2a2e3Sreyk 	/*
375d2b2a2e3Sreyk 	 * ...sent by the Host AP (addr2) to our BSSID (addr3)
376d2b2a2e3Sreyk 	 */
377d01b6ac2Sreyk 	if (bcmp(wh->i_addr2, apme->a_bssid, IEEE80211_ADDR_LEN) != 0 ||
378d01b6ac2Sreyk 	    bcmp(wh->i_addr3, apme->a_bssid, IEEE80211_ADDR_LEN) != 0)
379d2b2a2e3Sreyk 		return;
380d2b2a2e3Sreyk 
381d2b2a2e3Sreyk 	cfg->c_stats.cn_rx_apme++;
382d2b2a2e3Sreyk 
383d2b2a2e3Sreyk 	/*
384d2b2a2e3Sreyk 	 * Double-check if the station got associated to our Host AP
385d2b2a2e3Sreyk 	 */
386d2b2a2e3Sreyk 	bcopy(wh->i_addr1, node.ni_macaddr, IEEE80211_ADDR_LEN);
387d01b6ac2Sreyk 	if (hostapd_priv_apme_getnode(apme, &node) != 0) {
388d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG_DEBUG,
389f83005a6Sreyk 		    "%s: invalid association from %s on the Host AP",
390d01b6ac2Sreyk 		    apme->a_iface, etheraddr_string(wh->i_addr1));
391d2b2a2e3Sreyk 		return;
392d2b2a2e3Sreyk 	}
39350ce650fSreyk 	cfg->c_stats.cn_tx_apme++;
394d2b2a2e3Sreyk 
395d01b6ac2Sreyk 	/*
396d01b6ac2Sreyk 	 * Delete node on other attached Host APs
397d01b6ac2Sreyk 	 */
398d01b6ac2Sreyk 	TAILQ_FOREACH(other_apme, &cfg->c_apmes, a_entries) {
399d01b6ac2Sreyk 		if (apme == other_apme)
400d01b6ac2Sreyk 			continue;
40174ff1540Sreyk 		if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING)
402ae7a93f5Sreyk 			(void)hostapd_roaming_del(other_apme, &node);
403d01b6ac2Sreyk 		if (hostapd_apme_delnode(other_apme, &node) == 0)
404d01b6ac2Sreyk 			cfg->c_stats.cn_tx_apme++;
405d01b6ac2Sreyk 	}
406d01b6ac2Sreyk 
40774ff1540Sreyk 	if (iapp->i_flags & HOSTAPD_IAPP_F_ROAMING)
408ae7a93f5Sreyk 		(void)hostapd_roaming_add(apme, &node);
40974ff1540Sreyk 
410ae7a93f5Sreyk 	(void)hostapd_iapp_add_notify(apme, &node);
411d2b2a2e3Sreyk }
412d2b2a2e3Sreyk 
413d2b2a2e3Sreyk void
hostapd_apme_init(struct hostapd_apme * apme)414d01b6ac2Sreyk hostapd_apme_init(struct hostapd_apme *apme)
415d2b2a2e3Sreyk {
416d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
417d2b2a2e3Sreyk 	u_int i, dlt;
418d2b2a2e3Sreyk 	struct ifreq ifr;
419d2b2a2e3Sreyk 
420d01b6ac2Sreyk 	apme->a_raw = hostapd_bpf_open(O_RDWR);
421d2b2a2e3Sreyk 
422d01b6ac2Sreyk 	apme->a_rawlen = IAPP_MAXSIZE;
423d01b6ac2Sreyk 	if (ioctl(apme->a_raw, BIOCSBLEN, &apme->a_rawlen) == -1)
424d2b2a2e3Sreyk 		hostapd_fatal("failed to set BPF buffer len \"%s\": %s\n",
425d01b6ac2Sreyk 		    apme->a_iface, strerror(errno));
426d2b2a2e3Sreyk 
427d2b2a2e3Sreyk 	i = 1;
428d01b6ac2Sreyk 	if (ioctl(apme->a_raw, BIOCIMMEDIATE, &i) == -1)
429804d065bShenning 		hostapd_fatal("failed to set BPF immediate mode on \"%s\": "
430d01b6ac2Sreyk 		    "%s\n", apme->a_iface, strerror(errno));
431d2b2a2e3Sreyk 
432d2b2a2e3Sreyk 	bzero(&ifr, sizeof(struct ifreq));
433ae7a93f5Sreyk 	(void)strlcpy(ifr.ifr_name, apme->a_iface, sizeof(ifr.ifr_name));
434d2b2a2e3Sreyk 
435d2b2a2e3Sreyk 	/* This may fail, ignore it */
436ae7a93f5Sreyk 	(void)ioctl(apme->a_raw, BIOCPROMISC, NULL);
437d2b2a2e3Sreyk 
438d2b2a2e3Sreyk 	/* Associate the wireless network interface to the BPF descriptor */
439d01b6ac2Sreyk 	if (ioctl(apme->a_raw, BIOCSETIF, &ifr) == -1)
440d2b2a2e3Sreyk 		hostapd_fatal("failed to set BPF interface \"%s\": %s\n",
441d01b6ac2Sreyk 		    apme->a_iface, strerror(errno));
442d2b2a2e3Sreyk 
44350ce650fSreyk 	dlt = cfg->c_apme_dlt;
444d01b6ac2Sreyk 	if (ioctl(apme->a_raw, BIOCSDLT, &dlt) == -1)
445d2b2a2e3Sreyk 		hostapd_fatal("failed to set BPF link type on \"%s\": %s\n",
446d01b6ac2Sreyk 		    apme->a_iface, strerror(errno));
447d2b2a2e3Sreyk 
448d2b2a2e3Sreyk 	/* Lock the BPF descriptor, no further configuration */
449d01b6ac2Sreyk 	if (ioctl(apme->a_raw, BIOCLOCK, NULL) == -1)
450d2b2a2e3Sreyk 		hostapd_fatal("failed to lock BPF interface on \"%s\": %s\n",
451d01b6ac2Sreyk 		    apme->a_iface, strerror(errno));
452d2b2a2e3Sreyk }
45350ce650fSreyk 
45450ce650fSreyk int
hostapd_apme_addnode(struct hostapd_apme * apme,struct hostapd_node * node)455d01b6ac2Sreyk hostapd_apme_addnode(struct hostapd_apme *apme, struct hostapd_node *node)
45650ce650fSreyk {
457d01b6ac2Sreyk 	return (hostapd_priv_apme_setnode(apme, node, 1));
45850ce650fSreyk }
45950ce650fSreyk 
46050ce650fSreyk int
hostapd_apme_delnode(struct hostapd_apme * apme,struct hostapd_node * node)461d01b6ac2Sreyk hostapd_apme_delnode(struct hostapd_apme *apme, struct hostapd_node *node)
46250ce650fSreyk {
463d01b6ac2Sreyk 	return (hostapd_priv_apme_setnode(apme, node, 0));
46450ce650fSreyk }
465