From: Nicolas Thill Date: Tue, 22 Nov 2005 03:10:56 +0000 (+0000) Subject: update hostapd to new upstream stable release (v0.4.7) X-Git-Tag: reboot~31601 X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=328c96fad5b423fe3ec81122706c554a2743d3e4;p=oweals%2Fopenwrt.git update hostapd to new upstream stable release (v0.4.7) SVN-Revision: 2542 --- diff --git a/openwrt/package/hostapd/Makefile b/openwrt/package/hostapd/Makefile index 9afea1c21b..c3b8ec6aa0 100644 --- a/openwrt/package/hostapd/Makefile +++ b/openwrt/package/hostapd/Makefile @@ -3,9 +3,9 @@ include $(TOPDIR)/rules.mk PKG_NAME:=hostapd -PKG_VERSION:=0.4.5 +PKG_VERSION:=0.4.7 PKG_RELEASE:=1 -PKG_MD5SUM:=3d775a19f8071d6f8a363ef0dad58655 +PKG_MD5SUM:=b299211691fe8a6840e7adafda351d2b PKG_SOURCE_URL:=http://hostap.epitest.fi/releases/ PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz diff --git a/openwrt/package/hostapd/patches/002-madwifi.patch b/openwrt/package/hostapd/patches/002-madwifi.patch deleted file mode 100644 index f53ad7d898..0000000000 --- a/openwrt/package/hostapd/patches/002-madwifi.patch +++ /dev/null @@ -1,1278 +0,0 @@ -diff -ruN hostapd-0.4.5-old/driver_madwifi.c hostapd-0.4.5-new/driver_madwifi.c ---- hostapd-0.4.5-old/driver_madwifi.c 2005-10-23 14:52:39.000000000 +0200 -+++ hostapd-0.4.5-new/driver_madwifi.c 2005-10-23 15:09:45.000000000 +0200 -@@ -20,19 +20,20 @@ - #include - #include - #include -+#include -+#include - - #include - #include - #ifdef WME_NUM_AC - /* Assume this is built against BSD branch of madwifi driver. */ --#define MADWIFI_BSD - #include - #endif /* WME_NUM_AC */ - #include - #include - - #include --#include "wireless_copy.h" -+#include - - #include - #include -diff -ruN hostapd-0.4.5-old/driver_madwifi.~c hostapd-0.4.5-new/driver_madwifi.~c ---- hostapd-0.4.5-old/driver_madwifi.~c 1970-01-01 01:00:00.000000000 +0100 -+++ hostapd-0.4.5-new/driver_madwifi.~c 2005-10-23 15:06:20.000000000 +0200 -@@ -0,0 +1,1248 @@ -+/* -+ * Host AP - driver interaction with MADWIFI 802.11 driver -+ * Copyright (c) 2004, Sam Leffler -+ * Copyright (c) 2004, Video54 Technologies -+ * -+ * 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 -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include -+#include -+#ifdef WME_NUM_AC -+/* Assume this is built against BSD branch of madwifi driver. */ -+#include -+#endif /* WME_NUM_AC */ -+#include -+#include -+ -+#include -+#include -+ -+#include -+#include -+ -+#include "hostapd.h" -+#include "driver.h" -+#include "ieee802_1x.h" -+#include "eloop.h" -+#include "priv_netlink.h" -+#include "sta_info.h" -+#include "l2_packet.h" -+#include "hostap_common.h" -+ -+#include "eapol_sm.h" -+#include "wpa.h" -+#include "radius.h" -+#include "ieee802_11.h" -+#include "accounting.h" -+#include "common.h" -+ -+ -+struct madwifi_driver_data { -+ struct driver_ops ops; /* base class */ -+ struct hostapd_data *hapd; /* back pointer */ -+ -+ char iface[IFNAMSIZ + 1]; -+ int ifindex; -+ struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ -+ struct l2_packet_data *sock_recv; /* raw packet recv socket */ -+ int ioctl_sock; /* socket for ioctl() use */ -+ int wext_sock; /* socket for wireless events */ -+ int we_version; -+ u8 acct_mac[ETH_ALEN]; -+ struct hostap_sta_driver_data acct_data; -+}; -+ -+static const struct driver_ops madwifi_driver_ops; -+ -+static int madwifi_sta_deauth(void *priv, u8 *addr, int reason_code); -+ -+static int -+set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) -+{ -+#define N(a) (sizeof(a)/sizeof(a[0])) -+ struct iwreq iwr; -+ -+ memset(&iwr, 0, sizeof(iwr)); -+ strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ); -+ if (len < IFNAMSIZ) { -+ /* -+ * Argument data fits inline; put it there. -+ */ -+ memcpy(iwr.u.name, data, len); -+ } else { -+ /* -+ * Argument data too big for inline transfer; setup a -+ * parameter block instead; the kernel will transfer -+ * the data for the driver. -+ */ -+ iwr.u.data.pointer = data; -+ iwr.u.data.length = len; -+ } -+ -+ if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { -+ static const char *opnames[] = { -+ "ioctl[IEEE80211_IOCTL_SETPARAM]", -+ "ioctl[IEEE80211_IOCTL_GETPARAM]", -+ "ioctl[IEEE80211_IOCTL_SETKEY]", -+ "ioctl[SIOCIWFIRSTPRIV+3]", -+ "ioctl[IEEE80211_IOCTL_DELKEY]", -+ "ioctl[SIOCIWFIRSTPRIV+5]", -+ "ioctl[IEEE80211_IOCTL_SETMLME]", -+ "ioctl[SIOCIWFIRSTPRIV+7]", -+ "ioctl[IEEE80211_IOCTL_SETOPTIE]", -+ "ioctl[IEEE80211_IOCTL_GETOPTIE]", -+ "ioctl[IEEE80211_IOCTL_ADDMAC]", -+ "ioctl[SIOCIWFIRSTPRIV+11]", -+ "ioctl[IEEE80211_IOCTL_DELMAC]", -+ "ioctl[SIOCIWFIRSTPRIV+13]", -+ "ioctl[IEEE80211_IOCTL_CHANLIST]", -+ "ioctl[SIOCIWFIRSTPRIV+15]", -+ "ioctl[IEEE80211_IOCTL_GETRSN]", -+ "ioctl[SIOCIWFIRSTPRIV+17]", -+ "ioctl[IEEE80211_IOCTL_GETKEY]", -+ }; -+ op -= SIOCIWFIRSTPRIV; -+ if (0 <= op && op < N(opnames)) -+ perror(opnames[op]); -+ else -+ perror("ioctl[unknown???]"); -+ return -1; -+ } -+ return 0; -+#undef N -+} -+ -+static int -+set80211param(struct madwifi_driver_data *drv, int op, int arg) -+{ -+ struct iwreq iwr; -+ -+ memset(&iwr, 0, sizeof(iwr)); -+ strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ); -+ iwr.u.mode = op; -+ memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); -+ -+ if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { -+ perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); -+ return -1; -+ } -+ return 0; -+} -+ -+static const char * -+ether_sprintf(const u8 *addr) -+{ -+ static char buf[sizeof(MACSTR)]; -+ -+ if (addr != NULL) -+ snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); -+ else -+ snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); -+ return buf; -+} -+ -+/* -+ * Configure WPA parameters. -+ */ -+static int -+madwifi_configure_wpa(struct madwifi_driver_data *drv) -+{ -+ hostapd *hapd = drv->hapd; -+ struct hostapd_config *conf = hapd->conf; -+ int v; -+ -+ switch (conf->wpa_group) { -+ case WPA_CIPHER_CCMP: -+ v = IEEE80211_CIPHER_AES_CCM; -+ break; -+ case WPA_CIPHER_TKIP: -+ v = IEEE80211_CIPHER_TKIP; -+ break; -+ case WPA_CIPHER_WEP104: -+ v = IEEE80211_CIPHER_WEP; -+ break; -+ case WPA_CIPHER_WEP40: -+ v = IEEE80211_CIPHER_WEP; -+ break; -+ case WPA_CIPHER_NONE: -+ v = IEEE80211_CIPHER_NONE; -+ break; -+ default: -+ printf("Unknown group key cipher %u\n", -+ conf->wpa_group); -+ return -1; -+ } -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: group key cipher=%d\n", __func__, v); -+ if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { -+ printf("Unable to set group key cipher to %u\n", v); -+ return -1; -+ } -+ if (v == IEEE80211_CIPHER_WEP) { -+ /* key length is done only for specific ciphers */ -+ v = (conf->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); -+ if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { -+ printf("Unable to set group key length to %u\n", v); -+ return -1; -+ } -+ } -+ -+ v = 0; -+ if (conf->wpa_pairwise & WPA_CIPHER_CCMP) -+ v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) -+ v |= 1<wpa_pairwise & WPA_CIPHER_NONE) -+ v |= 1<wpa_key_mgmt); -+ if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, conf->wpa_key_mgmt)) { -+ printf("Unable to set key management algorithms to 0x%x\n", -+ conf->wpa_key_mgmt); -+ return -1; -+ } -+ -+ v = 0; -+ if (conf->rsn_preauth) -+ v |= BIT(0); -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: rsn capabilities=0x%x\n", __func__, conf->rsn_preauth); -+ if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { -+ printf("Unable to set RSN capabilities to 0x%x\n", v); -+ return -1; -+ } -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: enable WPA= 0x%x\n", __func__, conf->wpa); -+ if (set80211param(drv, IEEE80211_PARAM_WPA, conf->wpa)) { -+ printf("Unable to set WPA to %u\n", conf->wpa); -+ return -1; -+ } -+ return 0; -+} -+ -+ -+static int -+madwifi_set_iface_flags(void *priv, int dev_up) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ struct ifreq ifr; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, -+ "%s: dev_up=%d\n", __func__, dev_up); -+ -+ if (drv->ioctl_sock < 0) -+ return -1; -+ -+ memset(&ifr, 0, sizeof(ifr)); -+ snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->iface); -+ -+ if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) { -+ perror("ioctl[SIOCGIFFLAGS]"); -+ 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; -+ } -+ -+ if (dev_up) { -+ memset(&ifr, 0, sizeof(ifr)); -+ snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->iface); -+ ifr.ifr_mtu = HOSTAPD_MTU; -+ if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) { -+ perror("ioctl[SIOCSIFMTU]"); -+ printf("Setting MTU failed - trying to survive with " -+ "current value\n"); -+ } -+ } -+ -+ return 0; -+} -+ -+static int -+madwifi_set_ieee8021x(void *priv, int enabled) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ struct hostapd_config *conf = hapd->conf; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, -+ "%s: enabled=%d\n", __func__, enabled); -+ -+ if (!enabled) { -+ /* XXX restore state */ -+ return set80211param(priv, IEEE80211_PARAM_AUTHMODE, -+ IEEE80211_AUTH_AUTO); -+ } -+ if (!conf->wpa && !conf->ieee802_1x) { -+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, -+ HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); -+ return -1; -+ } -+ if (conf->wpa && madwifi_configure_wpa(drv) != 0) { -+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, -+ HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); -+ return -1; -+ } -+ if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, -+ (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { -+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER, -+ HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); -+ return -1; -+ } -+ return madwifi_set_iface_flags(priv, 1); -+} -+ -+static int -+madwifi_set_privacy(void *priv, int enabled) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: enabled=%d\n", __func__, enabled); -+ -+ return set80211param(priv, IEEE80211_PARAM_PRIVACY, enabled); -+} -+ -+static int -+madwifi_set_sta_authorized(void *priv, u8 *addr, int authorized) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ struct ieee80211req_mlme mlme; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, -+ "%s: addr=%s authorized=%d\n", -+ __func__, ether_sprintf(addr), authorized); -+ -+ if (authorized) -+ mlme.im_op = IEEE80211_MLME_AUTHORIZE; -+ else -+ mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; -+ mlme.im_reason = 0; -+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); -+ return set80211priv(priv, IEEE80211_IOCTL_SETMLME, &mlme, -+ sizeof(mlme)); -+} -+ -+static int -+madwifi_del_key(void *priv, unsigned char *addr, int key_idx) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ struct ieee80211req_del_key wk; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: addr=%s key_idx=%d\n", -+ __func__, ether_sprintf(addr), key_idx); -+ -+ memset(&wk, 0, sizeof(wk)); -+ if (addr != NULL) { -+ memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); -+ wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; -+ } else { -+ wk.idk_keyix = key_idx; -+ } -+ -+ return set80211priv(priv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); -+} -+ -+static int -+madwifi_set_key(void *priv, const char *alg, -+ unsigned char *addr, int key_idx, -+ u8 *key, size_t key_len) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ struct ieee80211req_key wk; -+ u_int8_t cipher; -+ -+ if (strcmp(alg, "none") == 0) -+ return madwifi_del_key(priv, addr, key_idx); -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: alg=%s addr=%s key_idx=%d\n", -+ __func__, alg, ether_sprintf(addr), key_idx); -+ -+ if (strcmp(alg, "WEP") == 0) -+ cipher = IEEE80211_CIPHER_WEP; -+ else if (strcmp(alg, "TKIP") == 0) -+ cipher = IEEE80211_CIPHER_TKIP; -+ else if (strcmp(alg, "CCMP") == 0) -+ cipher = IEEE80211_CIPHER_AES_CCM; -+ else { -+ printf("%s: unknown/unsupported algorithm %s\n", -+ __func__, alg); -+ return -1; -+ } -+ -+ if (key_len > sizeof(wk.ik_keydata)) { -+ printf("%s: key length %lu too big\n", __func__, -+ (unsigned long) key_len); -+ return -3; -+ } -+ -+ memset(&wk, 0, sizeof(wk)); -+ wk.ik_type = cipher; -+ wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; -+ if (addr == NULL) { -+ memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); -+ wk.ik_keyix = key_idx; -+ wk.ik_flags |= IEEE80211_KEY_DEFAULT; -+ } else { -+ memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); -+ wk.ik_keyix = IEEE80211_KEYIX_NONE; -+ } -+ wk.ik_keylen = key_len; -+ memcpy(wk.ik_keydata, key, key_len); -+ -+ return set80211priv(priv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); -+} -+ -+ -+static int -+madwifi_get_seqnum(void *priv, u8 *addr, int idx, u8 *seq) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ struct ieee80211req_key wk; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: addr=%s idx=%d\n", __func__, ether_sprintf(addr), idx); -+ -+ memset(&wk, 0, sizeof(wk)); -+ if (addr == NULL) -+ memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); -+ else -+ memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); -+ wk.ik_keyix = idx; -+ -+ if (set80211priv(priv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { -+ printf("Failed to get encryption.\n"); -+ return -1; -+ } -+ -+#ifdef WORDS_BIGENDIAN -+ { -+ /* -+ * wk.ik_keytsc is in host byte order (big endian), need to -+ * swap it to match with the byte order used in WPA. -+ */ -+ int i; -+ u8 tmp[WPA_KEY_RSC_LEN]; -+ memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); -+ for (i = 0; i < WPA_KEY_RSC_LEN; i++) { -+ seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; -+ } -+ } -+#else /* WORDS_BIGENDIAN */ -+ memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); -+#endif /* WORDS_BIGENDIAN */ -+ return 0; -+} -+ -+ -+static int -+madwifi_flush(void *priv) -+{ -+#ifdef MADWIFI_BSD -+ u8 allsta[IEEE80211_ADDR_LEN]; -+ memset(allsta, 0xff, IEEE80211_ADDR_LEN); -+ return madwifi_sta_deauth(priv, allsta, IEEE80211_REASON_AUTH_LEAVE); -+#else /* MADWIFI_BSD */ -+ return 0; /* XXX */ -+#endif /* MADWIFI_BSD */ -+} -+ -+ -+static int -+madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, -+ u8 *addr) -+{ -+ struct madwifi_driver_data *drv = priv; -+ -+#ifdef MADWIFI_BSD -+ struct ieee80211req_sta_stats stats; -+ -+ memset(data, 0, sizeof(*data)); -+ -+ /* -+ * Fetch statistics for station from the system. -+ */ -+ memset(&stats, 0, sizeof(stats)); -+ memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); -+ if (set80211priv(drv, IEEE80211_IOCTL_GETSTASTATS, &stats, -+ sizeof(stats))) { -+ if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { -+ memcpy(data, &drv->acct_data, sizeof(*data)); -+ return 0; -+ } -+ -+ printf("Failed to get station stats information element.\n"); -+ return -1; -+ } -+ -+ data->rx_packets = stats.is_stats.ns_rx_data; -+ data->rx_bytes = stats.is_stats.ns_rx_bytes; -+ data->tx_packets = stats.is_stats.ns_tx_data; -+ data->tx_bytes = stats.is_stats.ns_tx_bytes; -+ return 0; -+ -+#else /* MADWIFI_BSD */ -+ -+ char buf[1024], line[128], *pos; -+ FILE *f; -+ unsigned long val; -+ -+ memset(data, 0, sizeof(*data)); -+ snprintf(buf, sizeof(buf), "/proc/net/madwifi/%s/" MACSTR, -+ drv->iface, MAC2STR(addr)); -+ -+ f = fopen(buf, "r"); -+ if (!f) { -+ if (memcmp(addr, drv->acct_mac, ETH_ALEN) != 0) -+ return -1; -+ memcpy(data, &drv->acct_data, sizeof(*data)); -+ return 0; -+ } -+ /* Need to read proc file with in one piece, so use large enough -+ * buffer. */ -+ setbuffer(f, buf, sizeof(buf)); -+ -+ while (fgets(line, sizeof(line), f)) { -+ pos = strchr(line, '='); -+ if (!pos) -+ continue; -+ *pos++ = '\0'; -+ val = strtoul(pos, NULL, 10); -+ if (strcmp(line, "rx_packets") == 0) -+ data->rx_packets = val; -+ else if (strcmp(line, "tx_packets") == 0) -+ data->tx_packets = val; -+ else if (strcmp(line, "rx_bytes") == 0) -+ data->rx_bytes = val; -+ else if (strcmp(line, "tx_bytes") == 0) -+ data->tx_bytes = val; -+ } -+ -+ fclose(f); -+ -+ return 0; -+#endif /* MADWIFI_BSD */ -+} -+ -+ -+static int -+madwifi_sta_clear_stats(void *priv, u8 *addr) -+{ -+#ifdef MADWIFI_BSD -+ struct madwifi_driver_data *drv = priv; -+ struct hostapd_data *hapd = drv->hapd; -+ struct ieee80211req_mlme mlme; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "%s: addr=%s\n", -+ __func__, ether_sprintf(addr)); -+ -+ mlme.im_op = IEEE80211_MLME_CLEAR_STATS; -+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); -+ return set80211priv(priv, IEEE80211_IOCTL_SETMLME, &mlme, -+ sizeof(mlme)); -+#else /* MADWIFI_BSD */ -+ return 0; /* FIX */ -+#endif /* MADWIFI_BSD */ -+} -+ -+ -+static int -+madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) -+{ -+ /* -+ * Do nothing; we setup parameters at startup that define the -+ * contents of the beacon information element. -+ */ -+ return 0; -+} -+ -+static int -+madwifi_sta_deauth(void *priv, u8 *addr, int reason_code) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ struct ieee80211req_mlme mlme; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: addr=%s reason_code=%d\n", -+ __func__, ether_sprintf(addr), reason_code); -+ -+ mlme.im_op = IEEE80211_MLME_DEAUTH; -+ mlme.im_reason = reason_code; -+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); -+ return set80211priv(priv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); -+} -+ -+static int -+madwifi_sta_disassoc(void *priv, u8 *addr, int reason_code) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ struct ieee80211req_mlme mlme; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "%s: addr=%s reason_code=%d\n", -+ __func__, ether_sprintf(addr), reason_code); -+ -+ mlme.im_reason = reason_code; -+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); -+ return set80211priv(priv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); -+} -+ -+static int -+madwifi_del_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -+{ -+ struct hostapd_data *hapd = drv->hapd; -+ struct sta_info *sta; -+ -+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, -+ HOSTAPD_LEVEL_INFO, "deassociated"); -+ -+ sta = ap_get_sta(hapd, addr); -+ if (sta != NULL) { -+ sta->flags &= ~WLAN_STA_ASSOC; -+ wpa_sm_event(hapd, sta, WPA_DISASSOC); -+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; -+ ieee802_1x_set_port_enabled(hapd, sta, 0); -+ ap_free_sta(hapd, sta); -+ } -+ return 0; -+} -+ -+static int -+madwifi_process_wpa_ie(struct madwifi_driver_data *drv, struct sta_info *sta) -+{ -+ struct hostapd_data *hapd = drv->hapd; -+ struct ieee80211req_wpaie ie; -+ int ielen, res; -+ -+ /* -+ * Fetch negotiated WPA/RSN parameters from the system. -+ */ -+ memset(&ie, 0, sizeof(ie)); -+ memcpy(ie.wpa_macaddr, sta->addr, IEEE80211_ADDR_LEN); -+ if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { -+ printf("Failed to get WPA/RSN information element.\n"); -+ return -1; /* XXX not right */ -+ } -+ ielen = ie.wpa_ie[1]; -+ if (ielen == 0) { -+ printf("No WPA/RSN information element for station!?\n"); -+ return -1; /* XXX not right */ -+ } -+ ielen += 2; -+ res = wpa_validate_wpa_ie(hapd, sta, ie.wpa_ie, ielen, -+ ie.wpa_ie[0] == WLAN_EID_RSN ? -+ HOSTAPD_WPA_VERSION_WPA2 : HOSTAPD_WPA_VERSION_WPA); -+ if (res != WPA_IE_OK) { -+ printf("WPA/RSN information element rejected? (res %u)\n", res); -+ return -1; -+ } -+ free(sta->wpa_ie); -+ sta->wpa_ie = malloc(ielen); -+ if (sta->wpa_ie == NULL) { -+ printf("No memory to save WPA/RSN information element!\n"); -+ return -1; -+ } -+ memcpy(sta->wpa_ie, ie.wpa_ie, ielen); -+ sta->wpa_ie_len = ielen; -+ return 0; -+} -+ -+static int -+madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) -+{ -+ struct hostapd_data *hapd = drv->hapd; -+ struct sta_info *sta; -+ int new_assoc; -+ -+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, -+ HOSTAPD_LEVEL_INFO, "associated"); -+ -+ sta = ap_get_sta(hapd, addr); -+ if (sta) { -+ accounting_sta_stop(hapd, sta); -+ } else { -+ sta = ap_sta_add(hapd, addr); -+ if (sta == NULL) -+ return -1; -+ } -+ -+ if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { -+ /* Cached accounting data is not valid anymore. */ -+ memset(drv->acct_mac, 0, ETH_ALEN); -+ memset(&drv->acct_data, 0, sizeof(drv->acct_data)); -+ } -+ accounting_sta_get_id(hapd, sta); -+ -+ if (hapd->conf->wpa) { -+ if (madwifi_process_wpa_ie(drv, sta)) -+ return -1; -+ } else { -+ free(sta->wpa_ie); -+ sta->wpa_ie = NULL; -+ sta->wpa_ie_len = 0; -+ } -+ -+ /* -+ * Now that the internal station state is setup -+ * kick the authenticator into action. -+ */ -+ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; -+ sta->flags |= WLAN_STA_ASSOC; -+ wpa_sm_event(hapd, sta, WPA_ASSOC); -+ hostapd_new_assoc_sta(hapd, sta, !new_assoc); -+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); -+ return 0; -+} -+ -+static void -+madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, -+ char *custom) -+{ -+ struct hostapd_data *hapd = drv->hapd; -+ -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Custom wireless event: '%s'\n", -+ custom); -+ -+ if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { -+ char *pos; -+ u8 addr[ETH_ALEN]; -+ pos = strstr(custom, "addr="); -+ if (pos == NULL) { -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "MLME-MICHAELMICFAILURE.indication " -+ "without sender address ignored\n"); -+ return; -+ } -+ pos += 5; -+ if (hwaddr_aton(pos, addr) == 0) { -+ ieee80211_michael_mic_failure(drv->hapd, addr, 1); -+ } else { -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "MLME-MICHAELMICFAILURE.indication " -+ "with invalid MAC address"); -+ } -+ } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { -+ char *key, *value; -+ u32 val; -+ key = custom; -+ while ((key = strchr(key, '\n')) != NULL) { -+ key++; -+ value = strchr(key, '='); -+ if (value == NULL) -+ continue; -+ *value++ = '\0'; -+ val = strtoul(value, NULL, 10); -+ if (strcmp(key, "mac") == 0) -+ hwaddr_aton(value, drv->acct_mac); -+ else if (strcmp(key, "rx_packets") == 0) -+ drv->acct_data.rx_packets = val; -+ else if (strcmp(key, "tx_packets") == 0) -+ drv->acct_data.tx_packets = val; -+ else if (strcmp(key, "rx_bytes") == 0) -+ drv->acct_data.rx_bytes = val; -+ else if (strcmp(key, "tx_bytes") == 0) -+ drv->acct_data.tx_bytes = val; -+ key = value; -+ } -+ } -+} -+ -+static void -+madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, -+ char *data, int len) -+{ -+ struct hostapd_data *hapd = drv->hapd; -+ 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); -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_VERBOSE, "Wireless event: " -+ "cmd=0x%x len=%d\n", 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 IWEVEXPIRED: -+ madwifi_del_sta(drv, iwe->u.addr.sa_data); -+ break; -+ case IWEVREGISTERED: -+ madwifi_new_sta(drv, iwe->u.addr.sa_data); -+ break; -+ case IWEVCUSTOM: -+ if (custom + iwe->u.data.length > end) -+ return; -+ buf = malloc(iwe->u.data.length + 1); -+ if (buf == NULL) -+ return; /* XXX */ -+ memcpy(buf, custom, iwe->u.data.length); -+ buf[iwe->u.data.length] = '\0'; -+ madwifi_wireless_event_wireless_custom(drv, buf); -+ free(buf); -+ break; -+ } -+ -+ pos += iwe->len; -+ } -+} -+ -+ -+static void -+madwifi_wireless_event_rtm_newlink(struct madwifi_driver_data *drv, -+ struct nlmsghdr *h, int len) -+{ -+ struct ifinfomsg *ifi; -+ int attrlen, nlmsg_len, rta_len; -+ struct rtattr * attr; -+ -+ if (len < sizeof(*ifi)) -+ return; -+ -+ ifi = NLMSG_DATA(h); -+ -+ if (ifi->ifi_index != drv->ifindex) -+ return; -+ -+ 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) { -+ madwifi_wireless_event_wireless( -+ drv, ((char *) attr) + rta_len, -+ attr->rta_len - rta_len); -+ } -+ attr = RTA_NEXT(attr, attrlen); -+ } -+} -+ -+ -+static void -+madwifi_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 madwifi_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 >= 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: -+ madwifi_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 -+madwifi_get_we_version(struct madwifi_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 = malloc(buflen); -+ if (range == NULL) -+ return -1; -+ memset(range, 0, buflen); -+ -+ memset(&iwr, 0, sizeof(iwr)); -+ strncpy(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 -+madwifi_wireless_event_init(void *priv) -+{ -+ struct madwifi_driver_data *drv = priv; -+ int s; -+ struct sockaddr_nl local; -+ -+ madwifi_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, madwifi_wireless_event_receive, drv, NULL); -+ drv->wext_sock = s; -+ -+ return 0; -+} -+ -+ -+static void -+madwifi_wireless_event_deinit(void *priv) -+{ -+ struct madwifi_driver_data *drv = priv; -+ -+ if (drv != NULL) { -+ if (drv->wext_sock < 0) -+ return; -+ eloop_unregister_read_sock(drv->wext_sock); -+ close(drv->wext_sock); -+ } -+} -+ -+ -+static int -+madwifi_send_eapol(void *priv, u8 *addr, u8 *data, size_t data_len, int encrypt) -+{ -+ struct madwifi_driver_data *drv = priv; -+ hostapd *hapd = drv->hapd; -+ unsigned char buf[3000]; -+ unsigned char *bp = buf; -+ struct l2_ethhdr *eth; -+ size_t len; -+ int status; -+ -+ /* -+ * Prepend the Etherent header. If the caller left us -+ * space at the front we could just insert it but since -+ * we don't know we copy to a local buffer. Given the frequency -+ * and size of frames this probably doesn't matter. -+ */ -+ len = data_len + sizeof(struct l2_ethhdr); -+ if (len > sizeof(buf)) { -+ bp = malloc(len); -+ if (bp == NULL) { -+ printf("EAPOL frame discarded, cannot malloc temp " -+ "buffer of size %lu!\n", (unsigned long) len); -+ return -1; -+ } -+ } -+ eth = (struct l2_ethhdr *) bp; -+ memcpy(eth->h_dest, addr, ETH_ALEN); -+ memcpy(eth->h_source, drv->hapd->own_addr, ETH_ALEN); -+ eth->h_proto = htons(ETH_P_EAPOL); -+ memcpy(eth+1, data, data_len); -+ -+ if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) -+ hostapd_hexdump("TX EAPOL", bp, len); -+ -+ status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); -+ -+ if (bp != buf) -+ free(bp); -+ return status; -+} -+ -+static void -+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) -+{ -+ struct madwifi_driver_data *drv = ctx; -+ hostapd *hapd = drv->hapd; -+ struct sta_info *sta; -+ -+ sta = ap_get_sta(hapd, src_addr); -+ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { -+ printf("Data frame from not associated STA %s\n", -+ ether_sprintf(src_addr)); -+ /* XXX cannot happen */ -+ return; -+ } -+ ieee802_1x_receive(hapd, src_addr, buf + sizeof(struct l2_ethhdr), -+ len - sizeof(struct l2_ethhdr)); -+} -+ -+static int -+madwifi_init(struct hostapd_data *hapd) -+{ -+ struct madwifi_driver_data *drv; -+ struct ifreq ifr; -+ struct iwreq iwr; -+ -+ drv = malloc(sizeof(struct madwifi_driver_data)); -+ if (drv == NULL) { -+ printf("Could not allocate memory for madwifi driver data\n"); -+ goto bad; -+ } -+ -+ memset(drv, 0, sizeof(*drv)); -+ drv->ops = madwifi_driver_ops; -+ drv->hapd = hapd; -+ drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); -+ if (drv->ioctl_sock < 0) { -+ perror("socket[PF_INET,SOCK_DGRAM]"); -+ goto bad; -+ } -+ memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface)); -+ -+ memset(&ifr, 0, sizeof(ifr)); -+ snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", drv->iface); -+ if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { -+ perror("ioctl(SIOCGIFINDEX)"); -+ goto bad; -+ } -+ drv->ifindex = ifr.ifr_ifindex; -+ -+ drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, -+ handle_read, drv, 1); -+ if (drv->sock_xmit == NULL) -+ goto bad; -+ if (l2_packet_get_own_addr(drv->sock_xmit, hapd->own_addr)) -+ goto bad; -+ if (hapd->conf->bridge[0] != '\0') { -+ HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, -+ "Configure bridge %s for EAPOL traffic.\n", -+ hapd->conf->bridge); -+ drv->sock_recv = l2_packet_init(hapd->conf->bridge, NULL, -+ ETH_P_EAPOL, handle_read, drv, -+ 0); -+ if (drv->sock_recv == NULL) -+ goto bad; -+ } else -+ drv->sock_recv = drv->sock_xmit; -+ -+ memset(&iwr, 0, sizeof(iwr)); -+ strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ); -+ -+ iwr.u.mode = IW_MODE_MASTER; -+ -+ if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { -+ perror("ioctl[SIOCSIWMODE]"); -+ printf("Could not set interface to master mode!\n"); -+ goto bad; -+ } -+ -+ madwifi_set_iface_flags(drv, 0); /* mark down during setup */ -+ -+ hapd->driver = &drv->ops; -+ return 0; -+bad: -+ if (drv->sock_xmit != NULL) -+ l2_packet_deinit(drv->sock_xmit); -+ if (drv->ioctl_sock >= 0) -+ close(drv->ioctl_sock); -+ if (drv != NULL) -+ free(drv); -+ return -1; -+} -+ -+ -+static void -+madwifi_deinit(void *priv) -+{ -+ struct madwifi_driver_data *drv = priv; -+ -+ drv->hapd->driver = NULL; -+ -+ (void) madwifi_set_iface_flags(drv, 0); -+ if (drv->ioctl_sock >= 0) -+ close(drv->ioctl_sock); -+ if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) -+ l2_packet_deinit(drv->sock_recv); -+ if (drv->sock_xmit != NULL) -+ l2_packet_deinit(drv->sock_xmit); -+ free(drv); -+} -+ -+static int -+madwifi_set_ssid(void *priv, u8 *buf, int len) -+{ -+ struct madwifi_driver_data *drv = priv; -+ struct iwreq iwr; -+ -+ memset(&iwr, 0, sizeof(iwr)); -+ strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ); -+ iwr.u.essid.flags = 1; /* SSID active */ -+ iwr.u.essid.pointer = (caddr_t) buf; -+ iwr.u.essid.length = len + 1; -+ -+ if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { -+ perror("ioctl[SIOCSIWESSID]"); -+ printf("len=%d\n", len); -+ return -1; -+ } -+ return 0; -+} -+ -+static int -+madwifi_get_ssid(void *priv, u8 *buf, int len) -+{ -+ struct madwifi_driver_data *drv = priv; -+ struct iwreq iwr; -+ int ret = 0; -+ -+ memset(&iwr, 0, sizeof(iwr)); -+ strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ); -+ iwr.u.essid.pointer = (caddr_t) buf; -+ iwr.u.essid.length = len; -+ -+ if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { -+ perror("ioctl[SIOCGIWESSID]"); -+ ret = -1; -+ } else -+ ret = iwr.u.essid.length; -+ -+ return ret; -+} -+ -+static const struct driver_ops madwifi_driver_ops = { -+ .name = "madwifi", -+ .init = madwifi_init, -+ .deinit = madwifi_deinit, -+ .set_ieee8021x = madwifi_set_ieee8021x, -+ .set_privacy = madwifi_set_privacy, -+ .set_encryption = madwifi_set_key, -+ .get_seqnum = madwifi_get_seqnum, -+ .flush = madwifi_flush, -+ .set_generic_elem = madwifi_set_opt_ie, -+ .wireless_event_init = madwifi_wireless_event_init, -+ .wireless_event_deinit = madwifi_wireless_event_deinit, -+ .set_sta_authorized = madwifi_set_sta_authorized, -+ .read_sta_data = madwifi_read_sta_driver_data, -+ .send_eapol = madwifi_send_eapol, -+ .sta_disassoc = madwifi_sta_disassoc, -+ .sta_deauth = madwifi_sta_deauth, -+ .set_ssid = madwifi_set_ssid, -+ .get_ssid = madwifi_get_ssid, -+ .sta_clear_stats = madwifi_sta_clear_stats, -+}; -+ -+void madwifi_driver_register(void) -+{ -+ driver_register(madwifi_driver_ops.name, &madwifi_driver_ops); -+}