xref: /openbsd-src/usr.sbin/hostapd/handle.c (revision cb21588b789e158a41a4783b1dcf238685d192fc)
1*cb21588bSguenther /*	$OpenBSD: handle.c,v 1.13 2019/05/10 01:29:31 guenther Exp $	*/
250ce650fSreyk 
350ce650fSreyk /*
49a571ad9Sreyk  * Copyright (c) 2005, 2006 Reyk Floeter <reyk@openbsd.org>
550ce650fSreyk  *
650ce650fSreyk  * Permission to use, copy, modify, and distribute this software for any
750ce650fSreyk  * purpose with or without fee is hereby granted, provided that the above
850ce650fSreyk  * copyright notice and this permission notice appear in all copies.
950ce650fSreyk  *
1050ce650fSreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1150ce650fSreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1250ce650fSreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1350ce650fSreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1450ce650fSreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1550ce650fSreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1650ce650fSreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1750ce650fSreyk  */
1850ce650fSreyk 
1950ce650fSreyk #include <sys/ioctl.h>
2050ce650fSreyk #include <sys/types.h>
2150ce650fSreyk #include <sys/socket.h>
2250ce650fSreyk #include <sys/time.h>
2350ce650fSreyk 
2450ce650fSreyk #include <net/if.h>
2550ce650fSreyk #include <net/if_media.h>
2650ce650fSreyk #include <net/if_arp.h>
2750ce650fSreyk #include <net/if_llc.h>
2850ce650fSreyk #include <net/bpf.h>
2950ce650fSreyk 
3050ce650fSreyk #include <netinet/in.h>
3150ce650fSreyk #include <netinet/if_ether.h>
3250ce650fSreyk #include <arpa/inet.h>
3350ce650fSreyk 
349a571ad9Sreyk #include <net80211/ieee80211.h>
359a571ad9Sreyk #include <net80211/ieee80211_radiotap.h>
369a571ad9Sreyk 
3750ce650fSreyk #include <fcntl.h>
3850ce650fSreyk #include <stdlib.h>
3950ce650fSreyk #include <stdio.h>
4050ce650fSreyk #include <string.h>
4150ce650fSreyk #include <unistd.h>
42b9fc9a72Sderaadt #include <limits.h>
4350ce650fSreyk 
4450ce650fSreyk #include "hostapd.h"
4550ce650fSreyk 
46d01b6ac2Sreyk int	 hostapd_handle_frame(struct hostapd_apme *, struct hostapd_frame *,
4750ce650fSreyk 	    u_int8_t *, const u_int);
48d01b6ac2Sreyk int	 hostapd_handle_action(struct hostapd_apme *, struct hostapd_frame *,
4950ce650fSreyk 	    u_int8_t *, u_int8_t *, u_int8_t *, u_int8_t *, const u_int);
5050ce650fSreyk void	 hostapd_handle_addr(const u_int32_t, u_int32_t *, u_int8_t *,
5150ce650fSreyk 	    u_int8_t *, struct hostapd_table *);
5250ce650fSreyk void	 hostapd_handle_ref(u_int, u_int, u_int8_t *, u_int8_t *, u_int8_t *,
5350ce650fSreyk 	    u_int8_t *);
549a571ad9Sreyk int	 hostapd_handle_radiotap(struct hostapd_radiotap *, u_int8_t *,
559a571ad9Sreyk 	    const u_int);
569a571ad9Sreyk int	 hostapd_cmp(enum hostapd_op, int, int);
5750ce650fSreyk 
5850ce650fSreyk int
hostapd_handle_input(struct hostapd_apme * apme,u_int8_t * buf,u_int len)59d01b6ac2Sreyk hostapd_handle_input(struct hostapd_apme *apme, u_int8_t *buf, u_int len)
6050ce650fSreyk {
61d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
6250ce650fSreyk 	struct hostapd_frame *frame;
63d01b6ac2Sreyk 	int ret;
6450ce650fSreyk 
6550ce650fSreyk 	TAILQ_FOREACH(frame, &cfg->c_frames, f_entries) {
66d01b6ac2Sreyk 		if ((ret = hostapd_handle_frame(apme, frame, buf, len)) != 0)
6750ce650fSreyk 			return (ret);
6850ce650fSreyk 	}
6950ce650fSreyk 
7050ce650fSreyk 	return (0);
7150ce650fSreyk }
7250ce650fSreyk 
7350ce650fSreyk void
hostapd_handle_addr(const u_int32_t mask,u_int32_t * flags,u_int8_t * addr,u_int8_t * maddr,struct hostapd_table * table)7450ce650fSreyk hostapd_handle_addr(const u_int32_t mask, u_int32_t *flags, u_int8_t *addr,
7550ce650fSreyk     u_int8_t *maddr, struct hostapd_table *table)
7650ce650fSreyk {
7750ce650fSreyk 	int ret = 0;
7850ce650fSreyk 
7950ce650fSreyk 	if ((*flags & mask) & HOSTAPD_FRAME_TABLE) {
8050ce650fSreyk 		if (hostapd_entry_lookup(table, addr) == NULL)
8150ce650fSreyk 			ret = 1;
8250ce650fSreyk 	} else if (bcmp(addr, maddr, IEEE80211_ADDR_LEN) != 0)
8350ce650fSreyk 			ret = 1;
8450ce650fSreyk 
8550ce650fSreyk 	if ((ret == 1 && (*flags & mask) & HOSTAPD_FRAME_N) ||
8650ce650fSreyk 	    (ret == 0 && ((*flags & mask) & HOSTAPD_FRAME_N) == 0))
8750ce650fSreyk 		*flags &= ~mask;
8850ce650fSreyk }
8950ce650fSreyk 
9050ce650fSreyk void
hostapd_handle_ref(u_int flags,u_int shift,u_int8_t * wfrom,u_int8_t * wto,u_int8_t * wbssid,u_int8_t * addr)9150ce650fSreyk hostapd_handle_ref(u_int flags, u_int shift, u_int8_t *wfrom, u_int8_t *wto,
9250ce650fSreyk     u_int8_t *wbssid, u_int8_t *addr)
9350ce650fSreyk {
9450ce650fSreyk 	if (flags & (HOSTAPD_ACTION_F_REF_FROM << shift))
9550ce650fSreyk 		bcopy(wfrom, addr, IEEE80211_ADDR_LEN);
9650ce650fSreyk 	else if (flags & (HOSTAPD_ACTION_F_REF_TO << shift))
9750ce650fSreyk 		bcopy(wto, addr, IEEE80211_ADDR_LEN);
9850ce650fSreyk 	else if (flags & (HOSTAPD_ACTION_F_REF_BSSID << shift))
9950ce650fSreyk 		bcopy(wbssid, addr, IEEE80211_ADDR_LEN);
1008b938b68Sreyk 	else if (flags & (HOSTAPD_ACTION_F_REF_RANDOM << shift)) {
1018b938b68Sreyk 		hostapd_randval(addr, IEEE80211_ADDR_LEN);
1028b938b68Sreyk 		/* Avoid multicast/broadcast addresses */
1038b938b68Sreyk 		addr[0] &= ~0x1;
1048b938b68Sreyk 	}
10550ce650fSreyk }
10650ce650fSreyk 
10750ce650fSreyk int
hostapd_handle_frame(struct hostapd_apme * apme,struct hostapd_frame * frame,u_int8_t * buf,const u_int len)108d01b6ac2Sreyk hostapd_handle_frame(struct hostapd_apme *apme, struct hostapd_frame *frame,
10950ce650fSreyk     u_int8_t *buf, const u_int len)
11050ce650fSreyk {
1119a571ad9Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
11250ce650fSreyk 	struct ieee80211_frame *wh;
11350ce650fSreyk 	struct hostapd_ieee80211_frame *mh;
1149a571ad9Sreyk 	struct hostapd_radiotap rtap;
11550ce650fSreyk 	u_int8_t *wfrom, *wto, *wbssid;
11650ce650fSreyk 	struct timeval t_now;
11750ce650fSreyk 	u_int32_t flags;
1189a571ad9Sreyk 	int offset, min_rate = 0, val;
11950ce650fSreyk 
120d01b6ac2Sreyk 	if ((offset = hostapd_apme_offset(apme, buf, len)) < 0)
12150ce650fSreyk 		return (0);
12250ce650fSreyk 	wh = (struct ieee80211_frame *)(buf + offset);
12350ce650fSreyk 
12450ce650fSreyk 	mh = &frame->f_frame;
12550ce650fSreyk 	flags = frame->f_flags;
12650ce650fSreyk 
1279834aad8Sreyk 	/* Get timestamp */
128ae7a93f5Sreyk 	if (gettimeofday(&t_now, NULL) == -1)
129ae7a93f5Sreyk 		hostapd_fatal("gettimeofday");
1309834aad8Sreyk 
13150ce650fSreyk 	/* Handle optional limit */
13250ce650fSreyk 	if (frame->f_limit.tv_sec || frame->f_limit.tv_usec) {
13350ce650fSreyk 		if (timercmp(&t_now, &frame->f_then, <))
13450ce650fSreyk 			return (0);
13550ce650fSreyk 		timeradd(&t_now, &frame->f_limit, &frame->f_then);
13650ce650fSreyk 	}
13750ce650fSreyk 
13850ce650fSreyk 	switch (wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) {
13950ce650fSreyk 	case IEEE80211_FC1_DIR_NODS:
14050ce650fSreyk 		wfrom = wh->i_addr2;
14150ce650fSreyk 		wto = wh->i_addr1;
14250ce650fSreyk 		wbssid = wh->i_addr3;
14350ce650fSreyk 		break;
14450ce650fSreyk 	case IEEE80211_FC1_DIR_TODS:
14550ce650fSreyk 		wfrom = wh->i_addr2;
14650ce650fSreyk 		wto = wh->i_addr3;
14750ce650fSreyk 		wbssid = wh->i_addr1;
14850ce650fSreyk 		break;
14950ce650fSreyk 	case IEEE80211_FC1_DIR_FROMDS:
15050ce650fSreyk 		wfrom = wh->i_addr3;
15150ce650fSreyk 		wto = wh->i_addr1;
15250ce650fSreyk 		wbssid = wh->i_addr2;
15350ce650fSreyk 		break;
15450ce650fSreyk 	default:
15550ce650fSreyk 	case IEEE80211_FC1_DIR_DSTODS:
15650ce650fSreyk 		return (0);
15750ce650fSreyk 	}
15850ce650fSreyk 
1595058e176Sreyk 	if (flags & HOSTAPD_FRAME_F_APME_M) {
1605058e176Sreyk 		if (frame->f_apme == NULL)
1615058e176Sreyk 			return (0);
1625058e176Sreyk 		/* Match hostap interface */
1635058e176Sreyk 		if ((flags & HOSTAPD_FRAME_F_APME &&
1645058e176Sreyk 		    apme == frame->f_apme) ||
1655058e176Sreyk 		    (flags & HOSTAPD_FRAME_F_APME_N &&
1665058e176Sreyk 		    apme != frame->f_apme))
1675058e176Sreyk 			flags &= ~HOSTAPD_FRAME_F_APME_M;
1685058e176Sreyk 	}
1695058e176Sreyk 
17050ce650fSreyk 	if (flags & HOSTAPD_FRAME_F_TYPE) {
17150ce650fSreyk 		/* type $type */
17250ce650fSreyk 		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) ==
17350ce650fSreyk 		    (mh->i_fc[0] & IEEE80211_FC0_TYPE_MASK))
17450ce650fSreyk 			flags &= ~HOSTAPD_FRAME_F_TYPE;
17550ce650fSreyk 	} else if (flags & HOSTAPD_FRAME_F_TYPE_N) {
17650ce650fSreyk 		/* type !$type */
17750ce650fSreyk 		if ((wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK) !=
17850ce650fSreyk 		    (mh->i_fc[0] & IEEE80211_FC0_TYPE_MASK))
17950ce650fSreyk 			flags &= ~HOSTAPD_FRAME_F_TYPE_N;
18050ce650fSreyk 	}
18150ce650fSreyk 
18250ce650fSreyk 	if (flags & HOSTAPD_FRAME_F_SUBTYPE) {
18350ce650fSreyk 		/* subtype $subtype */
18450ce650fSreyk 		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) ==
18550ce650fSreyk 		    (mh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK))
18650ce650fSreyk 			flags &= ~HOSTAPD_FRAME_F_SUBTYPE;
18750ce650fSreyk 	} else if (flags & HOSTAPD_FRAME_F_SUBTYPE_N) {
18850ce650fSreyk 		/* subtype !$subtype */
18950ce650fSreyk 		if ((wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK) !=
19050ce650fSreyk 		    (mh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK))
19150ce650fSreyk 			flags &= ~HOSTAPD_FRAME_F_SUBTYPE_N;
19250ce650fSreyk 	}
19350ce650fSreyk 
19450ce650fSreyk 	if (flags & HOSTAPD_FRAME_F_DIR) {
19550ce650fSreyk 		/* dir $dir */
19650ce650fSreyk 		if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) ==
19750ce650fSreyk 		    (mh->i_fc[1] & IEEE80211_FC1_DIR_MASK))
19850ce650fSreyk 			flags &= ~HOSTAPD_FRAME_F_DIR;
19950ce650fSreyk 	} else if (flags & HOSTAPD_FRAME_F_DIR_N) {
20050ce650fSreyk 		/* dir !$dir */
20150ce650fSreyk 		if ((wh->i_fc[1] & IEEE80211_FC1_DIR_MASK) !=
20250ce650fSreyk 		    (mh->i_fc[1] & IEEE80211_FC1_DIR_MASK))
20350ce650fSreyk 			flags &= ~HOSTAPD_FRAME_F_DIR_N;
20450ce650fSreyk 	}
20550ce650fSreyk 
20650ce650fSreyk 	/* from/to/bssid [!]$addr/<table> */
20750ce650fSreyk 	hostapd_handle_addr(HOSTAPD_FRAME_F_FROM_M, &flags, wfrom,
20850ce650fSreyk 	    mh->i_from, frame->f_from);
20950ce650fSreyk 	hostapd_handle_addr(HOSTAPD_FRAME_F_TO_M, &flags, wto,
21050ce650fSreyk 	    mh->i_to, frame->f_to);
21150ce650fSreyk 	hostapd_handle_addr(HOSTAPD_FRAME_F_BSSID_M, &flags, wbssid,
21250ce650fSreyk 	    mh->i_bssid, frame->f_bssid);
21350ce650fSreyk 
2149a571ad9Sreyk 	/* parse the optional radiotap header if required */
2159a571ad9Sreyk 	if (frame->f_radiotap) {
2169a571ad9Sreyk 		if (hostapd_handle_radiotap(&rtap, buf, len) != 0)
2179a571ad9Sreyk 			return (0);
2189a571ad9Sreyk 		else if ((rtap.r_present & frame->f_radiotap) !=
2199a571ad9Sreyk 		    frame->f_radiotap) {
2209a571ad9Sreyk 			cfg->c_stats.cn_rtap_miss++;
2219a571ad9Sreyk 			return (0);
2229a571ad9Sreyk 		}
2239a571ad9Sreyk 		if (flags & HOSTAPD_FRAME_F_RSSI && rtap.r_max_rssi) {
2249a571ad9Sreyk 			val = ((float)rtap.r_rssi / rtap.r_max_rssi) * 100;
2259a571ad9Sreyk 			if (hostapd_cmp(frame->f_rssi_op,
2269a571ad9Sreyk 			    val, frame->f_rssi))
2279a571ad9Sreyk 				flags &= ~HOSTAPD_FRAME_F_RSSI;
2289a571ad9Sreyk 		}
2299a571ad9Sreyk 		if (flags & HOSTAPD_FRAME_F_RATE) {
2309a571ad9Sreyk 			val = rtap.r_txrate;
2319a571ad9Sreyk 			if (hostapd_cmp(frame->f_txrate_op,
2329a571ad9Sreyk 			    val, frame->f_txrate))
2339a571ad9Sreyk 				flags &= ~HOSTAPD_FRAME_F_RATE;
2349a571ad9Sreyk 		}
2359a571ad9Sreyk 		if (flags & HOSTAPD_FRAME_F_CHANNEL) {
2369a571ad9Sreyk 			val = rtap.r_chan;
2379a571ad9Sreyk 			if (hostapd_cmp(frame->f_chan_op,
2389a571ad9Sreyk 			    val, frame->f_chan))
2399a571ad9Sreyk 				flags &= ~HOSTAPD_FRAME_F_CHANNEL;
2409a571ad9Sreyk 		}
2419a571ad9Sreyk 	}
2429a571ad9Sreyk 
24350ce650fSreyk 	/* Handle if frame matches */
24450ce650fSreyk 	if ((flags & HOSTAPD_FRAME_F_M) != 0)
24550ce650fSreyk 		return (0);
24650ce650fSreyk 
2479834aad8Sreyk 	/* Handle optional minimal rate */
2489834aad8Sreyk 	if (frame->f_rate && frame->f_rate_intval) {
2499834aad8Sreyk 		frame->f_rate_delay = t_now.tv_sec - frame->f_last.tv_sec;
2509834aad8Sreyk 		if (frame->f_rate_delay < frame->f_rate_intval) {
2519834aad8Sreyk 			frame->f_rate_cnt++;
2529834aad8Sreyk 			if (frame->f_rate_cnt < frame->f_rate)
2539834aad8Sreyk 				min_rate = 1;
2549834aad8Sreyk 		} else {
2559834aad8Sreyk 			min_rate = 1;
2569834aad8Sreyk 			frame->f_rate_cnt = 0;
2579834aad8Sreyk 		}
2589834aad8Sreyk 	}
2599834aad8Sreyk 
2609834aad8Sreyk 	/* Update timestamp for the last match of this event */
2619834aad8Sreyk 	if (frame->f_rate_cnt == 0 || min_rate == 0)
2629834aad8Sreyk 		bcopy(&t_now, &frame->f_last, sizeof(struct timeval));
2639834aad8Sreyk 
2649834aad8Sreyk 	/* Return if the minimal rate is not reached, yet */
2659834aad8Sreyk 	if (min_rate)
2669834aad8Sreyk 		return (0);
2679834aad8Sreyk 
268d01b6ac2Sreyk 	if (hostapd_handle_action(apme, frame, wfrom, wto, wbssid, buf,
26950ce650fSreyk 	    len) != 0)
27050ce650fSreyk 		return (0);
27150ce650fSreyk 
2729834aad8Sreyk 	/* Reset minimal rate counter after successfully handled the frame */
2739834aad8Sreyk 	frame->f_rate_cnt = 0;
2749834aad8Sreyk 
27550ce650fSreyk 	return ((frame->f_flags & HOSTAPD_FRAME_F_RET_M) >>
27650ce650fSreyk 	    HOSTAPD_FRAME_F_RET_S);
27750ce650fSreyk }
27850ce650fSreyk 
27950ce650fSreyk int
hostapd_handle_action(struct hostapd_apme * apme,struct hostapd_frame * frame,u_int8_t * wfrom,u_int8_t * wto,u_int8_t * wbssid,u_int8_t * buf,const u_int len)280d01b6ac2Sreyk hostapd_handle_action(struct hostapd_apme *apme, struct hostapd_frame *frame,
28150ce650fSreyk     u_int8_t *wfrom, u_int8_t *wto, u_int8_t *wbssid, u_int8_t *buf,
28250ce650fSreyk     const u_int len)
28350ce650fSreyk {
284d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
2857c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
28650ce650fSreyk 	struct hostapd_action_data *action = &frame->f_action_data;
28750ce650fSreyk 	struct hostapd_node node;
28850ce650fSreyk 	u_int8_t *lladdr = NULL;
28950ce650fSreyk 	int ret = 0, offset;
29050ce650fSreyk 
29150ce650fSreyk 	switch (frame->f_action) {
29250ce650fSreyk 	case HOSTAPD_ACTION_RADIOTAP:
29350ce650fSreyk 		/* Send IAPP frame with radiotap/pcap payload */
294d01b6ac2Sreyk 		if ((ret = hostapd_iapp_radiotap(apme, buf, len)) != 0)
29550ce650fSreyk 			return (ret);
29650ce650fSreyk 
29750ce650fSreyk 		if ((frame->f_action_flags & HOSTAPD_ACTION_VERBOSE) == 0)
29850ce650fSreyk 			return (0);
29950ce650fSreyk 
30050ce650fSreyk 		hostapd_log(HOSTAPD_LOG,
301f83005a6Sreyk 		    "%s: sent IAPP frame HOSTAPD_%s (%u bytes)",
3027c8c4753Sreyk 		    iapp->i_iface, cfg->c_apme_dlt ==
30350ce650fSreyk 		    DLT_IEEE802_11_RADIO ? "RADIOTAP" : "PCAP", len);
30450ce650fSreyk 		break;
30550ce650fSreyk 
30650ce650fSreyk 	case HOSTAPD_ACTION_LOG:
30750ce650fSreyk 		/* Log frame to syslog/stderr */
3089834aad8Sreyk 		if (frame->f_rate && frame->f_rate_intval) {
3099834aad8Sreyk 			hostapd_printf("%s: (rate: %ld/%ld sec) ",
310d01b6ac2Sreyk 			    apme->a_iface, frame->f_rate_cnt,
3119834aad8Sreyk 			    frame->f_rate_delay + 1);
3129834aad8Sreyk 		} else
313d01b6ac2Sreyk 			hostapd_printf("%s: ", apme->a_iface);
31450ce650fSreyk 
31550ce650fSreyk 		hostapd_print_ieee80211(cfg->c_apme_dlt, frame->f_action_flags &
31650ce650fSreyk 		    HOSTAPD_ACTION_VERBOSE, buf, len);
31750ce650fSreyk 
31850ce650fSreyk 		/* Flush output buffer */
31950ce650fSreyk 		hostapd_printf(NULL);
32050ce650fSreyk 		break;
32150ce650fSreyk 
32250ce650fSreyk 	case HOSTAPD_ACTION_DELNODE:
32350ce650fSreyk 	case HOSTAPD_ACTION_ADDNODE:
32450ce650fSreyk 		bzero(&node, sizeof(node));
32550ce650fSreyk 
32650ce650fSreyk 		if (action->a_flags & HOSTAPD_ACTION_F_REF_FROM)
32750ce650fSreyk 			lladdr = wfrom;
32850ce650fSreyk 		else if (action->a_flags & HOSTAPD_ACTION_F_REF_TO)
32950ce650fSreyk 			lladdr = wto;
33050ce650fSreyk 		else if (action->a_flags & HOSTAPD_ACTION_F_REF_BSSID)
33150ce650fSreyk 			lladdr = wbssid;
33250ce650fSreyk 		else
33350ce650fSreyk 			lladdr = action->a_lladdr;
33450ce650fSreyk 
33550ce650fSreyk 		bcopy(lladdr, &node.ni_macaddr, IEEE80211_ADDR_LEN);
33650ce650fSreyk 
337132ac06bSreyk 		if (frame->f_action == HOSTAPD_ACTION_DELNODE)
338d01b6ac2Sreyk 			ret = hostapd_apme_delnode(apme, &node);
33950ce650fSreyk 		else
340d01b6ac2Sreyk 			ret = hostapd_apme_addnode(apme, &node);
34150ce650fSreyk 
34250ce650fSreyk 		if (ret != 0)  {
34350ce650fSreyk 			hostapd_log(HOSTAPD_LOG_DEBUG,
344f83005a6Sreyk 			    "%s: node add/delete %s failed: %s",
345d01b6ac2Sreyk 			    apme->a_iface, etheraddr_string(lladdr),
34650ce650fSreyk 			    strerror(ret));
34750ce650fSreyk 		}
34850ce650fSreyk 		break;
34950ce650fSreyk 
35050ce650fSreyk 	case HOSTAPD_ACTION_NONE:
35150ce650fSreyk 		/* Nothing */
35250ce650fSreyk 		break;
35350ce650fSreyk 
35450ce650fSreyk 	case HOSTAPD_ACTION_RESEND:
35550ce650fSreyk 		/* Resend received raw IEEE 802.11 frame */
356d01b6ac2Sreyk 		if ((offset = hostapd_apme_offset(apme, buf, len)) < 0)
35750ce650fSreyk 			return (EINVAL);
358d01b6ac2Sreyk 		if (write(apme->a_raw, buf + offset, len - offset) == -1)
35950ce650fSreyk 			ret = errno;
36050ce650fSreyk 		break;
36150ce650fSreyk 
36250ce650fSreyk 	case HOSTAPD_ACTION_FRAME:
36350ce650fSreyk 		if (action->a_flags & HOSTAPD_ACTION_F_REF_M) {
36450ce650fSreyk 			hostapd_handle_ref(action->a_flags &
36550ce650fSreyk 			    HOSTAPD_ACTION_F_REF_FROM_M,
36650ce650fSreyk 			    HOSTAPD_ACTION_F_REF_FROM_S, wfrom, wto, wbssid,
36750ce650fSreyk 			    action->a_frame.i_from);
36850ce650fSreyk 			hostapd_handle_ref(action->a_flags &
36950ce650fSreyk 			    HOSTAPD_ACTION_F_REF_TO_M,
37050ce650fSreyk 			    HOSTAPD_ACTION_F_REF_TO_S, wfrom, wto, wbssid,
37150ce650fSreyk 			    action->a_frame.i_to);
37250ce650fSreyk 			hostapd_handle_ref(action->a_flags &
37350ce650fSreyk 			    HOSTAPD_ACTION_F_REF_BSSID_M,
37450ce650fSreyk 			    HOSTAPD_ACTION_F_REF_BSSID_S, wfrom, wto, wbssid,
37550ce650fSreyk 			    action->a_frame.i_bssid);
37650ce650fSreyk 		}
37750ce650fSreyk 
37850ce650fSreyk 		/* Send a raw IEEE 802.11 frame */
379d01b6ac2Sreyk 		return (hostapd_apme_output(apme, &action->a_frame));
38050ce650fSreyk 
38150ce650fSreyk 	default:
38250ce650fSreyk 		return (0);
38350ce650fSreyk 	}
38450ce650fSreyk 
38550ce650fSreyk 	return (ret);
38650ce650fSreyk }
3879a571ad9Sreyk 
3889a571ad9Sreyk int
hostapd_handle_radiotap(struct hostapd_radiotap * rtap,u_int8_t * buf,const u_int len)3899a571ad9Sreyk hostapd_handle_radiotap(struct hostapd_radiotap *rtap,
3909a571ad9Sreyk     u_int8_t *buf, const u_int len)
3919a571ad9Sreyk {
3929a571ad9Sreyk 	struct ieee80211_radiotap_header *rh =
3939a571ad9Sreyk 	    (struct ieee80211_radiotap_header*)buf;
3949a571ad9Sreyk 	u_int8_t *t, *ptr = NULL;
3959a571ad9Sreyk 	u_int rh_len;
3969a571ad9Sreyk 	const u_int8_t *snapend = buf + len;
3979a571ad9Sreyk 
3989a571ad9Sreyk 	TCHECK(*rh);
3999a571ad9Sreyk 
4009a571ad9Sreyk 	rh_len = letoh16(rh->it_len);
4019a571ad9Sreyk 	if (rh->it_version != 0)
4029a571ad9Sreyk 		return (EINVAL);
4039a571ad9Sreyk 	if (len <= rh_len)
4049a571ad9Sreyk 		goto trunc;
4059a571ad9Sreyk 
4069a571ad9Sreyk 	bzero(rtap, sizeof(struct hostapd_radiotap));
4079a571ad9Sreyk 
4089a571ad9Sreyk 	t = (u_int8_t*)buf + sizeof(struct ieee80211_radiotap_header);
4099a571ad9Sreyk 	if ((rtap->r_present = letoh32(rh->it_present)) == 0)
4109a571ad9Sreyk 		return (0);
4119a571ad9Sreyk 
4129a571ad9Sreyk #define RADIOTAP(_x, _len)						\
4139a571ad9Sreyk 	if (rtap->r_present & HOSTAPD_RADIOTAP_F(_x)) {			\
4149a571ad9Sreyk 		TCHECK2(*t, _len);					\
4159a571ad9Sreyk 		ptr = t;						\
4169a571ad9Sreyk 		t += _len;						\
4179a571ad9Sreyk 	} else								\
4189a571ad9Sreyk 		ptr = NULL;
4199a571ad9Sreyk 
4209a571ad9Sreyk 	/* radiotap doesn't use TLV header fields, ugh */
4219a571ad9Sreyk 	RADIOTAP(TSFT, 8);
4229a571ad9Sreyk 	RADIOTAP(FLAGS, 1);
4239a571ad9Sreyk 
4249a571ad9Sreyk 	RADIOTAP(RATE, 1);
4259a571ad9Sreyk 	if (ptr != NULL) {
4269a571ad9Sreyk 		rtap->r_txrate = *(u_int8_t *)ptr;
4279a571ad9Sreyk 	}
4289a571ad9Sreyk 
4299a571ad9Sreyk 	RADIOTAP(CHANNEL, 4);
4309a571ad9Sreyk 	if (ptr != NULL) {
4319a571ad9Sreyk 		rtap->r_chan = letoh16(*(u_int16_t*)ptr);
4329a571ad9Sreyk 		rtap->r_chan_flags = letoh16(*(u_int16_t*)ptr + 1);
4339a571ad9Sreyk 	}
4349a571ad9Sreyk 
4359a571ad9Sreyk 	RADIOTAP(FHSS, 2);
4369a571ad9Sreyk 	RADIOTAP(DBM_ANTSIGNAL, 1);
4379a571ad9Sreyk 	RADIOTAP(DBM_ANTNOISE, 1);
4389a571ad9Sreyk 	RADIOTAP(LOCK_QUALITY, 2);
4399a571ad9Sreyk 	RADIOTAP(TX_ATTENUATION, 2);
4409a571ad9Sreyk 	RADIOTAP(DB_TX_ATTENUATION, 2);
4419a571ad9Sreyk 	RADIOTAP(DBM_TX_POWER, 1);
4429a571ad9Sreyk 	RADIOTAP(ANTENNA, 1);
4439a571ad9Sreyk 	RADIOTAP(DB_ANTSIGNAL, 1);
4449a571ad9Sreyk 	RADIOTAP(DB_ANTNOISE, 1);
4459a571ad9Sreyk 	RADIOTAP(FCS, 4);
4469a571ad9Sreyk 
4479a571ad9Sreyk 	RADIOTAP(RSSI, 2);
4489a571ad9Sreyk 	if (ptr != NULL) {
4499a571ad9Sreyk 		rtap->r_rssi = *(u_int8_t *)ptr;
4509a571ad9Sreyk 		rtap->r_max_rssi = *(u_int8_t *)ptr + 1;
4519a571ad9Sreyk 	}
4529a571ad9Sreyk 
4539a571ad9Sreyk 	return (0);
4549a571ad9Sreyk 
4559a571ad9Sreyk  trunc:
4569a571ad9Sreyk 	return (EINVAL);
4579a571ad9Sreyk }
4589a571ad9Sreyk 
4599a571ad9Sreyk int
hostapd_cmp(enum hostapd_op op,int val1,int val2)4609a571ad9Sreyk hostapd_cmp(enum hostapd_op op, int val1, int val2)
4619a571ad9Sreyk {
4629a571ad9Sreyk 	if ((op == HOSTAPD_OP_EQ && val1 == val2) ||
4639a571ad9Sreyk 	    (op == HOSTAPD_OP_NE && val1 != val2) ||
4649a571ad9Sreyk 	    (op == HOSTAPD_OP_LE && val1 <= val2) ||
4659a571ad9Sreyk 	    (op == HOSTAPD_OP_LT && val1 <  val2) ||
4669a571ad9Sreyk 	    (op == HOSTAPD_OP_GE && val1 >= val2) ||
4679a571ad9Sreyk 	    (op == HOSTAPD_OP_GT && val1 >  val2))
4689a571ad9Sreyk 		return (1);
4699a571ad9Sreyk 	return (0);
4709a571ad9Sreyk }
471