diff options
| author | mb <mb@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-02-16 18:51:55 +0000 | 
|---|---|---|
| committer | mb <mb@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2008-02-16 18:51:55 +0000 | 
| commit | 67a3aa6b3969d1d1c584598169ffbb5e4ba689cb (patch) | |
| tree | bec57e7b12848bad64b391615efe828f442f24bc /package/hostapd | |
| parent | c4328f2677943565ef7e3d1f2aa56640ce51e6ce (diff) | |
Add hostapd support for nl80211 (mac80211 stack)
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@10469 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'package/hostapd')
| -rw-r--r-- | package/hostapd/Makefile | 5 | ||||
| -rw-r--r-- | package/hostapd/files/default.config | 8 | ||||
| -rw-r--r-- | package/hostapd/files/driver_nl80211.c | 2382 | ||||
| -rw-r--r-- | package/hostapd/files/mini.config | 8 | ||||
| -rw-r--r-- | package/hostapd/files/radiotap.c | 287 | ||||
| -rw-r--r-- | package/hostapd/files/radiotap.h | 242 | ||||
| -rw-r--r-- | package/hostapd/files/radiotap_iter.h | 41 | ||||
| -rw-r--r-- | package/hostapd/patches/007-add-nl80211-driver.patch | 17 | 
8 files changed, 2990 insertions, 0 deletions
| diff --git a/package/hostapd/Makefile b/package/hostapd/Makefile index f196828c9..e38887cf7 100644 --- a/package/hostapd/Makefile +++ b/package/hostapd/Makefile @@ -77,6 +77,11 @@ define Build/ConfigureTarget  		$(PKG_BUILD_DIR)/hostapd \  		$(PKG_BUILD_DIR)/src \  		$(PKG_BUILD_DIR)/hostapd.$(1)/ +	$(CP) ./files/driver_nl80211.c $(PKG_BUILD_DIR)/hostapd.$(1)/hostapd/ +	$(CP) ./files/radiotap.c $(PKG_BUILD_DIR)/hostapd.$(1)/hostapd/ +	$(CP) ./files/radiotap.h $(PKG_BUILD_DIR)/hostapd.$(1)/hostapd/ +	$(CP) ./files/radiotap_iter.h $(PKG_BUILD_DIR)/hostapd.$(1)/hostapd/ +  	$(CP) ./files/$(1).config $(PKG_BUILD_DIR)/hostapd.$(1)/hostapd/.config  	$(if $(CONFIG_PACKAGE_kmod-mac80211),,$(SED) 's,^CONFIG_DRIVER_DEVICESCAPE,#CONFIG_DRIVER_DEVICESCAPE,g' $(PKG_BUILD_DIR)/hostapd.$(1)/hostapd/.config)  	$(if $(CONFIG_PACKAGE_kmod-madwifi),,$(SED) 's,^CONFIG_DRIVER_MADWIFI,#CONFIG_DRIVER_MADWIFI,g' $(PKG_BUILD_DIR)/hostapd.$(1)/hostapd/.config) diff --git a/package/hostapd/files/default.config b/package/hostapd/files/default.config index f383e116c..7e7555f71 100644 --- a/package/hostapd/files/default.config +++ b/package/hostapd/files/default.config @@ -22,6 +22,14 @@ CONFIG_DRIVER_MADWIFI=y  # Driver interface for Prism54 driver  #CONFIG_DRIVER_PRISM54=y +# Driver interface for drivers using the nl80211 kernel interface +CONFIG_DRIVER_NL80211=y +# driver_nl80211.c requires a rather new libnl, probably not +# shipped with your distribution yet +#LIBNL=/usr/src/libnl +#CFLAGS += -I$(LIBNL)/include +#LIBS += -L$(LIBNL)/lib +  # Driver interface for drivers using Devicescape IEEE 802.11 stack  #CONFIG_DRIVER_DEVICESCAPE=y  # Currently, driver_devicescape.c build requires some additional parameters diff --git a/package/hostapd/files/driver_nl80211.c b/package/hostapd/files/driver_nl80211.c new file mode 100644 index 000000000..d673aa504 --- /dev/null +++ b/package/hostapd/files/driver_nl80211.c @@ -0,0 +1,2382 @@ +/* + * hostapd / Kernel driver communication via nl80211 + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include <sys/ioctl.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include <netlink/msg.h> +#include <netlink/attr.h> +#include <linux/nl80211.h> +#include <net/if.h> +#include <linux/if_packet.h> +#include <linux/if_ether.h>   /* The L2 protocols */ +#include <linux/wireless.h> +#include <net/if_arp.h> + +#include "hostapd.h" +#include "driver.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "hw_features.h" +#include "mlme.h" +#include "radiotap.h" +#include "radiotap_iter.h" + +enum ieee80211_msg_type { +	ieee80211_msg_normal = 0, +	ieee80211_msg_tx_callback_ack = 1, +	ieee80211_msg_tx_callback_fail = 2, +}; + +struct i802_driver_data { +	struct hostapd_data *hapd; + +	char iface[IFNAMSIZ + 1]; +	int ioctl_sock; /* socket for ioctl() use */ +	int wext_sock; /* socket for wireless events */ +	int eapol_sock; /* socket for EAPOL frames */ +	int monitor_sock; /* socket for monitor */ +	int monitor_ifidx; + +	int default_if_indices[16]; +	int *if_indices; +	int num_if_indices; + +	int we_version; +	struct nl_handle *nl_handle; +	struct nl_cache *nl_cache; +	struct genl_family *nl80211; +	int dtim_period; +	unsigned int beacon_set:1; +	unsigned int ieee802_1x_active:1; +}; + + +static void add_ifidx(struct i802_driver_data *drv, int ifidx) +{ +	int i; +	int *old; + +	for (i = 0; i < drv->num_if_indices; i++) { +		if (drv->if_indices[i] == 0) { +			drv->if_indices[i] = ifidx; +			return; +		} +	} + +	if (drv->if_indices != drv->default_if_indices) +		old = drv->if_indices; +	else +		old = NULL; + +	drv->if_indices = realloc(old, +				  sizeof(int) * (drv->num_if_indices + 1)); +	if (!drv->if_indices) { +		if (!old) +			drv->if_indices = drv->default_if_indices; +		else +			drv->if_indices = old; +		wpa_printf(MSG_ERROR, "Failed to reallocate memory for " +			   "interfaces"); +		wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx); +		return; +	} +	drv->if_indices[drv->num_if_indices] = ifidx; +	drv->num_if_indices++; +} + + +static void del_ifidx(struct i802_driver_data *drv, int ifidx) +{ +	int i; + +	for (i = 0; i < drv->num_if_indices; i++) { +		if (drv->if_indices[i] == ifidx) { +			drv->if_indices[i] = 0; +			break; +		} +	} +} + + +static int have_ifidx(struct i802_driver_data *drv, int ifidx) +{ +	int i; + +	for (i = 0; i < drv->num_if_indices; i++) +		if (drv->if_indices[i] == ifidx) +			return 1; + +	return 0; +} + + +/* helper for netlink get routines */ +static int ack_wait_handler(struct nl_msg *msg, void *arg) +{ +	int *finished = arg; + +	*finished = 1; +	return NL_STOP; +} + + +static int hostapd_set_iface_flags(struct i802_driver_data *drv, +				   const char *ifname, int dev_up) +{ +	struct ifreq ifr; + +	if (drv->ioctl_sock < 0) +		return -1; + +	memset(&ifr, 0, sizeof(ifr)); +	os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + +	if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { +		perror("ioctl[SIOCGIFFLAGS]"); +		wpa_printf(MSG_DEBUG, "Could not read interface flags (%s)", +			   drv->iface); +		return -1; +	} + +	if (dev_up) +		ifr.ifr_flags |= IFF_UP; +	else +		ifr.ifr_flags &= ~IFF_UP; + +	if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) { +		perror("ioctl[SIOCSIFFLAGS]"); +		return -1; +	} + +	return 0; +} + + +static int i802_set_encryption(const char *iface, void *priv, const char *alg, +			       const u8 *addr, int idx, const u8 *key, +			       size_t key_len, int txkey) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	int ret = -1; +	int err = 0; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	if (strcmp(alg, "none") == 0) { +		genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +			    0, NL80211_CMD_DEL_KEY, 0); +	} else { +		genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +			    0, NL80211_CMD_NEW_KEY, 0); +		NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key); +		if (strcmp(alg, "WEP") == 0) { +			if (key_len == 5) +				NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, +					    0x000FAC01); +			else +				NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, +					    0x000FAC05); +		} else if (strcmp(alg, "TKIP") == 0) +			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); +		else if (strcmp(alg, "CCMP") == 0) +			NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); +		else +			goto out; +	} + +	if (addr) +		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); +	NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    (err = nl_wait_for_ack(drv->nl_handle)) < 0) { +		if (err != -ENOENT) { +			err = 0; +			goto out; +		} +	} + +	/* +	 * If we need to set the default TX key we do that below, +	 * otherwise we're done here. +	 */ +	if (!txkey || addr) { +		ret = 0; +		goto out; +	} + +	nlmsg_free(msg); + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_SET_KEY, 0); +	NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); +	NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    (err = nl_wait_for_ack(drv->nl_handle)) < 0) { +		if (err != -ENOENT) { +			err = 0; +			goto out; +		} +	} + +	ret = 0; + + out: + nla_put_failure: +	nlmsg_free(msg); +	return ret; +} + + +static inline int min_int(int a, int b) +{ +	if (a < b) +		return a; +	return b; +} + + +static int get_key_handler(struct nl_msg *msg, void *arg) +{ +	struct nlattr *tb[NL80211_ATTR_MAX]; +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + +	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), +		  genlmsg_attrlen(gnlh, 0), NULL); + +	/* +	 * TODO: validate the key index and mac address! +	 * Otherwise, there's a race condition as soon as +	 * the kernel starts sending key notifications. +	 */ + +	if (tb[NL80211_ATTR_KEY_SEQ]) +		memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]), +		       min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6)); +	return NL_SKIP; +} + + +static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr, +			   int idx, u8 *seq) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	struct nl_cb *cb = NULL; +	int ret = -1; +	int err = 0; +	int finished = 0; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_GET_KEY, 0); + +	if (addr) +		NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); +	NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + +	cb = nl_cb_alloc(NL_CB_CUSTOM); +	if (!cb) +		goto out; + +	memset(seq, 0, 6); + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0) +		goto out; + +	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_key_handler, seq); +	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, &finished); + +	err = nl_recvmsgs(drv->nl_handle, cb); + +	if (!finished) +		err = nl_wait_for_ack(drv->nl_handle); + +	if (err < 0) +		goto out; + +	ret = 0; + + out: +	nl_cb_put(cb); + nla_put_failure: +	nlmsg_free(msg); +	return ret; +} + + +static int i802_set_rate_sets(void *priv, int *supp_rates, int *basic_rates, +			      int mode) +{ +	return -1; +} + + +static int i802_set_ssid(const char *ifname, void *priv, const u8 *buf, +			 int len) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); +	iwr.u.essid.flags = 1; /* SSID active */ +	iwr.u.essid.pointer = (caddr_t) buf; +	iwr.u.essid.length = len; + +	if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { +		perror("ioctl[SIOCSIWESSID]"); +		printf("len=%d\n", len); +		return -1; +	} + +	return 0; +} + + +static int i802_send_mgmt_frame(void *priv, const void *data, size_t len, +				int flags) +{ +	struct ieee80211_hdr *hdr = (void*) data; +	__u8 rtap_hdr[] = { +		0x00, 0x00, /* radiotap version */ +		0x0e, 0x00, /* radiotap length */ +		0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */ +		0x0c,       /* F_WEP | F_FRAG (encrypt/fragment if required) */ +		0x00,       /* padding */ +		0x00, 0x00, /* RX and TX flags to indicate that */ +		0x00, 0x00, /* this is the injected frame directly */ +	}; +	struct i802_driver_data *drv = priv; +	struct iovec iov[2] = { +		{ +			.iov_base = &rtap_hdr, +			.iov_len = sizeof(rtap_hdr), +		}, +		{ +			.iov_base = (void*)data, +			.iov_len = len, +		} +	}; +	struct msghdr msg = { +		.msg_name = NULL, +		.msg_namelen = 0, +		.msg_iov = iov, +		.msg_iovlen = 2, +		.msg_control = NULL, +		.msg_controllen = 0, +		.msg_flags = 0, +	}; + +	/* +	 * ugh, guess what, the generic code sets one of the version +	 * bits to request tx callback +	 */ +	hdr->frame_control &= ~host_to_le16(BIT(1)); +	return sendmsg(drv->monitor_sock, &msg, flags); +} + + +/* Set kernel driver on given frequency (MHz) */ +static int i802_set_freq(void *priv, int mode, int freq) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); +	iwr.u.freq.m = freq; +	iwr.u.freq.e = 6; + +	if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { +		perror("ioctl[SIOCSIWFREQ]"); +		return -1; +	} + +	return 0; +} + + +static int i802_set_rts(void *priv, int rts) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); +	iwr.u.rts.value = rts; +	iwr.u.rts.fixed = 1; + +	if (ioctl(drv->ioctl_sock, SIOCSIWRTS, &iwr) < 0) { +		perror("ioctl[SIOCSIWRTS]"); +		return -1; +	} + +	return 0; +} + + +static int i802_get_rts(void *priv, int *rts) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + +	if (ioctl(drv->ioctl_sock, SIOCGIWRTS, &iwr) < 0) { +		perror("ioctl[SIOCGIWRTS]"); +		return -1; +	} + +	*rts = iwr.u.rts.value; + +	return 0; +} + + +static int i802_set_frag(void *priv, int frag) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); +	iwr.u.frag.value = frag; +	iwr.u.frag.fixed = 1; + +	if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) { +		perror("ioctl[SIOCSIWFRAG]"); +		return -1; +	} + +	return 0; +} + + +static int i802_get_frag(void *priv, int *frag) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + +	if (ioctl(drv->ioctl_sock, SIOCGIWFRAG, &iwr) < 0) { +		perror("ioctl[SIOCGIWFRAG]"); +		return -1; +	} + +	*frag = iwr.u.frag.value; + +	return 0; +} + + +static int i802_set_retry(void *priv, int short_retry, int long_retry) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + +	iwr.u.retry.value = short_retry; +	iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; +	if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) { +		perror("ioctl[SIOCSIWRETRY(short)]"); +		return -1; +	} + +	iwr.u.retry.value = long_retry; +	iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; +	if (ioctl(drv->ioctl_sock, SIOCSIWFRAG, &iwr) < 0) { +		perror("ioctl[SIOCSIWRETRY(long)]"); +		return -1; +	} + +	return 0; +} + + +static int i802_get_retry(void *priv, int *short_retry, int *long_retry) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, drv->hapd->conf->iface, IFNAMSIZ); + +	iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN; +	if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { +		perror("ioctl[SIOCGIWFRAG(short)]"); +		return -1; +	} +	*short_retry = iwr.u.retry.value; + +	iwr.u.retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX; +	if (ioctl(drv->ioctl_sock, SIOCGIWRETRY, &iwr) < 0) { +		perror("ioctl[SIOCGIWFRAG(long)]"); +		return -1; +	} +	*long_retry = iwr.u.retry.value; + +	return 0; +} + + +static int i802_flush(void *priv) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_NEW_STATION, 0); + +	/* +	 * XXX: FIX! this needs to flush all VLANs too +	 */ +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, +		    if_nametoindex(drv->iface)); + +	ret = 0; + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) { +		ret = -1; +	} + + nla_put_failure: +	nlmsg_free(msg); + + out: +	return ret; +} + + +static int get_sta_handler(struct nl_msg *msg, void *arg) +{ +	struct nlattr *tb[NL80211_ATTR_MAX + 1]; +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); +	struct hostap_sta_driver_data *data = arg; +	struct nlattr *stats[NL80211_STA_STAT_MAX + 1]; +	static struct nla_policy stats_policy[NL80211_STA_STAT_MAX + 1] = { +		[NL80211_STA_STAT_INACTIVE_TIME] = { .type = NLA_U32 }, +		[NL80211_STA_STAT_RX_BYTES] = { .type = NLA_U32 }, +		[NL80211_STA_STAT_TX_BYTES] = { .type = NLA_U32 }, +	}; + +	nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), +		  genlmsg_attrlen(gnlh, 0), NULL); + +	/* +	 * TODO: validate the interface and mac address! +	 * Otherwise, there's a race condition as soon as +	 * the kernel starts sending station notifications. +	 */ + +	if (!tb[NL80211_ATTR_STA_STATS]) { +		wpa_printf(MSG_DEBUG, "sta stats missing!"); +		return NL_SKIP; +	} +	if (nla_parse_nested(stats, NL80211_STA_STAT_MAX, +			     tb[NL80211_ATTR_STA_STATS], +			     stats_policy)) { +		wpa_printf(MSG_DEBUG, "failed to parse nested attributes!"); +		return NL_SKIP; +	} + +	if (stats[NL80211_STA_STAT_INACTIVE_TIME]) +		data->inactive_msec = +			nla_get_u32(stats[NL80211_STA_STAT_INACTIVE_TIME]); +	if (stats[NL80211_STA_STAT_RX_BYTES]) +		data->rx_bytes = nla_get_u32(stats[NL80211_STA_STAT_RX_BYTES]); +	if (stats[NL80211_STA_STAT_TX_BYTES]) +		data->rx_bytes = nla_get_u32(stats[NL80211_STA_STAT_TX_BYTES]); + +	return NL_SKIP; +} + +static int i802_read_sta_data(void *priv, struct hostap_sta_driver_data *data, +			      const u8 *addr) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	struct nl_cb *cb = NULL; +	int ret = -1; +	int err = 0; +	int finished = 0; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_GET_STATION, 0); + +	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + +	cb = nl_cb_alloc(NL_CB_CUSTOM); +	if (!cb) +		goto out; + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0) +		goto out; + +	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, get_sta_handler, data); +	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, &finished); + +	err = nl_recvmsgs(drv->nl_handle, cb); + +	if (!finished) +		err = nl_wait_for_ack(drv->nl_handle); + +	if (err < 0) +		goto out; + +	ret = 0; + + out: +	nl_cb_put(cb); + nla_put_failure: +	nlmsg_free(msg); +	return ret; + +} + + +static int i802_send_eapol(void *priv, const u8 *addr, const u8 *data, +			   size_t data_len, int encrypt, const u8 *own_addr) +{ +	struct i802_driver_data *drv = priv; +	struct ieee80211_hdr *hdr; +	size_t len; +	u8 *pos; +	int res; +#if 0 /* FIX */ +	int qos = sta->flags & WLAN_STA_WME; +#else +	int qos = 0; +#endif + +	len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 + +		data_len; +	hdr = os_zalloc(len); +	if (hdr == NULL) { +		printf("malloc() failed for i802_send_data(len=%lu)\n", +		       (unsigned long) len); +		return -1; +	} + +	hdr->frame_control = +		IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA); +	hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS); +	if (encrypt) +		hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP); +#if 0 /* To be enabled if qos determination is added above */ +	if (qos) { +		hdr->frame_control |= +			host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4); +	} +#endif + +	memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN); +	memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN); +	memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN); +	pos = (u8 *) (hdr + 1); + +#if 0 /* To be enabled if qos determination is added above */ +	if (qos) { +		/* add an empty QoS header if needed */ +		pos[0] = 0; +		pos[1] = 0; +		pos += 2; +	} +#endif + +	memcpy(pos, rfc1042_header, sizeof(rfc1042_header)); +	pos += sizeof(rfc1042_header); +	WPA_PUT_BE16(pos, ETH_P_PAE); +	pos += 2; +	memcpy(pos, data, data_len); + +	res = i802_send_mgmt_frame(drv, (u8 *) hdr, len, 0); +	free(hdr); + +	if (res < 0) { +		perror("i802_send_eapol: send"); +		printf("i802_send_eapol - packet len: %lu - failed\n", +		       (unsigned long) len); +	} + +	return res; +} + + +static int i802_sta_add(const char *ifname, void *priv, const u8 *addr, +			u16 aid, u16 capability, u8 *supp_rates, +			size_t supp_rates_len, int flags) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_NEW_STATION, 0); + +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, +		    if_nametoindex(drv->iface)); +	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); +	NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, aid); +	NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, supp_rates_len, +		supp_rates); +	NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 0); + +	ret = nl_send_auto_complete(drv->nl_handle, msg); +	if (ret < 0) +		goto nla_put_failure; + +	ret = nl_wait_for_ack(drv->nl_handle); +	/* ignore EEXIST, this happens if a STA associates while associated */ +	if (ret == -EEXIST || ret >= 0) +		ret = 0; + + nla_put_failure: +	nlmsg_free(msg); + + out: +	return ret; +} + + +static int i802_sta_remove(void *priv, const u8 *addr) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_DEL_STATION, 0); + +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, +		    if_nametoindex(drv->iface)); +	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + +	ret = 0; + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) { +		ret = -1; +	} + + nla_put_failure: +	nlmsg_free(msg); + + out: +	return ret; +} + + +static int i802_sta_set_flags(void *priv, const u8 *addr, +			      int total_flags, int flags_or, int flags_and) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg, *flags = NULL; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	flags = nlmsg_alloc(); +	if (!flags) +		goto free_msg; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_SET_STATION, 0); + +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, +		    if_nametoindex(drv->iface)); +	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + +	if (total_flags & WLAN_STA_AUTHORIZED || !drv->ieee802_1x_active) +		NLA_PUT_FLAG(flags, NL80211_STA_FLAG_AUTHORIZED); + +	if (total_flags & WLAN_STA_WME) +		NLA_PUT_FLAG(flags, NL80211_STA_FLAG_WME); + +	if (total_flags & WLAN_STA_SHORT_PREAMBLE) +		NLA_PUT_FLAG(flags, NL80211_STA_FLAG_SHORT_PREAMBLE); + +	if (nla_put_nested(msg, NL80211_ATTR_STA_FLAGS, flags)) +		goto nla_put_failure; + +	ret = 0; + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) { +		ret = -1; +	} + + nla_put_failure: +	nlmsg_free(flags); + + free_msg: +	nlmsg_free(msg); + + out: +	return ret; +} + + +static int i802_set_channel_flag(void *priv, int mode, int chan, int flag, +				 unsigned char power_level, +				 unsigned char antenna_max) +{ +	return -1; +} + + +static int i802_set_regulatory_domain(void *priv, unsigned int rd) +{ +	return -1; +} + + +static int i802_set_tx_queue_params(void *priv, int queue, int aifs, +				    int cw_min, int cw_max, int burst_time) +{ +	return -1; +} + + +static void nl80211_remove_iface(struct i802_driver_data *drv, int ifidx) +{ +	struct nl_msg *msg; + +	/* stop listening for EAPOL on this interface */ +	del_ifidx(drv, ifidx); + +	msg = nlmsg_alloc(); +	if (!msg) +		goto nla_put_failure; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_DEL_INTERFACE, 0); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx); +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) +	nla_put_failure: +		printf("Failed to remove interface.\n"); +	nlmsg_free(msg); +} + + +static int nl80211_create_iface(struct i802_driver_data *drv, +				const char *ifname, +				enum nl80211_iftype iftype, +				const u8 *addr) +{ +	struct nl_msg *msg, *flags = NULL; +	int ifidx; +	struct ifreq ifreq; +	struct iwreq iwr; + +	msg = nlmsg_alloc(); +	if (!msg) +		return -1; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_NEW_INTERFACE, 0); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, +		    if_nametoindex(drv->hapd->conf->iface)); +	NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); +	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + +	if (iftype == NL80211_IFTYPE_MONITOR) { +		int err; + +		flags = nlmsg_alloc(); +		if (!flags) +			goto nla_put_failure; + +		NLA_PUT_FLAG(flags, NL80211_MNTR_FLAG_COOK_FRAMES); + +		err = nla_put_nested(msg, NL80211_ATTR_MNTR_FLAGS, flags); + +		nlmsg_free(flags); + +		if (err) +			goto nla_put_failure; +	} + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) { + nla_put_failure: +		printf("Failed to create interface %s.\n", ifname); +		nlmsg_free(msg); +		return -1; +	} + +	nlmsg_free(msg); + +	ifidx = if_nametoindex(ifname); + +	if (ifidx <= 0) +		return -1; + +	/* start listening for EAPOL on this interface */ +	add_ifidx(drv, ifidx); + +	if (addr) { +		switch (iftype) { +		case NL80211_IFTYPE_AP: +			os_strlcpy(ifreq.ifr_name, ifname, IFNAMSIZ); +			memcpy(ifreq.ifr_hwaddr.sa_data, addr, ETH_ALEN); +			ifreq.ifr_hwaddr.sa_family = ARPHRD_ETHER; + +			if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifreq)) { +				nl80211_remove_iface(drv, ifidx); +				return -1; +			} +			break; +		case NL80211_IFTYPE_WDS: +			memset(&iwr, 0, sizeof(iwr)); +			os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); +			iwr.u.addr.sa_family = ARPHRD_ETHER; +			memcpy(iwr.u.addr.sa_data, addr, ETH_ALEN); +			if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr)) +				return -1; +			break; +		default: +			/* nothing */ +			break; +		} +	} + +	return ifidx; +} + + +static int i802_bss_add(void *priv, const char *ifname, const u8 *bssid) +{ +	int ifidx; + +	/* +	 * The kernel supports that when the low-level driver does, +	 * but we currently don't because we need per-BSS data that +	 * currently we can't handle easily. +	 */ +	return -1; + +	ifidx = nl80211_create_iface(priv, ifname, NL80211_IFTYPE_AP, bssid); +	if (ifidx < 0) +		return -1; +	if (hostapd_set_iface_flags(priv, ifname, 1)) { +		nl80211_remove_iface(priv, ifidx); +		return -1; +	} +	return 0; +} + + +static int i802_bss_remove(void *priv, const char *ifname) +{ +	nl80211_remove_iface(priv, if_nametoindex(ifname)); +	return 0; +} + + +static int i802_set_beacon(const char *iface, void *priv, +			   u8 *head, size_t head_len, +			   u8 *tail, size_t tail_len) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	u8 cmd = NL80211_CMD_NEW_BEACON; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	if (drv->beacon_set) +		cmd = NL80211_CMD_SET_BEACON; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, cmd, 0); +	NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, head_len, head); +	NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, tail_len, tail); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); +	NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, 1000); + +	if (!drv->dtim_period) +		drv->dtim_period = 2; +	NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) +		goto out; + +	ret = 0; + +	drv->beacon_set = 1; + + out: + nla_put_failure: +	nlmsg_free(msg); +	return ret; +} + + +static int i802_del_beacon(struct i802_driver_data *drv) +{ +	struct nl_msg *msg; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_DEL_BEACON, 0); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) +		goto out; + +	ret = 0; + + out: + nla_put_failure: +	nlmsg_free(msg); +	return ret; +} + + +static int i802_set_ieee8021x(const char *ifname, void *priv, int enabled) +{ +	struct i802_driver_data *drv = priv; + +	/* +	 * FIXME: This needs to be per interface (BSS) +	 */ +	drv->ieee802_1x_active = enabled; +	return 0; +} + + +static int i802_set_privacy(const char *ifname, void *priv, int enabled) +{ +	struct i802_driver_data *drv = priv; +	struct iwreq iwr; + +	memset(&iwr, 0, sizeof(iwr)); + +	os_strlcpy(iwr.ifr_name, ifname, IFNAMSIZ); +	iwr.u.param.flags = IW_AUTH_PRIVACY_INVOKED; +	iwr.u.param.value = enabled; + +	ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr); + +	/* ignore errors, the kernel/driver might not care */ +	return 0; +} + + +static int i802_set_internal_bridge(void *priv, int value) +{ +	return -1; +} + + +static int i802_set_beacon_int(void *priv, int value) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_SET_BEACON, 0); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + +	NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, value); + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) +		goto out; + +	ret = 0; + + out: + nla_put_failure: +	nlmsg_free(msg); +	return ret; +} + + +static int i802_set_dtim_period(const char *iface, void *priv, int value) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_SET_BEACON, 0); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface)); + +	drv->dtim_period = value; +	NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, drv->dtim_period); + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) +		goto out; + +	ret = 0; + + out: + nla_put_failure: +	nlmsg_free(msg); +	return ret; +} + + +static int i802_set_cts_protect(void *priv, int value) +{ +	return -1; +} + + +static int i802_set_preamble(void *priv, int value) +{ +	return -1; +} + + +static int i802_set_short_slot_time(void *priv, int value) +{ +	return -1; +} + + +static enum nl80211_iftype i802_if_type(enum hostapd_driver_if_type type) +{ +	switch (type) { +	case HOSTAPD_IF_VLAN: +		return NL80211_IFTYPE_AP_VLAN; +	case HOSTAPD_IF_WDS: +		return NL80211_IFTYPE_WDS; +	} +	return -1; +} + + +static int i802_if_add(const char *iface, void *priv, +		       enum hostapd_driver_if_type type, char *ifname, +		       const u8 *addr) +{ +	if (nl80211_create_iface(priv, ifname, i802_if_type(type), addr) < 0) +		return -1; +	return 0; +} + + +static int i802_if_update(void *priv, enum hostapd_driver_if_type type, +			  char *ifname, const u8 *addr) +{ +	/* unused at the moment */ +	return -1; +} + + +static int i802_if_remove(void *priv, enum hostapd_driver_if_type type, +			  const char *ifname, const u8 *addr) +{ +	nl80211_remove_iface(priv, if_nametoindex(ifname)); +	return 0; +} + + +struct phy_info_arg { +	u16 *num_modes; +	struct hostapd_hw_modes *modes; +	int error; +}; + +static int phy_info_handler(struct nl_msg *msg, void *arg) +{ +	struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; +	struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); +	struct phy_info_arg *phy_info = arg; + +	struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1]; + +	struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1]; +	static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = { +		[NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 }, +		[NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG }, +		[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG }, +		[NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG }, +		[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG }, +	}; + +	struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1]; +	static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = { +		[NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 }, +		[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] = { .type = NLA_FLAG }, +	}; + +	struct nlattr *nl_band; +	struct nlattr *nl_freq; +	struct nlattr *nl_rate; +	int rem_band, rem_freq, rem_rate; +	struct hostapd_hw_modes *mode; +	int idx, mode_is_set; + +	nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), +		  genlmsg_attrlen(gnlh, 0), NULL); + +	if (!tb_msg[NL80211_ATTR_WIPHY_BANDS]) +		return NL_SKIP; + +	nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band) { +		mode = realloc(phy_info->modes, (*phy_info->num_modes + 1) * sizeof(*mode)); +		if (!mode) +			return NL_SKIP; +		phy_info->modes = mode; + +		mode_is_set = 0; + +		mode = &phy_info->modes[*(phy_info->num_modes)]; +		memset(mode, 0, sizeof(*mode)); +		*(phy_info->num_modes) += 1; + +		nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band), +			  nla_len(nl_band), NULL); + +		nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { +			nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), +				  nla_len(nl_freq), freq_policy); +			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) +				continue; +			mode->num_channels++; +		} + +		mode->channels = calloc(mode->num_channels, sizeof(struct hostapd_channel_data)); +		if (!mode->channels) +			return NL_SKIP; + +		idx = 0; + +		nla_for_each_nested(nl_freq, tb_band[NL80211_BAND_ATTR_FREQS], rem_freq) { +			nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX, nla_data(nl_freq), +				  nla_len(nl_freq), freq_policy); +			if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ]) +				continue; + +			mode->channels[idx].freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]); +			mode->channels[idx].flag |= HOSTAPD_CHAN_W_SCAN | +						    HOSTAPD_CHAN_W_ACTIVE_SCAN | +						    HOSTAPD_CHAN_W_IBSS; + +			if (!mode_is_set) { +				/* crude heuristic */ +				if (mode->channels[idx].freq < 4000) +					mode->mode = HOSTAPD_MODE_IEEE80211B; +				else +					mode->mode = HOSTAPD_MODE_IEEE80211A; +				mode_is_set = 1; +			} + +			/* crude heuristic */ +			if (mode->channels[idx].freq < 4000) +				if (mode->channels[idx].freq == 2848) +					mode->channels[idx].chan = 14; +				else +					mode->channels[idx].chan = (mode->channels[idx].freq - 2407) / 5; +			else +				mode->channels[idx].chan = mode->channels[idx].freq/5 - 1000; + +			if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED]) +				mode->channels[idx].flag &= ~HOSTAPD_CHAN_W_SCAN; +			if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) +				mode->channels[idx].flag &= ~HOSTAPD_CHAN_W_ACTIVE_SCAN; +			if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) +				mode->channels[idx].flag &= ~HOSTAPD_CHAN_W_IBSS; +			idx++; +		} + +		nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { +			nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), +				  nla_len(nl_rate), rate_policy); +			if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) +				continue; +			mode->num_rates++; +		} + +		mode->rates = calloc(mode->num_rates, sizeof(struct hostapd_rate_data)); +		if (!mode->rates) +			return NL_SKIP; + +		idx = 0; + +		nla_for_each_nested(nl_rate, tb_band[NL80211_BAND_ATTR_RATES], rem_rate) { +			nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX, nla_data(nl_rate), +				  nla_len(nl_rate), rate_policy); +			if (!tb_rate[NL80211_BITRATE_ATTR_RATE]) +				continue; +			mode->rates[idx].rate = nla_get_u32(tb_rate[NL80211_BITRATE_ATTR_RATE]); + +			/* crude heuristic */ +			if (mode->mode == HOSTAPD_MODE_IEEE80211B && +			    mode->rates[idx].rate > 200) +				mode->mode = HOSTAPD_MODE_IEEE80211G; + +			if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) +				mode->rates[idx].flags |= HOSTAPD_RATE_PREAMBLE2; + +			idx++; +		} +	} + +	phy_info->error = 0; + +	return NL_SKIP; +} + +static struct hostapd_hw_modes *i802_get_hw_feature_data(void *priv, +							 u16 *num_modes, +							 u16 *flags) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	int err = -1; +	struct nl_cb *cb = NULL; +	int finished; +	struct phy_info_arg result = { +		.num_modes = num_modes, +		.modes = NULL, +		.error = 1, +	}; + +	*num_modes = 0; +	*flags = 0; + +	msg = nlmsg_alloc(); +	if (!msg) +		return NULL; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_GET_WIPHY, 0); + +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(drv->iface)); + +	cb = nl_cb_alloc(NL_CB_CUSTOM); +	if (!cb) +		goto out; + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0) +		goto out; + +	nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, phy_info_handler, &result); +	nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_wait_handler, &finished); + +	err = nl_recvmsgs(drv->nl_handle, cb); + +	if (!finished) +		err = nl_wait_for_ack(drv->nl_handle); + +	if (err < 0 || result.error) { +		hostapd_free_hw_features(result.modes, *num_modes); +		result.modes = NULL; +	} + + out: +	nl_cb_put(cb); + nla_put_failure: +	if (err) +		fprintf(stderr, "failed to get information: %d\n", err); +	nlmsg_free(msg); +	return result.modes; +} + + +static int i802_set_sta_vlan(void *priv, const u8 *addr, +			     const char *ifname, int vlan_id) +{ +	struct i802_driver_data *drv = priv; +	struct nl_msg *msg; +	int ret = -1; + +	msg = nlmsg_alloc(); +	if (!msg) +		goto out; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_SET_STATION, 0); + +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, +		    if_nametoindex(drv->iface)); +	NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, +		    if_nametoindex(ifname)); + +	ret = 0; + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    (errno = nl_wait_for_ack(drv->nl_handle) < 0)) { +		ret = -1; +	} + + nla_put_failure: +	nlmsg_free(msg); + + out: +	return ret; +} + + +static void handle_unknown_sta(struct hostapd_data *hapd, u8 *ta) +{ +	struct sta_info *sta; + +	sta = ap_get_sta(hapd, ta); +	if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { +		printf("Data/PS-poll frame from not associated STA " +		       MACSTR "\n", MAC2STR(ta)); +		if (sta && (sta->flags & WLAN_STA_AUTH)) +			hostapd_sta_disassoc( +				hapd, ta, +				WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); +		else +			hostapd_sta_deauth( +				hapd, ta, +				WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); +	} +} + + +static void handle_tx_callback(struct hostapd_data *hapd, u8 *buf, size_t len, +			       int ok) +{ +	struct ieee80211_hdr *hdr; +	u16 fc, type, stype; +	struct sta_info *sta; + +	hdr = (struct ieee80211_hdr *) buf; +	fc = le_to_host16(hdr->frame_control); + +	type = WLAN_FC_GET_TYPE(fc); +	stype = WLAN_FC_GET_STYPE(fc); + +	switch (type) { +	case WLAN_FC_TYPE_MGMT: +		wpa_printf(MSG_DEBUG, "MGMT (TX callback) %s", +			   ok ? "ACK" : "fail"); +		ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); +		break; +	case WLAN_FC_TYPE_CTRL: +		wpa_printf(MSG_DEBUG, "CTRL (TX callback) %s", +			   ok ? "ACK" : "fail"); +		break; +	case WLAN_FC_TYPE_DATA: +		wpa_printf(MSG_DEBUG, "DATA (TX callback) %s", +			   ok ? "ACK" : "fail"); +		sta = ap_get_sta(hapd, hdr->addr1); +		if (sta && sta->flags & WLAN_STA_PENDING_POLL) { +			wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " +				   "activity poll", MAC2STR(sta->addr), +				   ok ? "ACKed" : "did not ACK"); +			if (ok) +				sta->flags &= ~WLAN_STA_PENDING_POLL; +		} +		if (sta) +			ieee802_1x_tx_status(hapd, sta, buf, len, ok); +		break; +	default: +		printf("unknown TX callback frame type %d\n", type); +		break; +	} +} + + +static void handle_frame(struct hostapd_iface *iface, u8 *buf, size_t len, +			 struct hostapd_frame_info *hfi, +			 enum ieee80211_msg_type msg_type) +{ +	struct ieee80211_hdr *hdr; +	u16 fc, type, stype; +	size_t data_len = len; +	struct hostapd_data *hapd = NULL; +	int broadcast_bssid = 0; +	size_t i; +	u8 *bssid; + +	/* +	 * PS-Poll frames are 16 bytes. All other frames are +	 * 24 bytes or longer. +	 */ +	if (len < 16) +		return; + +	hdr = (struct ieee80211_hdr *) buf; +	fc = le_to_host16(hdr->frame_control); + +	type = WLAN_FC_GET_TYPE(fc); +	stype = WLAN_FC_GET_STYPE(fc); + +	switch (type) { +	case WLAN_FC_TYPE_DATA: +		if (len < 24) +			return; +		switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { +		case WLAN_FC_TODS: +			bssid = hdr->addr1; +			break; +		default: +			/* discard */ +			return; +		} +		break; +	case WLAN_FC_TYPE_CTRL: +		/* discard non-ps-poll frames */ +		if (stype != WLAN_FC_STYPE_PSPOLL) +			return; +		bssid = hdr->addr1; +		break; +	case WLAN_FC_TYPE_MGMT: +		bssid = hdr->addr3; +		break; +	default: +		/* discard */ +		return; +	} + +	/* find interface frame belongs to */ +	for (i = 0; i < iface->num_bss; i++) { +		if (memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0) { +			hapd = iface->bss[i]; +			break; +		} +	} + +	if (hapd == NULL) { +		hapd = iface->bss[0]; + +		if (bssid[0] != 0xff || bssid[1] != 0xff || +		    bssid[2] != 0xff || bssid[3] != 0xff || +		    bssid[4] != 0xff || bssid[5] != 0xff) { +			/* +			 * Unknown BSSID - drop frame if this is not from +			 * passive scanning or a beacon (at least ProbeReq +			 * frames to other APs may be allowed through RX +			 * filtering in the wlan hw/driver) +			 */ +			if ((type != WLAN_FC_TYPE_MGMT || +			     stype != WLAN_FC_STYPE_BEACON)) +				return; +		} else +			broadcast_bssid = 1; +	} + +	switch (msg_type) { +	case ieee80211_msg_normal: +		/* continue processing */ +		break; +	case ieee80211_msg_tx_callback_ack: +		handle_tx_callback(hapd, buf, data_len, 1); +		return; +	case ieee80211_msg_tx_callback_fail: +		handle_tx_callback(hapd, buf, data_len, 0); +		return; +	} + +	switch (type) { +	case WLAN_FC_TYPE_MGMT: +		if (stype != WLAN_FC_STYPE_BEACON && +		    stype != WLAN_FC_STYPE_PROBE_REQ) +			wpa_printf(MSG_MSGDUMP, "MGMT"); +		if (broadcast_bssid) { +			for (i = 0; i < iface->num_bss; i++) +				ieee802_11_mgmt(iface->bss[i], buf, data_len, +						stype, hfi); +		} else +			ieee802_11_mgmt(hapd, buf, data_len, stype, hfi); +		break; +	case WLAN_FC_TYPE_CTRL: +		/* can only get here with PS-Poll frames */ +		wpa_printf(MSG_DEBUG, "CTRL"); +		handle_unknown_sta(hapd, hdr->addr2); +		break; +	case WLAN_FC_TYPE_DATA: +		wpa_printf(MSG_DEBUG, "DATA"); +		handle_unknown_sta(hapd, hdr->addr2); +		break; +	} +} + + +static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx) +{ +	struct i802_driver_data *drv = eloop_ctx; +	struct hostapd_data *hapd = drv->hapd; +	struct sockaddr_ll lladdr; +	unsigned char buf[3000]; +	int len; +	socklen_t fromlen = sizeof(lladdr); + +	len = recvfrom(sock, buf, sizeof(buf), 0, +		       (struct sockaddr *)&lladdr, &fromlen); +	if (len < 0) { +		perror("recv"); +		return; +	} + +	if (have_ifidx(drv, lladdr.sll_ifindex)) +		ieee802_1x_receive(hapd, lladdr.sll_addr, buf, len); +} + + +static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +{ +	struct i802_driver_data *drv = eloop_ctx; +	int len; +	unsigned char buf[3000]; +	struct hostapd_data *hapd = drv->hapd; +	struct ieee80211_radiotap_iterator iter; +	int ret; +	struct hostapd_frame_info hfi; +	int injected = 0, failed = 0, msg_type, rxflags = 0; + +	len = recv(sock, buf, sizeof(buf), 0); +	if (len < 0) { +		perror("recv"); +		return; +	} + +	if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) { +		printf("received invalid radiotap frame\n"); +		return; +	} + +	memset(&hfi, 0, sizeof(hfi)); + +	while (1) { +		ret = ieee80211_radiotap_iterator_next(&iter); +		if (ret == -ENOENT) +			break; +		if (ret) { +			printf("received invalid radiotap frame (%d)\n", ret); +			return; +		} +		switch (iter.this_arg_index) { +		case IEEE80211_RADIOTAP_FLAGS: +			if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS) +				len -= 4; +			break; +		case IEEE80211_RADIOTAP_RX_FLAGS: +			rxflags = 1; +			break; +		case IEEE80211_RADIOTAP_TX_FLAGS: +			injected = 1; +			failed = le_to_host16((*(uint16_t *) iter.this_arg)) & +					IEEE80211_RADIOTAP_F_TX_FAIL; +			break; +		case IEEE80211_RADIOTAP_DATA_RETRIES: +			break; +		case IEEE80211_RADIOTAP_CHANNEL: +			/* TODO convert from freq/flags to channel number +			hfi.channel = XXX; +			hfi.phytype = XXX; +			 */ +			break; +		case IEEE80211_RADIOTAP_RATE: +			hfi.datarate = *iter.this_arg * 5; +			break; +		case IEEE80211_RADIOTAP_DB_ANTSIGNAL: +			hfi.ssi_signal = *iter.this_arg; +			break; +		} +	} + +	if (rxflags && injected) +		return; + +	if (!injected) +		msg_type = ieee80211_msg_normal; +	else if (failed) +		msg_type = ieee80211_msg_tx_callback_fail; +	else +		msg_type = ieee80211_msg_tx_callback_ack; + +	handle_frame(hapd->iface, buf + iter.max_length, +		     len - iter.max_length, &hfi, msg_type); +} + + +static int nl80211_create_monitor_interface(struct i802_driver_data *drv) +{ +	char buf[IFNAMSIZ]; +	struct sockaddr_ll ll; +	int optval; +	socklen_t optlen; + +	snprintf(buf, IFNAMSIZ, "mon.%s", drv->iface); +	buf[IFNAMSIZ - 1] = '\0'; + +	drv->monitor_ifidx = +		nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL); + +	if (drv->monitor_ifidx < 0) +		return -1; + +	if (hostapd_set_iface_flags(drv, buf, 1)) +		goto error; + +	memset(&ll, 0, sizeof(ll)); +	ll.sll_family = AF_PACKET; +	ll.sll_ifindex = drv->monitor_ifidx; +	drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); +	if (drv->monitor_sock < 0) { +		perror("socket[PF_PACKET,SOCK_RAW]"); +		goto error; +	} + +	if (bind(drv->monitor_sock, (struct sockaddr *) &ll, +		 sizeof(ll)) < 0) { +		perror("monitor socket bind"); +		goto error; +	} + +	optlen = sizeof(optval); +	optval = 20; +	if (setsockopt +	    (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) { +		perror("Failed to set socket priority"); +		goto error; +	} + +	if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read, +				     drv, NULL)) { +		printf("Could not register monitor read socket\n"); +		goto error; +	} + +	return 0; + error: +	nl80211_remove_iface(drv, drv->monitor_ifidx); +	return -1; +} + + +static int nl80211_set_master_mode(struct i802_driver_data *drv, +				   const char *ifname) +{ +	struct nl_msg *msg; + +	msg = nlmsg_alloc(); +	if (!msg) +		return -1; + +	genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, +		    0, NL80211_CMD_SET_INTERFACE, 0); +	NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, +		    if_nametoindex(ifname)); +	NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, NL80211_IFTYPE_AP); + +	if (nl_send_auto_complete(drv->nl_handle, msg) < 0 || +	    nl_wait_for_ack(drv->nl_handle) < 0) { + nla_put_failure: +		wpa_printf(MSG_ERROR, "Failed to set interface %s to master " +			   "mode.", ifname); +		nlmsg_free(msg); +		return -1; +	} + +	nlmsg_free(msg); + +	return 0; +} +   + +static int i802_init_sockets(struct i802_driver_data *drv, const u8 *bssid) +{ +	struct ifreq ifr; +	struct sockaddr_ll addr; + +	drv->ioctl_sock = -1; + +	drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); +	if (drv->ioctl_sock < 0) { +		perror("socket[PF_INET,SOCK_DGRAM]"); +		return -1; +	} + +	/* start listening for EAPOL on the default AP interface */ +	add_ifidx(drv, if_nametoindex(drv->iface)); + +	if (hostapd_set_iface_flags(drv, drv->iface, 0)) +		return -1; + +	if (bssid) { +		os_strlcpy(ifr.ifr_name, drv->iface, IFNAMSIZ); +		memcpy(ifr.ifr_hwaddr.sa_data, bssid, ETH_ALEN); +		ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + +		if (ioctl(drv->ioctl_sock, SIOCSIFHWADDR, &ifr)) { +			perror("ioctl(SIOCSIFHWADDR)"); +			return -1; +		} +	} + +	/* +	 * initialise generic netlink and nl80211 +	 */ +	drv->nl_handle = nl_handle_alloc(); +	if (!drv->nl_handle) { +		printf("Failed to allocate netlink handle.\n"); +		return -1; +	} + +	if (genl_connect(drv->nl_handle)) { +		printf("Failed to connect to generic netlink.\n"); +		return -1; +	} + +	drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); +	if (!drv->nl_cache) { +		printf("Failed to allocate generic netlink cache.\n"); +		return -1; +	} + +	drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); +	if (!drv->nl80211) { +		printf("nl80211 not found.\n"); +		return -1; +	} + +	/* Initialise a monitor interface */ +	if (nl80211_create_monitor_interface(drv)) +		return -1; + +	if (nl80211_set_master_mode(drv, drv->iface)) +		return -1; + +	if (hostapd_set_iface_flags(drv, drv->iface, 1)) +		return -1; + +	memset(&addr, 0, sizeof(addr)); +	addr.sll_family = AF_PACKET; +	addr.sll_ifindex = ifr.ifr_ifindex; +	wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", +		   addr.sll_ifindex); + +	drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE)); +	if (drv->eapol_sock < 0) { +		perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)"); +		return -1; +	} + +	if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL)) +	{ +		printf("Could not register read socket for eapol\n"); +		return -1; +	} + +        memset(&ifr, 0, sizeof(ifr)); +        os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); +        if (ioctl(drv->ioctl_sock, SIOCGIFHWADDR, &ifr) != 0) { +		perror("ioctl(SIOCGIFHWADDR)"); +		return -1; +        } + +	if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { +		printf("Invalid HW-addr family 0x%04x\n", +		       ifr.ifr_hwaddr.sa_family); +		return -1; +	} +	memcpy(drv->hapd->own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + +	return 0; +} + + +static int i802_get_inact_sec(void *priv, const u8 *addr) +{ +	struct hostap_sta_driver_data data; +	int ret; + +	data.inactive_msec = (unsigned long) -1; +	ret = i802_read_sta_data(priv, &data, addr); +	if (ret || data.inactive_msec == (unsigned long) -1) +		return -1; +	return data.inactive_msec / 1000; +} + + +static int i802_sta_clear_stats(void *priv, const u8 *addr) +{ +#if 0 +	/* TODO */ +#endif +	return 0; +} + + +static void +hostapd_wireless_event_wireless_custom(struct i802_driver_data *drv, +				       char *custom) +{ +	wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + +	if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { +		char *pos; +		u8 addr[ETH_ALEN]; +		pos = strstr(custom, "addr="); +		if (pos == NULL) { +			wpa_printf(MSG_DEBUG, +				   "MLME-MICHAELMICFAILURE.indication " +				   "without sender address ignored"); +			return; +		} +		pos += 5; +		if (hwaddr_aton(pos, addr) == 0) { +			ieee80211_michael_mic_failure(drv->hapd, addr, 1); +		} else { +			wpa_printf(MSG_DEBUG, +				   "MLME-MICHAELMICFAILURE.indication " +				   "with invalid MAC address"); +		} +	} +} + + +static void hostapd_wireless_event_wireless(struct i802_driver_data *drv, +					    char *data, int len) +{ +	struct iw_event iwe_buf, *iwe = &iwe_buf; +	char *pos, *end, *custom, *buf; + +	pos = data; +	end = data + len; + +	while (pos + IW_EV_LCP_LEN <= end) { +		/* Event data may be unaligned, so make a local, aligned copy +		 * before processing. */ +		memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); +		wpa_printf(MSG_DEBUG, "Wireless event: cmd=0x%x len=%d", +			   iwe->cmd, iwe->len); +		if (iwe->len <= IW_EV_LCP_LEN) +			return; + +		custom = pos + IW_EV_POINT_LEN; +		if (drv->we_version > 18 && +		    (iwe->cmd == IWEVMICHAELMICFAILURE || +		     iwe->cmd == IWEVCUSTOM)) { +			/* WE-19 removed the pointer from struct iw_point */ +			char *dpos = (char *) &iwe_buf.u.data.length; +			int dlen = dpos - (char *) &iwe_buf; +			memcpy(dpos, pos + IW_EV_LCP_LEN, +			       sizeof(struct iw_event) - dlen); +		} else { +			memcpy(&iwe_buf, pos, sizeof(struct iw_event)); +			custom += IW_EV_POINT_OFF; +		} + +		switch (iwe->cmd) { +		case IWEVCUSTOM: +			if (custom + iwe->u.data.length > end) +				return; +			buf = malloc(iwe->u.data.length + 1); +			if (buf == NULL) +				return; +			memcpy(buf, custom, iwe->u.data.length); +			buf[iwe->u.data.length] = '\0'; +			hostapd_wireless_event_wireless_custom(drv, buf); +			free(buf); +			break; +		} + +		pos += iwe->len; +	} +} + + +static void hostapd_wireless_event_rtm_newlink(struct i802_driver_data *drv, +					       struct nlmsghdr *h, int len) +{ +	struct ifinfomsg *ifi; +	int attrlen, nlmsg_len, rta_len; +	struct rtattr *attr; + +	if (len < (int) sizeof(*ifi)) +		return; + +	ifi = NLMSG_DATA(h); + +	/* TODO: use ifi->ifi_index to filter out wireless events from other +	 * interfaces */ + +	nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + +	attrlen = h->nlmsg_len - nlmsg_len; +	if (attrlen < 0) +		return; + +	attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + +	rta_len = RTA_ALIGN(sizeof(struct rtattr)); +	while (RTA_OK(attr, attrlen)) { +		if (attr->rta_type == IFLA_WIRELESS) { +			hostapd_wireless_event_wireless( +				drv, ((char *) attr) + rta_len, +				attr->rta_len - rta_len); +		} +		attr = RTA_NEXT(attr, attrlen); +	} +} + + +static void hostapd_wireless_event_receive(int sock, void *eloop_ctx, +					   void *sock_ctx) +{ +	char buf[256]; +	int left; +	struct sockaddr_nl from; +	socklen_t fromlen; +	struct nlmsghdr *h; +	struct i802_driver_data *drv = eloop_ctx; + +	fromlen = sizeof(from); +	left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, +			(struct sockaddr *) &from, &fromlen); +	if (left < 0) { +		if (errno != EINTR && errno != EAGAIN) +			perror("recvfrom(netlink)"); +		return; +	} + +	h = (struct nlmsghdr *) buf; +	while (left >= (int) sizeof(*h)) { +		int len, plen; + +		len = h->nlmsg_len; +		plen = len - sizeof(*h); +		if (len > left || plen < 0) { +			printf("Malformed netlink message: " +			       "len=%d left=%d plen=%d\n", +			       len, left, plen); +			break; +		} + +		switch (h->nlmsg_type) { +		case RTM_NEWLINK: +			hostapd_wireless_event_rtm_newlink(drv, h, plen); +			break; +		} + +		len = NLMSG_ALIGN(len); +		left -= len; +		h = (struct nlmsghdr *) ((char *) h + len); +	} + +	if (left > 0) { +		printf("%d extra bytes in the end of netlink message\n", left); +	} +} + + +static int hostap_get_we_version(struct i802_driver_data *drv) +{ +	struct iw_range *range; +	struct iwreq iwr; +	int minlen; +	size_t buflen; + +	drv->we_version = 0; + +	/* +	 * Use larger buffer than struct iw_range in order to allow the +	 * structure to grow in the future. +	 */ +	buflen = sizeof(struct iw_range) + 500; +	range = os_zalloc(buflen); +	if (range == NULL) +		return -1; + +	memset(&iwr, 0, sizeof(iwr)); +	os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); +	iwr.u.data.pointer = (caddr_t) range; +	iwr.u.data.length = buflen; + +	minlen = ((char *) &range->enc_capa) - (char *) range + +		sizeof(range->enc_capa); + +	if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { +		perror("ioctl[SIOCGIWRANGE]"); +		free(range); +		return -1; +	} else if (iwr.u.data.length >= minlen && +		   range->we_version_compiled >= 18) { +		wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " +			   "WE(source)=%d enc_capa=0x%x", +			   range->we_version_compiled, +			   range->we_version_source, +			   range->enc_capa); +		drv->we_version = range->we_version_compiled; +	} + +	free(range); +	return 0; +} + + +static int i802_wireless_event_init(void *priv) +{ +	struct i802_driver_data *drv = priv; +	int s; +	struct sockaddr_nl local; + +	hostap_get_we_version(drv); + +	drv->wext_sock = -1; + +	s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); +	if (s < 0) { +		perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); +		return -1; +	} + +	memset(&local, 0, sizeof(local)); +	local.nl_family = AF_NETLINK; +	local.nl_groups = RTMGRP_LINK; +	if (bind(s, (struct sockaddr *) &local, sizeof(local)) < 0) { +		perror("bind(netlink)"); +		close(s); +		return -1; +	} + +	eloop_register_read_sock(s, hostapd_wireless_event_receive, drv, +				 NULL); +	drv->wext_sock = s; + +	return 0; +} + + +static void i802_wireless_event_deinit(void *priv) +{ +	struct i802_driver_data *drv = priv; +	if (drv->wext_sock < 0) +		return; +	eloop_unregister_read_sock(drv->wext_sock); +	close(drv->wext_sock); +} + + +static int i802_sta_deauth(void *priv, const u8 *addr, int reason) +{ +	struct i802_driver_data *drv = priv; +	struct ieee80211_mgmt mgmt; + +	memset(&mgmt, 0, sizeof(mgmt)); +	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, +					  WLAN_FC_STYPE_DEAUTH); +	memcpy(mgmt.da, addr, ETH_ALEN); +	memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); +	memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); +	mgmt.u.deauth.reason_code = host_to_le16(reason); +	return i802_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + +				      sizeof(mgmt.u.deauth), 0); +} + + +static int i802_sta_disassoc(void *priv, const u8 *addr, int reason) +{ +	struct i802_driver_data *drv = priv; +	struct ieee80211_mgmt mgmt; + +	memset(&mgmt, 0, sizeof(mgmt)); +	mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, +					  WLAN_FC_STYPE_DISASSOC); +	memcpy(mgmt.da, addr, ETH_ALEN); +	memcpy(mgmt.sa, drv->hapd->own_addr, ETH_ALEN); +	memcpy(mgmt.bssid, drv->hapd->own_addr, ETH_ALEN); +	mgmt.u.disassoc.reason_code = host_to_le16(reason); +	return  i802_send_mgmt_frame(drv, &mgmt, IEEE80211_HDRLEN + +				       sizeof(mgmt.u.disassoc), 0); +} + + +static void *i802_init_bssid(struct hostapd_data *hapd, const u8 *bssid) +{ +	struct i802_driver_data *drv; + +	drv = os_zalloc(sizeof(struct i802_driver_data)); +	if (drv == NULL) { +		printf("Could not allocate memory for i802 driver data\n"); +		return NULL; +	} + +	drv->hapd = hapd; +	memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); + +	drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int); +	drv->if_indices = drv->default_if_indices; + +	if (i802_init_sockets(drv, bssid)) +		goto failed; + +	return drv; + +failed: +	free(drv); +	return NULL; +} + + +static void *i802_init(struct hostapd_data *hapd) +{ +	return i802_init_bssid(hapd, NULL); +} + + +static void i802_deinit(void *priv) +{ +	struct i802_driver_data *drv = priv; + +	i802_del_beacon(drv); + +	/* remove monitor interface */ +	nl80211_remove_iface(drv, drv->monitor_ifidx); + +	(void) hostapd_set_iface_flags(drv, drv->iface, 0); + +	if (drv->monitor_sock >= 0) { +		eloop_unregister_read_sock(drv->monitor_sock); +		close(drv->monitor_sock); +	} +	if (drv->ioctl_sock >= 0) +		close(drv->ioctl_sock); +	if (drv->eapol_sock >= 0) { +		eloop_unregister_read_sock(drv->eapol_sock); +		close(drv->eapol_sock); +	} + +	genl_family_put(drv->nl80211); +	nl_cache_free(drv->nl_cache); +	nl_handle_destroy(drv->nl_handle); + +	if (drv->if_indices != drv->default_if_indices) +		free(drv->if_indices); + +	free(drv); +} + + +const struct wpa_driver_ops wpa_driver_nl80211_ops = { +	.name = "nl80211", +	.init = i802_init, +//	.init_bssid = i802_init_bssid, +	.deinit = i802_deinit, +	.wireless_event_init = i802_wireless_event_init, +	.wireless_event_deinit = i802_wireless_event_deinit, +	.set_ieee8021x = i802_set_ieee8021x, +	.set_privacy = i802_set_privacy, +	.set_encryption = i802_set_encryption, +	.get_seqnum = i802_get_seqnum, +	.flush = i802_flush, +	.read_sta_data = i802_read_sta_data, +	.send_eapol = i802_send_eapol, +	.sta_set_flags = i802_sta_set_flags, +	.sta_deauth = i802_sta_deauth, +	.sta_disassoc = i802_sta_disassoc, +	.sta_remove = i802_sta_remove, +	.set_ssid = i802_set_ssid, +	.send_mgmt_frame = i802_send_mgmt_frame, +	.sta_add = i802_sta_add, +	.get_inact_sec = i802_get_inact_sec, +	.sta_clear_stats = i802_sta_clear_stats, +	.set_freq = i802_set_freq, +	.set_rts = i802_set_rts, +	.get_rts = i802_get_rts, +	.set_frag = i802_set_frag, +	.get_frag = i802_get_frag, +	.set_retry = i802_set_retry, +	.get_retry = i802_get_retry, +	.set_rate_sets = i802_set_rate_sets, +	.set_channel_flag = i802_set_channel_flag, +	.set_regulatory_domain = i802_set_regulatory_domain, +	.set_beacon = i802_set_beacon, +	.set_internal_bridge = i802_set_internal_bridge, +	.set_beacon_int = i802_set_beacon_int, +	.set_dtim_period = i802_set_dtim_period, +	.set_cts_protect = i802_set_cts_protect, +	.set_preamble = i802_set_preamble, +	.set_short_slot_time = i802_set_short_slot_time, +	.set_tx_queue_params = i802_set_tx_queue_params, +	.bss_add = i802_bss_add, +	.bss_remove = i802_bss_remove, +	.if_add = i802_if_add, +	.if_update = i802_if_update, +	.if_remove = i802_if_remove, +	.get_hw_feature_data = i802_get_hw_feature_data, +	.set_sta_vlan = i802_set_sta_vlan, +}; diff --git a/package/hostapd/files/mini.config b/package/hostapd/files/mini.config index d1ab057cc..0d13b1ed4 100644 --- a/package/hostapd/files/mini.config +++ b/package/hostapd/files/mini.config @@ -22,6 +22,14 @@ CONFIG_DRIVER_MADWIFI=y  # Driver interface for Prism54 driver  #CONFIG_DRIVER_PRISM54=y +# Driver interface for drivers using the nl80211 kernel interface +CONFIG_DRIVER_NL80211=y +# driver_nl80211.c requires a rather new libnl, probably not +# shipped with your distribution yet +#LIBNL=/usr/src/libnl +#CFLAGS += -I$(LIBNL)/include +#LIBS += -L$(LIBNL)/lib +  # Driver interface for drivers using Devicescape IEEE 802.11 stack  #CONFIG_DRIVER_DEVICESCAPE=y  # Currently, driver_devicescape.c build requires some additional parameters diff --git a/package/hostapd/files/radiotap.c b/package/hostapd/files/radiotap.c new file mode 100644 index 000000000..804473fa4 --- /dev/null +++ b/package/hostapd/files/radiotap.c @@ -0,0 +1,287 @@ +/* + * Radiotap parser + * + * Copyright 2007		Andy Green <andy@warmcat.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * + * Modified for userspace by Johannes Berg <johannes@sipsolutions.net> + * I only modified some things on top to ease syncing should bugs be found. + */ + +#include "includes.h" + +#include "common.h" +#include "radiotap_iter.h" + +#define le16_to_cpu		le_to_host16 +#define le32_to_cpu		le_to_host32 +#define __le32			uint32_t +#define ulong			unsigned long +#define unlikely(cond)		(cond) +#define get_unaligned(p)					\ +({								\ +	struct packed_dummy_struct {				\ +		typeof(*(p)) __val;				\ +	} __attribute__((packed)) *__ptr = (void *) (p);	\ +								\ +	__ptr->__val;						\ +}) + +/* function prototypes and related defs are in radiotap_iter.h */ + +/** + * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization + * @iterator: radiotap_iterator to initialize + * @radiotap_header: radiotap header to parse + * @max_length: total length we can parse into (eg, whole packet length) + * + * Returns: 0 or a negative error code if there is a problem. + * + * This function initializes an opaque iterator struct which can then + * be passed to ieee80211_radiotap_iterator_next() to visit every radiotap + * argument which is present in the header.  It knows about extended + * present headers and handles them. + * + * How to use: + * call __ieee80211_radiotap_iterator_init() to init a semi-opaque iterator + * struct ieee80211_radiotap_iterator (no need to init the struct beforehand) + * checking for a good 0 return code.  Then loop calling + * __ieee80211_radiotap_iterator_next()... it returns either 0, + * -ENOENT if there are no more args to parse, or -EINVAL if there is a problem. + * The iterator's @this_arg member points to the start of the argument + * associated with the current argument index that is present, which can be + * found in the iterator's @this_arg_index member.  This arg index corresponds + * to the IEEE80211_RADIOTAP_... defines. + * + * Radiotap header length: + * You can find the CPU-endian total radiotap header length in + * iterator->max_length after executing ieee80211_radiotap_iterator_init() + * successfully. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned.  Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + * + * Example code: + * See Documentation/networking/radiotap-headers.txt + */ + +int ieee80211_radiotap_iterator_init( +    struct ieee80211_radiotap_iterator *iterator, +    struct ieee80211_radiotap_header *radiotap_header, +    int max_length) +{ +	/* Linux only supports version 0 radiotap format */ +	if (radiotap_header->it_version) +		return -EINVAL; + +	/* sanity check for allowed length and radiotap length field */ +	if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len))) +		return -EINVAL; + +	iterator->rtheader = radiotap_header; +	iterator->max_length = le16_to_cpu(get_unaligned( +						&radiotap_header->it_len)); +	iterator->arg_index = 0; +	iterator->bitmap_shifter = le32_to_cpu(get_unaligned( +						&radiotap_header->it_present)); +	iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header); +	iterator->this_arg = NULL; + +	/* find payload start allowing for extended bitmap(s) */ + +	if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) { +		while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) & +				   (1<<IEEE80211_RADIOTAP_EXT)) { +			iterator->arg += sizeof(u32); + +			/* +			 * check for insanity where the present bitmaps +			 * keep claiming to extend up to or even beyond the +			 * stated radiotap header length +			 */ + +			if (((ulong)iterator->arg - (ulong)iterator->rtheader) +			    > (ulong)iterator->max_length) +				return -EINVAL; +		} + +		iterator->arg += sizeof(u32); + +		/* +		 * no need to check again for blowing past stated radiotap +		 * header length, because ieee80211_radiotap_iterator_next +		 * checks it before it is dereferenced +		 */ +	} + +	/* we are all initialized happily */ + +	return 0; +} + + +/** + * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg + * @iterator: radiotap_iterator to move to next arg (if any) + * + * Returns: 0 if there is an argument to handle, + * -ENOENT if there are no more args or -EINVAL + * if there is something else wrong. + * + * This function provides the next radiotap arg index (IEEE80211_RADIOTAP_*) + * in @this_arg_index and sets @this_arg to point to the + * payload for the field.  It takes care of alignment handling and extended + * present fields.  @this_arg can be changed by the caller (eg, + * incremented to move inside a compound argument like + * IEEE80211_RADIOTAP_CHANNEL).  The args pointed to are in + * little-endian format whatever the endianess of your CPU. + * + * Alignment Gotcha: + * You must take care when dereferencing iterator.this_arg + * for multibyte types... the pointer is not aligned.  Use + * get_unaligned((type *)iterator.this_arg) to dereference + * iterator.this_arg for type "type" safely on all arches. + */ + +int ieee80211_radiotap_iterator_next( +    struct ieee80211_radiotap_iterator *iterator) +{ + +	/* +	 * small length lookup table for all radiotap types we heard of +	 * starting from b0 in the bitmap, so we can walk the payload +	 * area of the radiotap header +	 * +	 * There is a requirement to pad args, so that args +	 * of a given length must begin at a boundary of that length +	 * -- but note that compound args are allowed (eg, 2 x u16 +	 * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not +	 * a reliable indicator of alignment requirement. +	 * +	 * upper nybble: content alignment for arg +	 * lower nybble: content length for arg +	 */ + +	static const u8 rt_sizes[] = { +		[IEEE80211_RADIOTAP_TSFT] = 0x88, +		[IEEE80211_RADIOTAP_FLAGS] = 0x11, +		[IEEE80211_RADIOTAP_RATE] = 0x11, +		[IEEE80211_RADIOTAP_CHANNEL] = 0x24, +		[IEEE80211_RADIOTAP_FHSS] = 0x22, +		[IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11, +		[IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11, +		[IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22, +		[IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22, +		[IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22, +		[IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11, +		[IEEE80211_RADIOTAP_ANTENNA] = 0x11, +		[IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11, +		[IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11, +		[IEEE80211_RADIOTAP_RX_FLAGS] = 0x22, +		[IEEE80211_RADIOTAP_TX_FLAGS] = 0x22, +		[IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11, +		[IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11, +		/* +		 * add more here as they are defined in +		 * include/net/ieee80211_radiotap.h +		 */ +	}; + +	/* +	 * for every radiotap entry we can at +	 * least skip (by knowing the length)... +	 */ + +	while (iterator->arg_index < (int) sizeof(rt_sizes)) { +		int hit = 0; +		int pad; + +		if (!(iterator->bitmap_shifter & 1)) +			goto next_entry; /* arg not present */ + +		/* +		 * arg is present, account for alignment padding +		 *  8-bit args can be at any alignment +		 * 16-bit args must start on 16-bit boundary +		 * 32-bit args must start on 32-bit boundary +		 * 64-bit args must start on 64-bit boundary +		 * +		 * note that total arg size can differ from alignment of +		 * elements inside arg, so we use upper nybble of length +		 * table to base alignment on +		 * +		 * also note: these alignments are ** relative to the +		 * start of the radiotap header **.  There is no guarantee +		 * that the radiotap header itself is aligned on any +		 * kind of boundary. +		 * +		 * the above is why get_unaligned() is used to dereference +		 * multibyte elements from the radiotap area +		 */ + +		pad = (((ulong)iterator->arg) - +			((ulong)iterator->rtheader)) & +			((rt_sizes[iterator->arg_index] >> 4) - 1); + +		if (pad) +			iterator->arg += +				(rt_sizes[iterator->arg_index] >> 4) - pad; + +		/* +		 * this is what we will return to user, but we need to +		 * move on first so next call has something fresh to test +		 */ +		iterator->this_arg_index = iterator->arg_index; +		iterator->this_arg = iterator->arg; +		hit = 1; + +		/* internally move on the size of this arg */ +		iterator->arg += rt_sizes[iterator->arg_index] & 0x0f; + +		/* +		 * check for insanity where we are given a bitmap that +		 * claims to have more arg content than the length of the +		 * radiotap section.  We will normally end up equalling this +		 * max_length on the last arg, never exceeding it. +		 */ + +		if (((ulong)iterator->arg - (ulong)iterator->rtheader) > +		    (ulong) iterator->max_length) +			return -EINVAL; + +	next_entry: +		iterator->arg_index++; +		if (unlikely((iterator->arg_index & 31) == 0)) { +			/* completed current u32 bitmap */ +			if (iterator->bitmap_shifter & 1) { +				/* b31 was set, there is more */ +				/* move to next u32 bitmap */ +				iterator->bitmap_shifter = le32_to_cpu( +					get_unaligned(iterator->next_bitmap)); +				iterator->next_bitmap++; +			} else +				/* no more bitmaps: end */ +				iterator->arg_index = sizeof(rt_sizes); +		} else /* just try the next bit */ +			iterator->bitmap_shifter >>= 1; + +		/* if we found a valid arg earlier, return it now */ +		if (hit) +			return 0; +	} + +	/* we don't know how to handle any more args, we're done */ +	return -ENOENT; +} diff --git a/package/hostapd/files/radiotap.h b/package/hostapd/files/radiotap.h new file mode 100644 index 000000000..508264c4c --- /dev/null +++ b/package/hostapd/files/radiotap.h @@ -0,0 +1,242 @@ +/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ +/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ + +/*- + * Copyright (c) 2003, 2004 David Young.  All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + *    notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + *    notice, this list of conditions and the following disclaimer in the + *    documentation and/or other materials provided with the distribution. + * 3. The name of David Young may not be used to endorse or promote + *    products derived from this software without specific prior + *    written permission. + * + * THIS SOFTWARE IS PROVIDED BY DAVID YOUNG ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DAVID + * YOUNG BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY + * OF SUCH DAMAGE. + */ + +/* + * Modifications to fit into the linux IEEE 802.11 stack, + * Mike Kershaw (dragorn@kismetwireless.net) + */ + +#ifndef IEEE80211RADIOTAP_H +#define IEEE80211RADIOTAP_H + +#include <stdint.h> + +/* Base version of the radiotap packet header data */ +#define PKTHDR_RADIOTAP_VERSION		0 + +/* A generic radio capture format is desirable. There is one for + * Linux, but it is neither rigidly defined (there were not even + * units given for some fields) nor easily extensible. + * + * I suggest the following extensible radio capture format. It is + * based on a bitmap indicating which fields are present. + * + * I am trying to describe precisely what the application programmer + * should expect in the following, and for that reason I tell the + * units and origin of each measurement (where it applies), or else I + * use sufficiently weaselly language ("is a monotonically nondecreasing + * function of...") that I cannot set false expectations for lawyerly + * readers. + */ + +/* The radio capture header precedes the 802.11 header. + * All data in the header is little endian on all platforms. + */ +struct ieee80211_radiotap_header { +	uint8_t it_version;	/* Version 0. Only increases +				 * for drastic changes, +				 * introduction of compatible +				 * new fields does not count. +				 */ +	uint8_t it_pad; +	uint16_t it_len;	/* length of the whole +				 * header in bytes, including +				 * it_version, it_pad, +				 * it_len, and data fields. +				 */ +	uint32_t it_present;	/* A bitmap telling which +				 * fields are present. Set bit 31 +				 * (0x80000000) to extend the +				 * bitmap by another 32 bits. +				 * Additional extensions are made +				 * by setting bit 31. +				 */ +}; + +/* Name                                 Data type    Units + * ----                                 ---------    ----- + * + * IEEE80211_RADIOTAP_TSFT              __le64       microseconds + * + *      Value in microseconds of the MAC's 64-bit 802.11 Time + *      Synchronization Function timer when the first bit of the + *      MPDU arrived at the MAC. For received frames, only. + * + * IEEE80211_RADIOTAP_CHANNEL           2 x uint16_t   MHz, bitmap + * + *      Tx/Rx frequency in MHz, followed by flags (see below). + * + * IEEE80211_RADIOTAP_FHSS              uint16_t       see below + * + *      For frequency-hopping radios, the hop set (first byte) + *      and pattern (second byte). + * + * IEEE80211_RADIOTAP_RATE              u8           500kb/s + * + *      Tx/Rx data rate + * + * IEEE80211_RADIOTAP_DBM_ANTSIGNAL     s8           decibels from + *                                                   one milliwatt (dBm) + * + *      RF signal power at the antenna, decibel difference from + *      one milliwatt. + * + * IEEE80211_RADIOTAP_DBM_ANTNOISE      s8           decibels from + *                                                   one milliwatt (dBm) + * + *      RF noise power at the antenna, decibel difference from one + *      milliwatt. + * + * IEEE80211_RADIOTAP_DB_ANTSIGNAL      u8           decibel (dB) + * + *      RF signal power at the antenna, decibel difference from an + *      arbitrary, fixed reference. + * + * IEEE80211_RADIOTAP_DB_ANTNOISE       u8           decibel (dB) + * + *      RF noise power at the antenna, decibel difference from an + *      arbitrary, fixed reference point. + * + * IEEE80211_RADIOTAP_LOCK_QUALITY      uint16_t       unitless + * + *      Quality of Barker code lock. Unitless. Monotonically + *      nondecreasing with "better" lock strength. Called "Signal + *      Quality" in datasheets.  (Is there a standard way to measure + *      this?) + * + * IEEE80211_RADIOTAP_TX_ATTENUATION    uint16_t       unitless + * + *      Transmit power expressed as unitless distance from max + *      power set at factory calibration.  0 is max power. + *      Monotonically nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DB_TX_ATTENUATION uint16_t       decibels (dB) + * + *      Transmit power expressed as decibel distance from max power + *      set at factory calibration.  0 is max power.  Monotonically + *      nondecreasing with lower power levels. + * + * IEEE80211_RADIOTAP_DBM_TX_POWER      s8           decibels from + *                                                   one milliwatt (dBm) + * + *      Transmit power expressed as dBm (decibels from a 1 milliwatt + *      reference). This is the absolute power level measured at + *      the antenna port. + * + * IEEE80211_RADIOTAP_FLAGS             u8           bitmap + * + *      Properties of transmitted and received frames. See flags + *      defined below. + * + * IEEE80211_RADIOTAP_ANTENNA           u8           antenna index + * + *      Unitless indication of the Rx/Tx antenna for this packet. + *      The first antenna is antenna 0. + * + * IEEE80211_RADIOTAP_RX_FLAGS          uint16_t       bitmap + * + *     Properties of received frames. See flags defined below. + * + * IEEE80211_RADIOTAP_TX_FLAGS          uint16_t       bitmap + * + *     Properties of transmitted frames. See flags defined below. + * + * IEEE80211_RADIOTAP_RTS_RETRIES       u8           data + * + *     Number of rts retries a transmitted frame used. + * + * IEEE80211_RADIOTAP_DATA_RETRIES      u8           data + * + *     Number of unicast retries a transmitted frame used. + * + */ +enum ieee80211_radiotap_type { +	IEEE80211_RADIOTAP_TSFT = 0, +	IEEE80211_RADIOTAP_FLAGS = 1, +	IEEE80211_RADIOTAP_RATE = 2, +	IEEE80211_RADIOTAP_CHANNEL = 3, +	IEEE80211_RADIOTAP_FHSS = 4, +	IEEE80211_RADIOTAP_DBM_ANTSIGNAL = 5, +	IEEE80211_RADIOTAP_DBM_ANTNOISE = 6, +	IEEE80211_RADIOTAP_LOCK_QUALITY = 7, +	IEEE80211_RADIOTAP_TX_ATTENUATION = 8, +	IEEE80211_RADIOTAP_DB_TX_ATTENUATION = 9, +	IEEE80211_RADIOTAP_DBM_TX_POWER = 10, +	IEEE80211_RADIOTAP_ANTENNA = 11, +	IEEE80211_RADIOTAP_DB_ANTSIGNAL = 12, +	IEEE80211_RADIOTAP_DB_ANTNOISE = 13, +	IEEE80211_RADIOTAP_RX_FLAGS = 14, +	IEEE80211_RADIOTAP_TX_FLAGS = 15, +	IEEE80211_RADIOTAP_RTS_RETRIES = 16, +	IEEE80211_RADIOTAP_DATA_RETRIES = 17, +	IEEE80211_RADIOTAP_EXT = 31 +}; + +/* Channel flags. */ +#define	IEEE80211_CHAN_TURBO	0x0010	/* Turbo channel */ +#define	IEEE80211_CHAN_CCK	0x0020	/* CCK channel */ +#define	IEEE80211_CHAN_OFDM	0x0040	/* OFDM channel */ +#define	IEEE80211_CHAN_2GHZ	0x0080	/* 2 GHz spectrum channel. */ +#define	IEEE80211_CHAN_5GHZ	0x0100	/* 5 GHz spectrum channel */ +#define	IEEE80211_CHAN_PASSIVE	0x0200	/* Only passive scan allowed */ +#define	IEEE80211_CHAN_DYN	0x0400	/* Dynamic CCK-OFDM channel */ +#define	IEEE80211_CHAN_GFSK	0x0800	/* GFSK channel (FHSS PHY) */ + +/* For IEEE80211_RADIOTAP_FLAGS */ +#define	IEEE80211_RADIOTAP_F_CFP	0x01	/* sent/received +						 * during CFP +						 */ +#define	IEEE80211_RADIOTAP_F_SHORTPRE	0x02	/* sent/received +						 * with short +						 * preamble +						 */ +#define	IEEE80211_RADIOTAP_F_WEP	0x04	/* sent/received +						 * with WEP encryption +						 */ +#define	IEEE80211_RADIOTAP_F_FRAG	0x08	/* sent/received +						 * with fragmentation +						 */ +#define	IEEE80211_RADIOTAP_F_FCS	0x10	/* frame includes FCS */ +#define	IEEE80211_RADIOTAP_F_DATAPAD	0x20	/* frame has padding between +						 * 802.11 header and payload +						 * (to 32-bit boundary) +						 */ +/* For IEEE80211_RADIOTAP_RX_FLAGS */ +#define IEEE80211_RADIOTAP_F_RX_BADFCS	0x0001	/* frame failed crc check */ + +/* For IEEE80211_RADIOTAP_TX_FLAGS */ +#define IEEE80211_RADIOTAP_F_TX_FAIL	0x0001	/* failed due to excessive +						 * retries */ +#define IEEE80211_RADIOTAP_F_TX_CTS	0x0002	/* used cts 'protection' */ +#define IEEE80211_RADIOTAP_F_TX_RTS	0x0004	/* used rts/cts handshake */ + +#endif				/* IEEE80211_RADIOTAP_H */ diff --git a/package/hostapd/files/radiotap_iter.h b/package/hostapd/files/radiotap_iter.h new file mode 100644 index 000000000..92a798a67 --- /dev/null +++ b/package/hostapd/files/radiotap_iter.h @@ -0,0 +1,41 @@ +#ifndef __RADIOTAP_ITER_H +#define __RADIOTAP_ITER_H + +#include "radiotap.h" + +/* Radiotap header iteration + *   implemented in radiotap.c + */ +/** + * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args + * @rtheader: pointer to the radiotap header we are walking through + * @max_length: length of radiotap header in cpu byte ordering + * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg + * @this_arg: pointer to current radiotap arg + * @arg_index: internal next argument index + * @arg: internal next argument pointer + * @next_bitmap: internal pointer to next present u32 + * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present + */ + +struct ieee80211_radiotap_iterator { +	struct ieee80211_radiotap_header *rtheader; +	int max_length; +	int this_arg_index; +	unsigned char *this_arg; + +	int arg_index; +	unsigned char *arg; +	uint32_t *next_bitmap; +	uint32_t bitmap_shifter; +}; + +extern int ieee80211_radiotap_iterator_init( +   struct ieee80211_radiotap_iterator *iterator, +   struct ieee80211_radiotap_header *radiotap_header, +   int max_length); + +extern int ieee80211_radiotap_iterator_next( +   struct ieee80211_radiotap_iterator *iterator); + +#endif /* __RADIOTAP_ITER_H */ diff --git a/package/hostapd/patches/007-add-nl80211-driver.patch b/package/hostapd/patches/007-add-nl80211-driver.patch new file mode 100644 index 000000000..538890c3e --- /dev/null +++ b/package/hostapd/patches/007-add-nl80211-driver.patch @@ -0,0 +1,17 @@ +Index: hostapd-20071107_03ec0ec5cdb974d51a4a2a566bea4c4568138576/hostapd/Makefile +=================================================================== +--- hostapd-20071107_03ec0ec5cdb974d51a4a2a566bea4c4568138576.orig/hostapd/Makefile	2007-11-16 05:08:24.000000000 +0100 ++++ hostapd-20071107_03ec0ec5cdb974d51a4a2a566bea4c4568138576/hostapd/Makefile	2008-02-16 19:38:51.000000000 +0100 +@@ -121,6 +121,12 @@ OBJS += driver_devicescape.o + LIBS += -lnl + endif +  ++ifdef CONFIG_DRIVER_NL80211 ++CFLAGS += -DCONFIG_DRIVER_NL80211 ++OBJS += driver_nl80211.o radiotap.o ++LIBS += -lnl ++endif ++ + ifdef CONFIG_DRIVER_BSD + CFLAGS += -DCONFIG_DRIVER_BSD + OBJS += driver_bsd.o | 
