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