diff options
author | rpaulo <rpaulo@FreeBSD.org> | 2010-06-14 15:37:48 +0000 |
---|---|---|
committer | rpaulo <rpaulo@FreeBSD.org> | 2010-06-14 15:37:48 +0000 |
commit | ce9c8f380b1fae326c166456a15ae504dce5c1d1 (patch) | |
tree | 14bf596282a14863f779d075d7a8f7e60c25f0a8 /contrib/wpa/src/drivers | |
parent | da0290f8d3606ed387adb04fec5d24de81a39032 (diff) | |
parent | 60c44471bf25f9e84d8701afe1bbcbcc88e18c89 (diff) | |
download | FreeBSD-src-ce9c8f380b1fae326c166456a15ae504dce5c1d1.zip FreeBSD-src-ce9c8f380b1fae326c166456a15ae504dce5c1d1.tar.gz |
MFV hostapd & wpa_supplicant 0.6.10.
Diffstat (limited to 'contrib/wpa/src/drivers')
33 files changed, 17398 insertions, 8 deletions
diff --git a/contrib/wpa/src/drivers/Apple80211.h b/contrib/wpa/src/drivers/Apple80211.h new file mode 100644 index 0000000..2a612e7 --- /dev/null +++ b/contrib/wpa/src/drivers/Apple80211.h @@ -0,0 +1,156 @@ +#ifndef APPLE80211_H +#define APPLE80211_H + +/* + * Apple80211 framework definitions + * This is an undocumented interface and the definitions here are based on + * information from MacStumbler (http://www.macstumbler.com/Apple80211.h) and + * whatever related information can be found with google and experiments ;-). + */ + +typedef struct __WirelessRef *WirelessRef; +typedef SInt32 WirelessError; +#define errWirelessNoError 0 + +typedef struct WirelessInfo { + UInt16 link_qual; + UInt16 comms_qual; + UInt16 signal; + UInt16 noise; + UInt16 port_stat; + UInt16 client_mode; + UInt16 res1; + UInt16 power; + UInt16 res2; + UInt8 bssID[6]; + UInt8 ssid[34]; +} WirelessInfo; + +typedef struct WirelessInfo2 { + /* TODO - these are probably not in correct order or complete */ + WirelessInfo info1; + UInt8 macAddress[6]; +} WirelessInfo2; + +typedef struct WirelessNetworkInfo { + UInt16 channel; + UInt16 noise; + UInt16 signal; + UInt8 bssid[6]; + UInt16 beacon_int; + UInt16 capability; + UInt16 ssid_len; + UInt8 ssid[32]; +} WirelessNetworkInfo; + +typedef int wirelessKeyType; /* TODO */ + +int WirelessIsAvailable(void); +WirelessError WirelessAttach(WirelessRef *ref, UInt32 res); +WirelessError WirelessDetach(WirelessRef ref); +WirelessError WirelessPrivate(WirelessRef ref, void *in_ptr, int in_bytes, + void *out_ptr, int out_bytes); +WirelessError WirelessSetEnabled(WirelessRef ref, UInt8 enabled); +WirelessError WirelessGetEnabled(WirelessRef ref, UInt8 *enabled); +WirelessError WirelessSetPower(WirelessRef ref, UInt8 power); +WirelessError WirelessGetPower(WirelessRef ref, UInt8 *power); +WirelessError WirelessGetInfo(WirelessRef ref, WirelessInfo *info); +WirelessError WirelessGetInfo2(WirelessRef ref, WirelessInfo2 *info); +WirelessError WirelessScan(WirelessRef ref, CFArrayRef *results, + UInt32 strip_dups); +WirelessError WirelessScanSplit(WirelessRef ref, CFArrayRef *ap_results, + CFArrayRef *ibss_results, UInt32 strip_dups); +WirelessError WirelessDirectedScan(WirelessRef ref, CFArrayRef *results, + UInt32 strip_dups, CFStringRef ssid); +WirelessError WirelessDirectedScan2(WirelessRef ref, CFDataRef ssid, + UInt32 strip_dups, CFArrayRef *results); +WirelessError WirelessJoin(WirelessRef ref, CFStringRef ssid); +WirelessError WirelessJoinWEP(WirelessRef ref, CFStringRef ssid, + CFStringRef passwd); +WirelessError WirelessJoin8021x(WirelessRef ref, CFStringRef ssid); +/* + * Set WEP key + * ref: wireless reference from WirelessAttach() + * type: ? + * key_idx: 0..3 + * key_len: 13 for WEP-104 or 0 for clearing the key + * key: Pointer to the key or %NULL if key_len = 0 + */ +WirelessError WirelessSetKey(WirelessRef ref, wirelessKeyType type, + int key_idx, int key_len, + const unsigned char *key); +/* + * Set WPA key (e.g., PMK for 4-way handshake) + * ref: wireless reference from WirelessAttach() + * type: 0..4; 1 = PMK + * key_len: 16, 32, or 0 + * key: Pointer to the key or %NULL if key_len = 0 + */ +WirelessError WirelessSetWPAKey(WirelessRef ref, wirelessKeyType type, + int key_len, const unsigned char *key); +WirelessError WirelessAssociate(WirelessRef ref, int type, CFDataRef ssid, + CFStringRef key); +WirelessError WirelessAssociate2(WirelessRef ref, CFDictionaryRef scan_res, + CFStringRef key); +WirelessError WirelessDisassociate(WirelessRef ref); + +/* + * Get a copy of scan results for the given SSID + * The returned dictionary includes following entries: + * beaconInterval: CFNumber(kCFNumberSInt32Type) + * SSID: CFData buffer of the SSID + * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 = WPA2 + * name: Name of the network (SSID string) + * BSSID: CFData buffer of the BSSID + * channel: CFNumber(kCFNumberSInt32Type) + * signal: CFNumber(kCFNumberSInt32Type) + * appleIE: CFData + * WPSNOPINRequired: CFBoolean + * noise: CFNumber(kCFNumberSInt32Type) + * capability: CFNumber(kCFNumberSInt32Type) + * uniCipher: CFArray of CFNumber(kCFNumberSInt32Type) + * appleIE_Version: CFNumber(kCFNumberSInt32Type) + * appleIE_Robust: CFBoolean + * WPSConfigured: CFBoolean + * scanWasDirected: CFBoolean + * appleIE_Product: CFNumber(kCFNumberSInt32Type) + * authModes: CFArray of CFNumber(kCFNumberSInt32Type) + * multiCipher: CFNumber(kCFNumberSInt32Type) + */ +CFDictionaryRef WirelessSafeDirectedScanCopy(WirelessRef ref, CFDataRef ssid); + +/* + * Get information about the current association + * The returned dictionary includes following entries: + * keyData: CFData buffer of the key (e.g., 32-octet PSK) + * multiCipher: CFNumber(kCFNumberSInt32Type); 0 = none, 5 = CCMP? + * channel: CFNumber(kCFNumberSInt32Type) + * isIBSS: CFBoolean + * authMode: CFNumber(kCFNumberSInt32Type); 2 = WPA-Personal; 3 = open, + * 129 = WPA2-Enterprise + * isWPA: CFNumber(kCFNumberSInt32Type); 0 = not used, 1 = WPA, -128 == WPA2 + * SSID: CFData buffer of the SSID + * cipherMode: CFNumber(kCFNumberSInt32Type); 0 = none, 4 = CCMP? + */ +CFDictionaryRef WirelessGetAssociationInfo(WirelessRef ref); + +WirelessError WirelessConfigure(WirelessRef ref); + +/* + * Get ASP information + * The returned dictionary includes following entries: + * Version: version number (e.g., 3.0) + * Channel: channel (e.g., 1) + * Vendor: vendor (e.g., 2) + */ +CFDictionaryRef WirelessGetInfoASP(void); + +/* + * Get a copy of the interface dictionary + * The returned dictionary has a key,value pairs for wireless interfaces. + * The key is the interface name and the value is the driver identifier, e.g., + * en1: com.apple.driver.AirPort.Atheros + */ +CFDictionaryRef WirelessCopyInterfaceDict(void); + +#endif /* APPLE80211_H */ diff --git a/contrib/wpa/src/drivers/Makefile b/contrib/wpa/src/drivers/Makefile new file mode 100644 index 0000000..cffba62 --- /dev/null +++ b/contrib/wpa/src/drivers/Makefile @@ -0,0 +1,9 @@ +all: + @echo Nothing to be made. + +clean: + for d in $(SUBDIRS); do make -C $$d clean; done + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/wpa/src/drivers/MobileApple80211.c b/contrib/wpa/src/drivers/MobileApple80211.c new file mode 100644 index 0000000..ce004fe --- /dev/null +++ b/contrib/wpa/src/drivers/MobileApple80211.c @@ -0,0 +1,189 @@ +#include "includes.h" +#include <dlfcn.h> + +#include "common.h" + +#include <CoreFoundation/CoreFoundation.h> +#include "MobileApple80211.h" + +/* + * Code for dynamically loading Apple80211 functions from Aeropuerto to avoid + * having to link with full Preferences.framework. + */ + +static void *aeropuerto = NULL; + + +int _Apple80211Initialized(void) +{ + return aeropuerto ? 1 : 0; +} + + +static int (*__Apple80211Open)(Apple80211Ref *ctx) = NULL; + +int Apple80211Open(Apple80211Ref *ctx) +{ + return __Apple80211Open(ctx); +} + + +static int (*__Apple80211Close)(Apple80211Ref ctx) = NULL; + +int Apple80211Close(Apple80211Ref ctx) +{ + return __Apple80211Close(ctx); +} + + +static int (*__Apple80211GetIfListCopy)(Apple80211Ref handle, CFArrayRef *list) + = NULL; + +int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list) +{ + return __Apple80211GetIfListCopy(handle, list); +} + + +static int (*__Apple80211BindToInterface)(Apple80211Ref handle, + CFStringRef interface) = NULL; + +int Apple80211BindToInterface(Apple80211Ref handle, + CFStringRef interface) +{ + return __Apple80211BindToInterface(handle, interface); +} + + +static int (*__Apple80211GetInterfaceNameCopy)(Apple80211Ref handle, + CFStringRef *name) = NULL; + +int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, + CFStringRef *name) +{ + return __Apple80211GetInterfaceNameCopy(handle, name); +} + + +static int (*__Apple80211GetInfoCopy)(Apple80211Ref handle, + CFDictionaryRef *info) = NULL; + +int Apple80211GetInfoCopy(Apple80211Ref handle, + CFDictionaryRef *info) +{ + return __Apple80211GetInfoCopy(handle, info); +} + + +static int (*__Apple80211GetPower)(Apple80211Ref handle, char *pwr) = NULL; + +int Apple80211GetPower(Apple80211Ref handle, char *pwr) +{ + return __Apple80211GetPower(handle, pwr); +} + + +static int (*__Apple80211SetPower)(Apple80211Ref handle, char pwr) = NULL; + +int Apple80211SetPower(Apple80211Ref handle, char pwr) +{ + return __Apple80211SetPower(handle, pwr); +} + + +static int (*__Apple80211Scan)(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters) = NULL; + +int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters) +{ + return __Apple80211Scan(handle, list, parameters); +} + + +static int (*__Apple80211Associate)(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password) = NULL; + +int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password) +{ + return __Apple80211Associate(handle, bss, password); +} + + +static int (*__Apple80211AssociateAndCopyInfo)(Apple80211Ref handle, + CFDictionaryRef bss, + CFStringRef password, + CFDictionaryRef *info) = + NULL; + +int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password, CFDictionaryRef *info) +{ + return __Apple80211AssociateAndCopyInfo(handle, bss, password, info); +} + + +static int (*__Apple80211CopyValue)(Apple80211Ref handle, int field, + CFDictionaryRef arg2, void *value) = NULL; + +int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, + void *value) +{ + return __Apple80211CopyValue(handle, field, arg2, value); +} + + +#define DLSYM(s) \ +do { \ + __ ## s = dlsym(aeropuerto, #s); \ + if (__ ## s == NULL) { \ + wpa_printf(MSG_ERROR, "MobileApple80211: Could not resolve " \ + "symbol '" #s "' (%s)", dlerror()); \ + err = 1; \ + } \ +} while (0) + + +__attribute__ ((constructor)) +void _Apple80211_constructor(void) +{ + const char *fname = "/System/Library/SystemConfiguration/" + "Aeropuerto.bundle/Aeropuerto"; + int err = 0; + + aeropuerto = dlopen(fname, RTLD_LAZY); + if (!aeropuerto) { + wpa_printf(MSG_ERROR, "MobileApple80211: Failed to open %s " + "for symbols", fname); + return; + } + + DLSYM(Apple80211Open); + DLSYM(Apple80211Close); + DLSYM(Apple80211GetIfListCopy); + DLSYM(Apple80211BindToInterface); + DLSYM(Apple80211GetInterfaceNameCopy); + DLSYM(Apple80211GetInfoCopy); + DLSYM(Apple80211GetPower); + DLSYM(Apple80211SetPower); + DLSYM(Apple80211Scan); + DLSYM(Apple80211Associate); + DLSYM(Apple80211AssociateAndCopyInfo); + DLSYM(Apple80211CopyValue); + + if (err) { + dlclose(aeropuerto); + aeropuerto = NULL; + } +} + + +__attribute__ ((destructor)) +void _Apple80211_destructor(void) +{ + if (aeropuerto) { + dlclose(aeropuerto); + aeropuerto = NULL; + } +} diff --git a/contrib/wpa/src/drivers/MobileApple80211.h b/contrib/wpa/src/drivers/MobileApple80211.h new file mode 100644 index 0000000..64d439d --- /dev/null +++ b/contrib/wpa/src/drivers/MobileApple80211.h @@ -0,0 +1,43 @@ +#ifndef MOBILEAPPLE80211_H +#define MOBILEAPPLE80211_H + +/* + * MobileApple80211 interface for iPhone/iPod touch + * These functions are available from Aeropuerto. + */ + +struct Apple80211; +typedef struct Apple80211 *Apple80211Ref; + +int Apple80211Open(Apple80211Ref *ctx); +int Apple80211Close(Apple80211Ref ctx); +int Apple80211GetIfListCopy(Apple80211Ref handle, CFArrayRef *list); +int Apple80211BindToInterface(Apple80211Ref handle, + CFStringRef interface); +int Apple80211GetInterfaceNameCopy(Apple80211Ref handle, + CFStringRef *name); +int Apple80211GetInfoCopy(Apple80211Ref handle, + CFDictionaryRef *info); +int Apple80211GetPower(Apple80211Ref handle, char *pwr); +int Apple80211SetPower(Apple80211Ref handle, char pwr); + +/* parameters can be NULL; returns scan results in CFArrayRef *list; + * caller will need to free with CFRelease() */ +int Apple80211Scan(Apple80211Ref handle, CFArrayRef *list, + CFDictionaryRef parameters); + +int Apple80211Associate(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password); +int Apple80211AssociateAndCopyInfo(Apple80211Ref handle, CFDictionaryRef bss, + CFStringRef password, + CFDictionaryRef *info); + +enum { + APPLE80211_VALUE_SSID = 1, + APPLE80211_VALUE_BSSID = 9 +}; + +int Apple80211CopyValue(Apple80211Ref handle, int field, CFDictionaryRef arg2, + void *value); + +#endif /* MOBILEAPPLE80211_H */ diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h index 9bce6c6..c2975d2 100644 --- a/contrib/wpa/src/drivers/driver.h +++ b/contrib/wpa/src/drivers/driver.h @@ -131,7 +131,7 @@ struct wpa_scan_results { * @ifname: Interface name that can be used with init() or init2() * @desc: Human readable adapter description (e.g., vendor/model) or NULL if * not available - * @drv_bame: struct wpa_driver_ops::name (note: unlike other strings, this one + * @drv_name: struct wpa_driver_ops::name (note: unlike other strings, this one * is not an allocated copy, i.e., get_interfaces() caller will not free * this) */ @@ -702,7 +702,7 @@ struct wpa_driver_ops { int (*flush_pmkid)(void *priv); /** - * flush_pmkid - Flush PMKSA cache + * get_capa - Get driver capabilities * @priv: private driver interface data * * Returns: 0 on success, -1 on failure diff --git a/contrib/wpa/src/drivers/driver_atmel.c b/contrib/wpa/src/drivers/driver_atmel.c new file mode 100644 index 0000000..0a7a66d --- /dev/null +++ b/contrib/wpa/src/drivers/driver_atmel.c @@ -0,0 +1,506 @@ +/* + * WPA Supplicant - Driver interaction with Atmel Wireless LAN drivers + * Copyright (c) 2000-2005, ATMEL Corporation + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * 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. + */ + +/****************************************************************************** + Copyright 2000-2001 ATMEL Corporation. + + WPA Supplicant - driver interaction with Atmel Wireless lan drivers. + + This is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Atmel wireless lan drivers; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +******************************************************************************/ + +/* + * Alternatively, this software may be distributed under the terms of BSD + * license. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_atmel_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + + +#define ATMEL_WPA_IOCTL (SIOCIWFIRSTPRIV + 2) +#define ATMEL_WPA_IOCTL_PARAM (SIOCIWFIRSTPRIV + 3) +#define ATMEL_WPA_IOCTL_GET_PARAM (SIOCIWFIRSTPRIV + 4) + + +/* ATMEL_WPA_IOCTL ioctl() cmd: */ +enum { + SET_WPA_ENCRYPTION = 1, + SET_CIPHER_SUITES = 2, + MLME_STA_DEAUTH = 3, + MLME_STA_DISASSOC = 4 +}; + +/* ATMEL_WPA_IOCTL_PARAM ioctl() cmd: */ +enum { + ATMEL_PARAM_WPA = 1, + ATMEL_PARAM_PRIVACY_INVOKED = 2, + ATMEL_PARAM_WPA_TYPE = 3 +}; + +#define MAX_KEY_LENGTH 40 + +struct atmel_param{ + unsigned char sta_addr[6]; + int cmd; + u8 alg; + u8 key_idx; + u8 set_tx; + u8 seq[8]; + u8 seq_len; + u16 key_len; + u8 key[MAX_KEY_LENGTH]; + struct{ + int reason_code; + u8 state; + }mlme; + u8 pairwise_suite; + u8 group_suite; + u8 key_mgmt_suite; +}; + + + +static int atmel_ioctl(struct wpa_driver_atmel_data *drv, + struct atmel_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, ATMEL_WPA_IOCTL, &iwr) < 0) { + int ret; + ret = errno; + if (show_err) + perror("ioctl[ATMEL_WPA_IOCTL]"); + return ret; + } + + return 0; +} + + +static int atmel2param(struct wpa_driver_atmel_data *drv, int param, int value) +{ + struct iwreq iwr; + int *i, ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->sock, ATMEL_WPA_IOCTL_PARAM, &iwr) < 0) { + perror("ioctl[ATMEL_WPA_IOCTL_PARAM]"); + ret = -1; + } + return ret; +} + + +#if 0 +static int wpa_driver_atmel_set_wpa_ie(struct wpa_driver_atmel_data *drv, + const char *wpa_ie, size_t wpa_ie_len) +{ + struct atmel_param *param; + int res; + size_t blen = ATMEL_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = ATMEL_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = atmel_ioctl(drv, param, blen, 1); + + os_free(param); + + return res; +} +#endif + + +static int wpa_driver_atmel_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; + + printf("wpa_driver_atmel_set_wpa %s\n", drv->ifname); + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + +#if 0 + if (!enabled && wpa_driver_atmel_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; +#endif + if (atmel2param(drv, ATMEL_PARAM_PRIVACY_INVOKED, enabled) < 0) + ret = -1; + if (atmel2param(drv, ATMEL_PARAM_WPA, enabled) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_atmel_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; + struct atmel_param *param; + u8 *buf; + u8 alg_type; + + size_t blen; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + alg_type = 0; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + alg_type = 1; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + alg_type = 2; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + alg_type = 3; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct atmel_param *) buf; + + param->cmd = SET_WPA_ENCRYPTION; + + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); + + param->alg = alg_type; + param->key_idx = key_idx; + param->set_tx = set_tx; + os_memcpy(param->seq, seq, seq_len); + param->seq_len = seq_len; + param->key_len = key_len; + os_memcpy((u8 *)param->key, key, key_len); + + if (atmel_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + /* TODO: show key error*/ + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_atmel_set_countermeasures(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_atmel_set_countermeasures - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_atmel_set_drop_unencrypted(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_atmel_set_drop_unencrypted - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_atmel_mlme(void *priv, const u8 *addr, int cmd, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + struct atmel_param param; + int ret; + int mgmt_error = 0xaa; + + os_memset(¶m, 0, sizeof(param)); + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.cmd = cmd; + param.mlme.reason_code = reason_code; + param.mlme.state = mgmt_error; + ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); + return ret; +} + + +#if 0 +static int wpa_driver_atmel_set_suites(struct wpa_driver_atmel_data *drv, + u8 pairwise_suite, u8 group_suite, + u8 key_mgmt_suite) +{ + struct atmel_param param; + int ret; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = SET_CIPHER_SUITES; + param.pairwise_suite = pairwise_suite; + param.group_suite = group_suite; + param.key_mgmt_suite = key_mgmt_suite; + + ret = atmel_ioctl(drv, ¶m, sizeof(param), 1); + return ret; +} +#endif + + +static int wpa_driver_atmel_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + printf("wpa_driver_atmel_deauthenticate\n"); + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DEAUTH, + reason_code); + +} + + +static int wpa_driver_atmel_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_atmel_data *drv = priv; + printf("wpa_driver_atmel_disassociate\n"); + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_atmel_mlme(drv, addr, MLME_STA_DISASSOC, + reason_code); + +} + + +#if 0 +/* Atmel driver uses specific values for each cipher suite */ +static int convertSuiteToDriver(wpa_cipher suite) +{ + u8 suite_type; + + switch(suite) { + case CIPHER_NONE: + suite_type = 0; + break; + case CIPHER_WEP40: + suite_type = 1; + break; + case CIPHER_TKIP: + suite_type = 2; + break; + case CIPHER_WEP104: + suite_type = 5; + break; + case CIPHER_CCMP: + suite_type = 3; + break; + default: + suite_type = 2; + } + + return suite_type; + +} +#endif + +static int +wpa_driver_atmel_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_atmel_data *drv = priv; + int ret = 0; +#if 0 + u8 pairwise_suite_driver; + u8 group_suite_driver; + u8 key_mgmt_suite_driver; + + pairwise_suite_driver = convertSuiteToDriver(params->pairwise_suite); + group_suite_driver = convertSuiteToDriver(params->group_suite); + key_mgmt_suite_driver = convertSuiteToDriver(params->key_mgmt_suite); + + if (wpa_driver_atmel_set_suites(drv, pairwise_suite_driver, + group_suite_driver, + key_mgmt_suite_driver) < 0){ + printf("wpa_driver_atmel_set_suites.\n"); + ret = -1; + } + if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) { + printf("wpa_driver_atmel_set_freq.\n"); + ret = -1; + } +#endif + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) + < 0) { + printf("FAILED : wpa_driver_atmel_set_ssid.\n"); + ret = -1; + } + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) { + printf("FAILED : wpa_driver_atmel_set_bssid.\n"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_atmel_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_atmel_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_atmel_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_driver_atmel_get_scan_results(void *priv) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_atmel_set_operstate(void *priv, int state) +{ + struct wpa_driver_atmel_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_atmel_init(void *ctx, const char *ifname) +{ + struct wpa_driver_atmel_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_atmel_deinit(void *priv) +{ + struct wpa_driver_atmel_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_atmel_ops = { + .name = "atmel", + .desc = "ATMEL AT76C5XXx (USB, PCMCIA)", + .get_bssid = wpa_driver_atmel_get_bssid, + .get_ssid = wpa_driver_atmel_get_ssid, + .set_wpa = wpa_driver_atmel_set_wpa, + .set_key = wpa_driver_atmel_set_key, + .init = wpa_driver_atmel_init, + .deinit = wpa_driver_atmel_deinit, + .set_countermeasures = wpa_driver_atmel_set_countermeasures, + .set_drop_unencrypted = wpa_driver_atmel_set_drop_unencrypted, + .scan = wpa_driver_atmel_scan, + .get_scan_results2 = wpa_driver_atmel_get_scan_results, + .deauthenticate = wpa_driver_atmel_deauthenticate, + .disassociate = wpa_driver_atmel_disassociate, + .associate = wpa_driver_atmel_associate, + .set_operstate = wpa_driver_atmel_set_operstate, +}; diff --git a/contrib/wpa/src/drivers/driver_broadcom.c b/contrib/wpa/src/drivers/driver_broadcom.c new file mode 100644 index 0000000..3600dae --- /dev/null +++ b/contrib/wpa/src/drivers/driver_broadcom.c @@ -0,0 +1,604 @@ +/* + * WPA Supplicant - driver interaction with old Broadcom wl.o driver + * Copyright (c) 2004, Nikki Chumkov <nikki@gattaca.ru> + * Copyright (c) 2004, Jouni Malinen <j@w1.fi> + * + * 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. + * + * Please note that the newer Broadcom driver ("hybrid Linux driver") supports + * Linux wireless extensions and does not need (or even work) with this old + * driver wrapper. Use driver_wext.c with that driver. + */ + +#include "includes.h" + +#include <sys/ioctl.h> + +#include "common.h" + +#if 0 +#include <netpacket/packet.h> +#include <net/ethernet.h> /* the L2 protocols */ +#else +#include <linux/if_packet.h> +#include <linux/if_ether.h> /* The L2 protocols */ +#endif +#include <net/if.h> +#include <typedefs.h> + +/* wlioctl.h is a Broadcom header file and it is available, e.g., from Linksys + * WRT54G GPL tarball. */ +#include <wlioctl.h> + +#include "driver.h" +#include "eloop.h" + +struct wpa_driver_broadcom_data { + void *ctx; + int ioctl_sock; + int event_sock; + char ifname[IFNAMSIZ + 1]; +}; + + +#ifndef WLC_DEAUTHENTICATE +#define WLC_DEAUTHENTICATE 143 +#endif +#ifndef WLC_DEAUTHENTICATE_WITH_REASON +#define WLC_DEAUTHENTICATE_WITH_REASON 201 +#endif +#ifndef WLC_SET_TKIP_COUNTERMEASURES +#define WLC_SET_TKIP_COUNTERMEASURES 202 +#endif + +#if !defined(PSK_ENABLED) /* NEW driver interface */ +#define WL_VERSION 360130 +/* wireless authentication bit vector */ +#define WPA_ENABLED 1 +#define PSK_ENABLED 2 + +#define WAUTH_WPA_ENABLED(wauth) ((wauth) & WPA_ENABLED) +#define WAUTH_PSK_ENABLED(wauth) ((wauth) & PSK_ENABLED) +#define WAUTH_ENABLED(wauth) ((wauth) & (WPA_ENABLED | PSK_ENABLED)) + +#define WSEC_PRIMARY_KEY WL_PRIMARY_KEY + +typedef wl_wsec_key_t wsec_key_t; +#endif + +typedef struct { + uint32 val; + struct ether_addr ea; + uint16 res; +} wlc_deauth_t; + + +static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, + void *timeout_ctx); + +static int broadcom_ioctl(struct wpa_driver_broadcom_data *drv, int cmd, + void *buf, int len) +{ + struct ifreq ifr; + wl_ioctl_t ioc; + int ret = 0; + + wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl(%s,%d,len=%d,val=%p)", + drv->ifname, cmd, len, buf); + /* wpa_hexdump(MSG_MSGDUMP, "BROADCOM: wlioctl buf", buf, len); */ + + ioc.cmd = cmd; + ioc.buf = buf; + ioc.len = len; + os_strlcpy(ifr.ifr_name, drv->ifname, IFNAMSIZ); + ifr.ifr_data = (caddr_t) &ioc; + if ((ret = ioctl(drv->ioctl_sock, SIOCDEVPRIVATE, &ifr)) < 0) { + if (cmd != WLC_GET_MAGIC) + perror(ifr.ifr_name); + wpa_printf(MSG_MSGDUMP, "BROADCOM: wlioctl cmd=%d res=%d", + cmd, ret); + } + + return ret; +} + +static int wpa_driver_broadcom_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_broadcom_data *drv = priv; + if (broadcom_ioctl(drv, WLC_GET_BSSID, bssid, ETH_ALEN) == 0) + return 0; + + os_memset(bssid, 0, ETH_ALEN); + return -1; +} + +static int wpa_driver_broadcom_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t s; + + if (broadcom_ioctl(drv, WLC_GET_SSID, &s, sizeof(s)) == -1) + return -1; + + os_memcpy(ssid, s.SSID, s.SSID_len); + return s.SSID_len; +} + +static int wpa_driver_broadcom_set_wpa(void *priv, int enable) +{ + struct wpa_driver_broadcom_data *drv = priv; + unsigned int wauth, wsec; + struct ether_addr ea; + + os_memset(&ea, enable ? 0xff : 0, sizeof(ea)); + if (broadcom_ioctl(drv, WLC_GET_WPA_AUTH, &wauth, sizeof(wauth)) == + -1 || + broadcom_ioctl(drv, WLC_GET_WSEC, &wsec, sizeof(wsec)) == -1) + return -1; + + if (enable) { + wauth = PSK_ENABLED; + wsec = TKIP_ENABLED; + } else { + wauth = 255; + wsec &= ~(TKIP_ENABLED | AES_ENABLED); + } + + if (broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wauth, sizeof(wauth)) == + -1 || + broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) == -1) + return -1; + + /* FIX: magic number / error handling? */ + broadcom_ioctl(drv, 122, &ea, sizeof(ea)); + + return 0; +} + +static int wpa_driver_broadcom_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_broadcom_data *drv = priv; + int ret; + wsec_key_t wkt; + + os_memset(&wkt, 0, sizeof wkt); + wpa_printf(MSG_MSGDUMP, "BROADCOM: SET %sKEY[%d] alg=%d", + set_tx ? "PRIMARY " : "", key_idx, alg); + if (key && key_len > 0) + wpa_hexdump_key(MSG_MSGDUMP, "BROADCOM: key", key, key_len); + + switch (alg) { + case WPA_ALG_NONE: + wkt.algo = CRYPTO_ALGO_OFF; + break; + case WPA_ALG_WEP: + wkt.algo = CRYPTO_ALGO_WEP128; /* CRYPTO_ALGO_WEP1? */ + break; + case WPA_ALG_TKIP: + wkt.algo = 0; /* CRYPTO_ALGO_TKIP? */ + break; + case WPA_ALG_CCMP: + wkt.algo = 0; /* CRYPTO_ALGO_AES_CCM; + * AES_OCB_MSDU, AES_OCB_MPDU? */ + break; + default: + wkt.algo = CRYPTO_ALGO_NALG; + break; + } + + if (seq && seq_len > 0) + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: SEQ", seq, seq_len); + + if (addr) + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: addr", addr, ETH_ALEN); + + wkt.index = key_idx; + wkt.len = key_len; + if (key && key_len > 0) { + os_memcpy(wkt.data, key, key_len); + if (key_len == 32) { + /* hack hack hack XXX */ + os_memcpy(&wkt.data[16], &key[24], 8); + os_memcpy(&wkt.data[24], &key[16], 8); + } + } + /* wkt.algo = CRYPTO_ALGO_...; */ + wkt.flags = set_tx ? 0 : WSEC_PRIMARY_KEY; + if (addr && set_tx) + os_memcpy(&wkt.ea, addr, sizeof(wkt.ea)); + ret = broadcom_ioctl(drv, WLC_SET_KEY, &wkt, sizeof(wkt)); + if (addr && set_tx) { + /* FIX: magic number / error handling? */ + broadcom_ioctl(drv, 121, &wkt.ea, sizeof(wkt.ea)); + } + return ret; +} + + +static void wpa_driver_broadcom_event_receive(int sock, void *ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + wl_wpa_header_t *wwh; + union wpa_event_data data; + + if ((left = recv(sock, buf, sizeof buf, 0)) < 0) + return; + + wpa_hexdump(MSG_DEBUG, "RECEIVE EVENT", (u8 *) buf, left); + + if ((size_t) left < sizeof(wl_wpa_header_t)) + return; + + wwh = (wl_wpa_header_t *) buf; + + if (wwh->snap.type != WL_WPA_ETHER_TYPE) + return; + if (os_memcmp(&wwh->snap, wl_wpa_snap_template, 6) != 0) + return; + + os_memset(&data, 0, sizeof(data)); + + switch (wwh->type) { + case WLC_ASSOC_MSG: + left -= WL_WPA_HEADER_LEN; + wpa_printf(MSG_DEBUG, "BROADCOM: ASSOC MESSAGE (left: %d)", + left); + if (left > 0) { + data.assoc_info.resp_ies = os_malloc(left); + if (data.assoc_info.resp_ies == NULL) + return; + os_memcpy(data.assoc_info.resp_ies, + buf + WL_WPA_HEADER_LEN, left); + data.assoc_info.resp_ies_len = left; + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: copying %d bytes " + "into resp_ies", + data.assoc_info.resp_ies, left); + } + /* data.assoc_info.req_ies = NULL; */ + /* data.assoc_info.req_ies_len = 0; */ + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + break; + case WLC_DISASSOC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: DISASSOC MESSAGE"); + wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + break; + case WLC_PTK_MIC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: PTK MIC MSG MESSAGE"); + data.michael_mic_failure.unicast = 1; + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + break; + case WLC_GTK_MIC_MSG: + wpa_printf(MSG_DEBUG, "BROADCOM: GTK MIC MSG MESSAGE"); + data.michael_mic_failure.unicast = 0; + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + break; + default: + wpa_printf(MSG_DEBUG, "BROADCOM: UNKNOWN MESSAGE (%d)", + wwh->type); + break; + } + os_free(data.assoc_info.resp_ies); +} + +static void * wpa_driver_broadcom_init(void *ctx, const char *ifname) +{ + int s; + struct sockaddr_ll ll; + struct wpa_driver_broadcom_data *drv; + struct ifreq ifr; + + /* open socket to kernel */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + return NULL; + } + /* do it */ + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror(ifr.ifr_name); + return NULL; + } + + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ioctl_sock = s; + + s = socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2)); + if (s < 0) { + perror("socket(PF_PACKET, SOCK_RAW, ntohs(ETH_P_802_2))"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_memset(&ll, 0, sizeof(ll)); + ll.sll_family = AF_PACKET; + ll.sll_protocol = ntohs(ETH_P_802_2); + ll.sll_ifindex = ifr.ifr_ifindex; + ll.sll_hatype = 0; + ll.sll_pkttype = PACKET_HOST; + ll.sll_halen = 0; + + if (bind(s, (struct sockaddr *) &ll, sizeof(ll)) < 0) { + perror("bind(netlink)"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_broadcom_event_receive, ctx, + NULL); + drv->event_sock = s; + + return drv; +} + +static void wpa_driver_broadcom_deinit(void *priv) +{ + struct wpa_driver_broadcom_data *drv = priv; + eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); + eloop_unregister_read_sock(drv->event_sock); + close(drv->event_sock); + close(drv->ioctl_sock); + os_free(drv); +} + +static int wpa_driver_broadcom_set_countermeasures(void *priv, + int enabled) +{ +#if 0 + struct wpa_driver_broadcom_data *drv = priv; + /* FIX: ? */ + return broadcom_ioctl(drv, WLC_SET_TKIP_COUNTERMEASURES, &enabled, + sizeof(enabled)); +#else + return 0; +#endif +} + +static int wpa_driver_broadcom_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_broadcom_data *drv = priv; + /* SET_EAP_RESTRICT, SET_WEP_RESTRICT */ + int restrict = (enabled ? 1 : 0); + + if (broadcom_ioctl(drv, WLC_SET_WEP_RESTRICT, + &restrict, sizeof(restrict)) < 0 || + broadcom_ioctl(drv, WLC_SET_EAP_RESTRICT, + &restrict, sizeof(restrict)) < 0) + return -1; + + return 0; +} + +static void wpa_driver_broadcom_scan_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + +static int wpa_driver_broadcom_scan(void *priv, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t wst = { 0, "" }; + + if (ssid && ssid_len > 0 && ssid_len <= sizeof(wst.SSID)) { + wst.SSID_len = ssid_len; + os_memcpy(wst.SSID, ssid, ssid_len); + } + + if (broadcom_ioctl(drv, WLC_SCAN, &wst, sizeof(wst)) < 0) + return -1; + + eloop_cancel_timeout(wpa_driver_broadcom_scan_timeout, drv, drv->ctx); + eloop_register_timeout(3, 0, wpa_driver_broadcom_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static const int frequency_list[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 +}; + +struct bss_ie_hdr { + u8 elem_id; + u8 len; + u8 oui[3]; + /* u8 oui_type; */ + /* u16 version; */ +} __attribute__ ((packed)); + +static int +wpa_driver_broadcom_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_broadcom_data *drv = priv; + char *buf; + wl_scan_results_t *wsr; + wl_bss_info_t *wbi; + size_t ap_num; + + buf = os_malloc(WLC_IOCTL_MAXLEN); + if (buf == NULL) + return -1; + + wsr = (wl_scan_results_t *) buf; + + wsr->buflen = WLC_IOCTL_MAXLEN - sizeof(wsr); + wsr->version = 107; + wsr->count = 0; + + if (broadcom_ioctl(drv, WLC_SCAN_RESULTS, buf, WLC_IOCTL_MAXLEN) < 0) { + os_free(buf); + return -1; + } + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + for (ap_num = 0, wbi = wsr->bss_info; ap_num < wsr->count; ++ap_num) { + int left; + struct bss_ie_hdr *ie; + + os_memcpy(results[ap_num].bssid, &wbi->BSSID, ETH_ALEN); + os_memcpy(results[ap_num].ssid, wbi->SSID, wbi->SSID_len); + results[ap_num].ssid_len = wbi->SSID_len; + results[ap_num].freq = frequency_list[wbi->channel - 1]; + /* get ie's */ + wpa_hexdump(MSG_MSGDUMP, "BROADCOM: AP IEs", + (u8 *) wbi + sizeof(*wbi), wbi->ie_length); + ie = (struct bss_ie_hdr *) ((u8 *) wbi + sizeof(*wbi)); + for (left = wbi->ie_length; left > 0; + left -= (ie->len + 2), ie = (struct bss_ie_hdr *) + ((u8 *) ie + 2 + ie->len)) { + wpa_printf(MSG_MSGDUMP, "BROADCOM: IE: id:%x, len:%d", + ie->elem_id, ie->len); + if (ie->len >= 3) + wpa_printf(MSG_MSGDUMP, + "BROADCOM: oui:%02x%02x%02x", + ie->oui[0], ie->oui[1], ie->oui[2]); + if (ie->elem_id != 0xdd || + ie->len < 6 || + os_memcmp(ie->oui, WPA_OUI, 3) != 0) + continue; + os_memcpy(results[ap_num].wpa_ie, ie, ie->len + 2); + results[ap_num].wpa_ie_len = ie->len + 2; + break; + } + + wbi = (wl_bss_info_t *) ((u8 *) wbi + wbi->length); + } + + wpa_printf(MSG_MSGDUMP, "Received %d bytes of scan results (%lu " + "BSSes)", + wsr->buflen, (unsigned long) ap_num); + + os_free(buf); + return ap_num; +} + +static int wpa_driver_broadcom_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_deauth_t wdt; + wdt.val = reason_code; + os_memcpy(&wdt.ea, addr, sizeof wdt.ea); + wdt.res = 0x7fff; + return broadcom_ioctl(drv, WLC_DEAUTHENTICATE_WITH_REASON, &wdt, + sizeof(wdt)); +} + +static int wpa_driver_broadcom_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_broadcom_data *drv = priv; + return broadcom_ioctl(drv, WLC_DISASSOC, 0, 0); +} + +static int +wpa_driver_broadcom_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_broadcom_data *drv = priv; + wlc_ssid_t s; + int infra = 1; + int auth = 0; + int wsec = 4; + int dummy; + int wpa_auth; + + s.SSID_len = params->ssid_len; + os_memcpy(s.SSID, params->ssid, params->ssid_len); + + switch (params->pairwise_suite) { + case CIPHER_WEP40: + case CIPHER_WEP104: + wsec = 1; + break; + + case CIPHER_TKIP: + wsec = 2; + break; + + case CIPHER_CCMP: + wsec = 4; + break; + + default: + wsec = 0; + break; + } + + switch (params->key_mgmt_suite) { + case KEY_MGMT_802_1X: + wpa_auth = 1; + break; + + case KEY_MGMT_PSK: + wpa_auth = 2; + break; + + default: + wpa_auth = 255; + break; + } + + /* printf("broadcom_associate: %u %u %u\n", pairwise_suite, + * group_suite, key_mgmt_suite); + * broadcom_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec)); + * wl join uses wlc_sec_wep here, not wlc_set_wsec */ + + if (broadcom_ioctl(drv, WLC_SET_WSEC, &wsec, sizeof(wsec)) < 0 || + broadcom_ioctl(drv, WLC_SET_WPA_AUTH, &wpa_auth, + sizeof(wpa_auth)) < 0 || + broadcom_ioctl(drv, WLC_GET_WEP, &dummy, sizeof(dummy)) < 0 || + broadcom_ioctl(drv, WLC_SET_INFRA, &infra, sizeof(infra)) < 0 || + broadcom_ioctl(drv, WLC_SET_AUTH, &auth, sizeof(auth)) < 0 || + broadcom_ioctl(drv, WLC_SET_WEP, &wsec, sizeof(wsec)) < 0 || + broadcom_ioctl(drv, WLC_SET_SSID, &s, sizeof(s)) < 0) + return -1; + + return 0; +} + +const struct wpa_driver_ops wpa_driver_broadcom_ops = { + .name = "broadcom", + .desc = "Broadcom wl.o driver", + .get_bssid = wpa_driver_broadcom_get_bssid, + .get_ssid = wpa_driver_broadcom_get_ssid, + .set_wpa = wpa_driver_broadcom_set_wpa, + .set_key = wpa_driver_broadcom_set_key, + .init = wpa_driver_broadcom_init, + .deinit = wpa_driver_broadcom_deinit, + .set_countermeasures = wpa_driver_broadcom_set_countermeasures, + .set_drop_unencrypted = wpa_driver_broadcom_set_drop_unencrypted, + .scan = wpa_driver_broadcom_scan, + .get_scan_results = wpa_driver_broadcom_get_scan_results, + .deauthenticate = wpa_driver_broadcom_deauthenticate, + .disassociate = wpa_driver_broadcom_disassociate, + .associate = wpa_driver_broadcom_associate, +}; diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c new file mode 100644 index 0000000..da57852 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_bsd.c @@ -0,0 +1,795 @@ +/* + * WPA Supplicant - driver interaction with BSD net80211 layer + * Copyright (c) 2004, Sam Leffler <sam@errno.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. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#include <net/if.h> + +#ifdef __NetBSD__ +#include <net/if_ether.h> +#define COMPAT_FREEBSD_NET80211 +#else +#include <net/ethernet.h> +#endif + +#include <net80211/_ieee80211.h> +#include <net80211/ieee80211.h> +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + +struct wpa_driver_bsd_data { + int sock; /* open socket for 802.11 ioctls */ + int route; /* routing socket for events */ + char ifname[IFNAMSIZ+1]; /* interface name */ + unsigned int ifindex; /* interface index */ + void *ctx; + int prev_roaming; /* roaming state to restore on deinit */ + int prev_privacy; /* privacy state to restore on deinit */ + int prev_wpa; /* wpa state to restore on deinit */ +}; + +static int +set80211var(struct wpa_driver_bsd_data *drv, int op, const void *arg, int arg_len) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = (void *) arg; + + if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCS80211, op %u, len %u]: %s\n", + op, arg_len, strerror(errno)); + return -1; + } + return 0; +} + +static int +get80211var(struct wpa_driver_bsd_data *drv, int op, void *arg, int arg_len) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_len = arg_len; + ireq.i_data = arg; + + if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCG80211, op %u, len %u]: %s\n", + op, arg_len, strerror(errno)); + return -1; + } + return ireq.i_len; +} + +static int +set80211param(struct wpa_driver_bsd_data *drv, int op, int arg) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + ireq.i_val = arg; + + if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCS80211, op %u, arg 0x%x]: %s\n", + op, arg, strerror(errno)); + return -1; + } + return 0; +} + +static int +get80211param(struct wpa_driver_bsd_data *drv, int op) +{ + struct ieee80211req ireq; + + os_memset(&ireq, 0, sizeof(ireq)); + os_strlcpy(ireq.i_name, drv->ifname, IFNAMSIZ); + ireq.i_type = op; + + if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) { + fprintf(stderr, "ioctl[SIOCG80211, op %u]: %s\n", + op, strerror(errno)); + return -1; + } + return ireq.i_val; +} + +static int +getifflags(struct wpa_driver_bsd_data *drv, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) { + perror("SIOCGIFFLAGS"); + return errno; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + +static int +setifflags(struct wpa_driver_bsd_data *drv, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->sock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) { + perror("SIOCSIFFLAGS"); + return errno; + } + return 0; +} + +static int +wpa_driver_bsd_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return get80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0; +} + +#if 0 +static int +wpa_driver_bsd_set_bssid(void *priv, const char *bssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return set80211var(drv, IEEE80211_IOC_BSSID, + bssid, IEEE80211_ADDR_LEN); +} +#endif + +static int +wpa_driver_bsd_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_bsd_data *drv = priv; + + return get80211var(drv, IEEE80211_IOC_SSID, + ssid, IEEE80211_NWID_LEN); +} + +static int +wpa_driver_bsd_set_ssid(void *priv, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_driver_bsd_data *drv = priv; + + return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len); +} + +static int +wpa_driver_bsd_set_wpa_ie(struct wpa_driver_bsd_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + return set80211var(drv, IEEE80211_IOC_APPIE, wpa_ie, wpa_ie_len); +} + +static int +wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy) +{ + struct wpa_driver_bsd_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d", + __FUNCTION__, wpa, privacy); + + if (!wpa && wpa_driver_bsd_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) + ret = -1; + if (set80211param(drv, IEEE80211_IOC_WPA, wpa) < 0) + ret = -1; + + return ret; +} + +static int +wpa_driver_bsd_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled); +} + +static int +wpa_driver_bsd_del_key(struct wpa_driver_bsd_data *drv, int key_idx, + const unsigned char *addr) +{ + struct ieee80211req_del_key wk; + + os_memset(&wk, 0, sizeof(wk)); + if (addr != NULL && + bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) != 0) { + struct ether_addr ea; + + os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); + wpa_printf(MSG_DEBUG, "%s: addr=%s keyidx=%d", + __func__, ether_ntoa(&ea), key_idx); + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (uint8_t) IEEE80211_KEYIX_NONE; + } else { + wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __func__, key_idx); + wk.idk_keyix = key_idx; + } + return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk)); +} + +static int +wpa_driver_bsd_set_key(void *priv, wpa_alg alg, + const unsigned char *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_key wk; + struct ether_addr ea; + char *alg_name; + u_int8_t cipher; + + if (alg == WPA_ALG_NONE) + return wpa_driver_bsd_del_key(drv, key_idx, addr); + + switch (alg) { + case WPA_ALG_WEP: + alg_name = "WEP"; + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + cipher = IEEE80211_CIPHER_AES_CCM; + break; + default: + wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", + __func__, alg); + return -1; + } + + os_memcpy(&ea, addr, IEEE80211_ADDR_LEN); + wpa_printf(MSG_DEBUG, + "%s: alg=%s addr=%s key_idx=%d set_tx=%d seq_len=%zu key_len=%zu", + __func__, alg_name, ether_ntoa(&ea), key_idx, set_tx, + seq_len, key_len); + + if (seq_len > sizeof(u_int64_t)) { + wpa_printf(MSG_DEBUG, "%s: seq_len %zu too big", + __func__, seq_len); + return -2; + } + if (key_len > sizeof(wk.ik_keydata)) { + wpa_printf(MSG_DEBUG, "%s: key length %zu too big", + __func__, key_len); + return -3; + } + + os_memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV; + if (set_tx) + wk.ik_flags |= IEEE80211_KEY_XMIT; + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + /* + * Deduce whether group/global or unicast key by checking + * the address (yech). Note also that we can only mark global + * keys default; doing this for a unicast key is an error. + */ + if (bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) == 0) { + wk.ik_flags |= IEEE80211_KEY_GROUP; + wk.ik_keyix = key_idx; + } else { + wk.ik_keyix = (key_idx == 0 ? IEEE80211_KEYIX_NONE : key_idx); + } + if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx) + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + wk.ik_keylen = key_len; + os_memcpy(&wk.ik_keyrsc, seq, seq_len); + os_memcpy(wk.ik_keydata, key, key_len); + + return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)); +} + +static int +wpa_driver_bsd_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_bsd_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(drv, IEEE80211_IOC_COUNTERMEASURES, enabled); +} + + +static int +wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_bsd_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return set80211param(drv, IEEE80211_IOC_DROPUNENCRYPTED, enabled); +} + +static int +wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __func__); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)); +} + +static int +wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_bsd_data *drv = priv; + struct ieee80211req_mlme mlme; + int privacy; + + wpa_printf(MSG_DEBUG, + "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u" + , __func__ + , params->ssid_len, params->ssid + , params->wpa_ie_len + , params->pairwise_suite + , params->group_suite + , params->key_mgmt_suite + ); + + /* XXX error handling is wrong but unclear what to do... */ + if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) + return -1; + + privacy = !(params->pairwise_suite == CIPHER_NONE && + params->group_suite == CIPHER_NONE && + params->key_mgmt_suite == KEY_MGMT_NONE && + params->wpa_ie_len == 0); + wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy); + + if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0) + return -1; + + if (params->wpa_ie_len && + set80211param(drv, IEEE80211_IOC_WPA, + params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0) + return -1; + + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_ASSOC; + if (params->ssid != NULL) + os_memcpy(mlme.im_ssid, params->ssid, params->ssid_len); + mlme.im_ssid_len = params->ssid_len; + if (params->bssid != NULL) + os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); + if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0) + return -1; + return 0; +} + +static int +wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_bsd_data *drv = priv; + int authmode; + + if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && + (auth_alg & AUTH_ALG_SHARED_KEY)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & AUTH_ALG_SHARED_KEY) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(drv, IEEE80211_IOC_AUTHMODE, authmode); +} + +static int +wpa_driver_bsd_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_bsd_data *drv = priv; + int flags; + + /* NB: interface must be marked UP to do a scan */ + if (getifflags(drv, &flags) != 0 || setifflags(drv, flags | IFF_UP) != 0) + return -1; + + /* set desired ssid before scan */ + if (wpa_driver_bsd_set_ssid(drv, ssid, ssid_len) < 0) + return -1; + + /* NB: net80211 delivers a scan complete event so no need to poll */ + return set80211param(drv, IEEE80211_IOC_SCAN_REQ, 0); +} + +#include <net/route.h> +#if __FreeBSD__ +#include <net80211/ieee80211_freebsd.h> +#endif +#if __NetBSD__ +#include <net80211/ieee80211_netbsd.h> +#endif + +static void +wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx) +{ + struct wpa_driver_bsd_data *drv = sock_ctx; + char buf[2048]; + struct if_announcemsghdr *ifan; + struct if_msghdr *ifm; + struct rt_msghdr *rtm; + union wpa_event_data event; + struct ieee80211_michael_event *mic; + int n; + + n = read(sock, buf, sizeof(buf)); + if (n < 0) { + if (errno != EINTR && errno != EAGAIN) + perror("read(PF_ROUTE)"); + return; + } + + rtm = (struct rt_msghdr *) buf; + if (rtm->rtm_version != RTM_VERSION) { + wpa_printf(MSG_DEBUG, "Routing message version %d not " + "understood\n", rtm->rtm_version); + return; + } + os_memset(&event, 0, sizeof(event)); + switch (rtm->rtm_type) { + case RTM_IFANNOUNCE: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + switch (ifan->ifan_what) { + case IFAN_DEPARTURE: + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + default: + return; + } + wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s", + event.interface_status.ifname, + ifan->ifan_what == IFAN_DEPARTURE ? + "removed" : "added"); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + break; + case RTM_IEEE80211: + ifan = (struct if_announcemsghdr *) rtm; + if (ifan->ifan_index != drv->ifindex) + break; + switch (ifan->ifan_what) { + case RTM_IEEE80211_ASSOC: + case RTM_IEEE80211_REASSOC: + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + break; + case RTM_IEEE80211_DISASSOC: + wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL); + break; + case RTM_IEEE80211_SCAN: + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case RTM_IEEE80211_REPLAY: + /* ignore */ + break; + case RTM_IEEE80211_MICHAEL: + mic = (struct ieee80211_michael_event *) &ifan[1]; + wpa_printf(MSG_DEBUG, + "Michael MIC failure wireless event: " + "keyix=%u src_addr=" MACSTR, mic->iev_keyix, + MAC2STR(mic->iev_src)); + + os_memset(&event, 0, sizeof(event)); + event.michael_mic_failure.unicast = + !IEEE80211_IS_MULTICAST(mic->iev_dst); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, + &event); + break; + } + break; + case RTM_IFINFO: + ifm = (struct if_msghdr *) rtm; + if (ifm->ifm_index != drv->ifindex) + break; + if ((rtm->rtm_flags & RTF_UP) == 0) { + strlcpy(event.interface_status.ifname, drv->ifname, + sizeof(event.interface_status.ifname)); + event.interface_status.ievent = EVENT_INTERFACE_REMOVED; + wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN", + event.interface_status.ifname); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); + } + break; + } +} + +/* Compare function for sorting scan results. Return >0 if @b is consider + * better. */ +static int +wpa_scan_result_compar(const void *a, const void *b) +{ + const struct wpa_scan_result *wa = a; + const struct wpa_scan_result *wb = b; + + /* WPA/WPA2 support preferred */ + if ((wb->wpa_ie_len || wb->rsn_ie_len) && + !(wa->wpa_ie_len || wa->rsn_ie_len)) + return 1; + if (!(wb->wpa_ie_len || wb->rsn_ie_len) && + (wa->wpa_ie_len || wa->rsn_ie_len)) + return -1; + + /* privacy support preferred */ + if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) && + (wb->caps & IEEE80211_CAPINFO_PRIVACY) == 0) + return 1; + if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) == 0 && + (wb->caps & IEEE80211_CAPINFO_PRIVACY)) + return -1; + + /* best/max rate preferred if signal level close enough XXX */ + if (wa->maxrate != wb->maxrate && abs(wb->level - wa->level) < 5) + return wb->maxrate - wa->maxrate; + + /* use freq for channel preference */ + + /* all things being equal, use signal level */ + return wb->level - wa->level; +} + +static int +getmaxrate(uint8_t rates[15], uint8_t nrates) +{ + int i, maxrate = -1; + + for (i = 0; i < nrates; i++) { + int rate = rates[i] & IEEE80211_RATE_VAL; + if (rate > maxrate) + rate = maxrate; + } + return maxrate; +} + +/* unalligned little endian access */ +#define LE_READ_4(p) \ + ((u_int32_t) \ + ((((const u_int8_t *)(p))[0] ) | \ + (((const u_int8_t *)(p))[1] << 8) | \ + (((const u_int8_t *)(p))[2] << 16) | \ + (((const u_int8_t *)(p))[3] << 24))) + +static int __inline +iswpaoui(const u_int8_t *frm) +{ + return frm[1] > 3 && LE_READ_4(frm+2) == ((WPA_OUI_TYPE<<24)|WPA_OUI); +} + +static int +wpa_driver_bsd_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ +#define min(a,b) ((a)>(b)?(b):(a)) + struct wpa_driver_bsd_data *drv = priv; + uint8_t buf[24*1024]; + uint8_t *cp, *vp; + struct ieee80211req_scan_result *sr; + struct wpa_scan_result *wsr; + int len, ielen; + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + len = get80211var(drv, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf)); + if (len < 0) + return -1; + cp = buf; + wsr = results; + while (len >= sizeof(struct ieee80211req_scan_result)) { + sr = (struct ieee80211req_scan_result *) cp; + os_memcpy(wsr->bssid, sr->isr_bssid, IEEE80211_ADDR_LEN); + wsr->ssid_len = sr->isr_ssid_len; + wsr->freq = sr->isr_freq; + wsr->noise = sr->isr_noise; + wsr->qual = sr->isr_rssi; + wsr->level = 0; /* XXX? */ + wsr->caps = sr->isr_capinfo; + wsr->maxrate = getmaxrate(sr->isr_rates, sr->isr_nrates); + vp = (u_int8_t *)(sr+1); + os_memcpy(wsr->ssid, vp, sr->isr_ssid_len); + if (sr->isr_ie_len > 0) { + vp += sr->isr_ssid_len; + ielen = sr->isr_ie_len; + while (ielen > 0) { + switch (vp[0]) { + case IEEE80211_ELEMID_VENDOR: + if (!iswpaoui(vp)) + break; + wsr->wpa_ie_len = + min(2+vp[1], SSID_MAX_WPA_IE_LEN); + os_memcpy(wsr->wpa_ie, vp, + wsr->wpa_ie_len); + break; + case IEEE80211_ELEMID_RSN: + wsr->rsn_ie_len = + min(2+vp[1], SSID_MAX_WPA_IE_LEN); + os_memcpy(wsr->rsn_ie, vp, + wsr->rsn_ie_len); + break; + } + ielen -= 2+vp[1]; + vp += 2+vp[1]; + } + } + + cp += sr->isr_len, len -= sr->isr_len; + wsr++; + } + qsort(results, wsr - results, sizeof(struct wpa_scan_result), + wpa_scan_result_compar); + + wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%d BSSes)", + len, wsr - results); + + return wsr - results; +#undef min +} + +static void * +wpa_driver_bsd_init(void *ctx, const char *ifname) +{ +#define GETPARAM(drv, param, v) \ + (((v) = get80211param(drv, param)) != -1) + struct wpa_driver_bsd_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + /* + * NB: We require the interface name be mappable to an index. + * This implies we do not support having wpa_supplicant + * wait for an interface to appear. This seems ok; that + * doesn't belong here; it's really the job of devd. + */ + drv->ifindex = if_nametoindex(ifname); + if (drv->ifindex == 0) { + wpa_printf(MSG_DEBUG, "%s: interface %s does not exist", + __func__, ifname); + goto fail1; + } + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail1; + drv->route = socket(PF_ROUTE, SOCK_RAW, 0); + if (drv->route < 0) + goto fail; + eloop_register_read_sock(drv->route, + wpa_driver_bsd_event_receive, ctx, drv); + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) { + wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) { + wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s", + __func__, strerror(errno)); + goto fail; + } + if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) { + wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s", + __func__, strerror(errno)); + goto fail; + } + if (set80211param(drv, IEEE80211_IOC_ROAMING, IEEE80211_ROAMING_MANUAL) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " + "roaming: %s", __func__, strerror(errno)); + goto fail; + } + + if (set80211param(drv, IEEE80211_IOC_WPA, 1+2) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support %s", + __func__, strerror(errno)); + goto fail; + } + + return drv; +fail: + close(drv->sock); +fail1: + os_free(drv); + return NULL; +#undef GETPARAM +} + +static void +wpa_driver_bsd_deinit(void *priv) +{ + struct wpa_driver_bsd_data *drv = priv; + int flags; + + eloop_unregister_read_sock(drv->route); + + /* NB: mark interface down */ + if (getifflags(drv, &flags) == 0) + (void) setifflags(drv, flags &~ IFF_UP); + + wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy); + if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) + wpa_printf(MSG_DEBUG, "%s: failed to restore roaming state", + __func__); + + (void) close(drv->route); /* ioctl socket */ + (void) close(drv->sock); /* event socket */ + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_bsd_ops = { + .name = "bsd", + .desc = "BSD 802.11 support (Atheros, etc.)", + .init = wpa_driver_bsd_init, + .deinit = wpa_driver_bsd_deinit, + .get_bssid = wpa_driver_bsd_get_bssid, + .get_ssid = wpa_driver_bsd_get_ssid, + .set_wpa = wpa_driver_bsd_set_wpa, + .set_key = wpa_driver_bsd_set_key, + .set_countermeasures = wpa_driver_bsd_set_countermeasures, + .set_drop_unencrypted = wpa_driver_bsd_set_drop_unencrypted, + .scan = wpa_driver_bsd_scan, + .get_scan_results = wpa_driver_bsd_get_scan_results, + .deauthenticate = wpa_driver_bsd_deauthenticate, + .disassociate = wpa_driver_bsd_disassociate, + .associate = wpa_driver_bsd_associate, + .set_auth_alg = wpa_driver_bsd_set_auth_alg, +}; diff --git a/contrib/wpa/src/drivers/driver_hostap.c b/contrib/wpa/src/drivers/driver_hostap.c new file mode 100644 index 0000000..84ef3bd --- /dev/null +++ b/contrib/wpa/src/drivers/driver_hostap.c @@ -0,0 +1,513 @@ +/* + * WPA Supplicant - driver interaction with Linux Host AP driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * 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 "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "driver_hostap.h" + + +struct wpa_driver_hostap_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; + int current_mode; /* infra/adhoc */ +}; + + +static int hostapd_ioctl(struct wpa_driver_hostap_data *drv, + struct prism2_hostapd_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[PRISM2_IOCTL_HOSTAPD]"); + return ret; + } + + return 0; +} + + +static int wpa_driver_hostap_set_wpa_ie(struct wpa_driver_hostap_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = hostapd_ioctl(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int prism2param(struct wpa_driver_hostap_data *drv, int param, + int value) +{ + struct iwreq iwr; + int *i, ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = param; + *i++ = value; + + if (ioctl(drv->sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]"); + ret = -1; + } + return ret; +} + + +static int wpa_driver_hostap_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + if (!enabled && wpa_driver_hostap_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, enabled ? 2 : 0) < 0) + ret = -1; + if (prism2param(drv, PRISM2_PARAM_WPA, enabled) < 0) + ret = -1; + + return ret; +} + + +static void show_set_key_error(struct prism2_hostapd_param *param) +{ + switch (param->u.crypt.err) { + case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " + "WEP."); + break; + case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int wpa_driver_hostap_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_hostap_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + /* TODO: In theory, STA in client mode can use five keys; four default + * keys for receiving (with keyidx 0..3) and one individual key for + * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, + * keyidx 0 is reserved for this unicast use and default keys can only + * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). + * This should be fine for more or less all cases, but for completeness + * sake, the driver could be enhanced to support the missing key. */ +#if 0 + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); +#else + os_memset(param->sta_addr, 0xff, ETH_ALEN); +#endif + os_strlcpy((char *) param->u.crypt.alg, alg_name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_hostap_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return prism2param(drv, PRISM2_PARAM_TKIP_COUNTERMEASURES, enabled); +} + + +static int wpa_driver_hostap_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return prism2param(drv, PRISM2_PARAM_DROP_UNENCRYPTED, enabled); +} + + +static int wpa_driver_hostap_reset(struct wpa_driver_hostap_data *drv, + int type) +{ + struct iwreq iwr; + int *i, ret = 0; + + wpa_printf(MSG_DEBUG, "%s: type=%d", __FUNCTION__, type); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + i = (int *) iwr.u.name; + *i++ = type; + + if (ioctl(drv->sock, PRISM2_IOCTL_RESET, &iwr) < 0) { + perror("ioctl[PRISM2_IOCTL_RESET]"); + ret = -1; + } + return ret; +} + + +static int wpa_driver_hostap_mlme(struct wpa_driver_hostap_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct prism2_hostapd_param param; + int ret; + + /* There does not seem to be a better way of deauthenticating or + * disassociating with Prism2/2.5/3 than sending the management frame + * and then resetting the Port0 to make sure both the AP and the STA + * end up in disconnected state. */ + os_memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_MLME; + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.u.mlme.cmd = cmd; + param.u.mlme.reason_code = reason_code; + ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); + if (ret == 0) { + os_sleep(0, 100000); + ret = wpa_driver_hostap_reset(drv, 2); + } + return ret; +} + + +static int wpa_driver_hostap_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DEAUTH, + reason_code); +} + + +static int wpa_driver_hostap_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_hostap_mlme(drv, addr, MLME_STA_DISASSOC, + reason_code); +} + + +static int +wpa_driver_hostap_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_hostap_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (params->mode != drv->current_mode) { + /* At the moment, Host AP driver requires host_roaming=2 for + * infrastructure mode and host_roaming=0 for adhoc. */ + if (prism2param(drv, PRISM2_PARAM_HOST_ROAMING, + params->mode == IEEE80211_MODE_IBSS ? 0 : 2) < + 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set host_roaming", + __func__); + } + drv->current_mode = params->mode; + } + + if (prism2param(drv, PRISM2_PARAM_PRIVACY_INVOKED, + params->key_mgmt_suite != KEY_MGMT_NONE) < 0) + ret = -1; + if (wpa_driver_hostap_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_mode(drv->wext, params->mode) < 0) + ret = -1; + if (params->freq && + wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, params->ssid_len) + < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (prism2param(drv, PRISM2_PARAM_IEEE_802_1X, + allow_unencrypted_eapol) < 0) { + wpa_printf(MSG_DEBUG, "hostap: Failed to configure " + "ieee_802_1x param"); + /* Ignore this error.. driver_hostap.c can also be used with + * other drivers that do not support this prism2_param. */ + } + + return ret; +} + + +static int wpa_driver_hostap_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_hostap_data *drv = priv; + struct prism2_hostapd_param param; + int ret; + + if (ssid == NULL) { + /* Use standard Linux Wireless Extensions ioctl if possible + * because some drivers using hostap code in wpa_supplicant + * might not support Host AP specific scan request (with SSID + * info). */ + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); + } + + if (ssid_len > 32) + ssid_len = 32; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = PRISM2_HOSTAPD_SCAN_REQ; + param.u.scan_req.ssid_len = ssid_len; + os_memcpy(param.u.scan_req.ssid, ssid, ssid_len); + ret = hostapd_ioctl(drv, ¶m, sizeof(param), 1); + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + eloop_register_timeout(3, 0, wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + + return ret; +} + + +static int wpa_driver_hostap_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_hostap_data *drv = priv; + int algs = 0; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= 1; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= 2; + if (auth_alg & AUTH_ALG_LEAP) + algs |= 4; + if (algs == 0) + algs = 1; /* at least one algorithm should be set */ + + return prism2param(drv, PRISM2_PARAM_AP_AUTH_ALGS, algs); +} + + +static int wpa_driver_hostap_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_hostap_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static struct wpa_scan_results * wpa_driver_hostap_get_scan_results(void *priv) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_hostap_set_operstate(void *priv, int state) +{ + struct wpa_driver_hostap_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_hostap_init(void *ctx, const char *ifname) +{ + struct wpa_driver_hostap_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + perror("socket"); + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + if (os_strncmp(ifname, "wlan", 4) == 0) { + /* + * Host AP driver may use both wlan# and wifi# interface in + * wireless events. + */ + char ifname2[IFNAMSIZ + 1]; + os_strlcpy(ifname2, ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_wext_alternative_ifindex(drv->wext, ifname2); + } + + return drv; +} + + +static void wpa_driver_hostap_deinit(void *priv) +{ + struct wpa_driver_hostap_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_hostap_ops = { + .name = "hostap", + .desc = "Host AP driver (Intersil Prism2/2.5/3)", + .get_bssid = wpa_driver_hostap_get_bssid, + .get_ssid = wpa_driver_hostap_get_ssid, + .set_wpa = wpa_driver_hostap_set_wpa, + .set_key = wpa_driver_hostap_set_key, + .set_countermeasures = wpa_driver_hostap_set_countermeasures, + .set_drop_unencrypted = wpa_driver_hostap_set_drop_unencrypted, + .scan = wpa_driver_hostap_scan, + .get_scan_results2 = wpa_driver_hostap_get_scan_results, + .deauthenticate = wpa_driver_hostap_deauthenticate, + .disassociate = wpa_driver_hostap_disassociate, + .associate = wpa_driver_hostap_associate, + .set_auth_alg = wpa_driver_hostap_set_auth_alg, + .init = wpa_driver_hostap_init, + .deinit = wpa_driver_hostap_deinit, + .set_operstate = wpa_driver_hostap_set_operstate, +}; diff --git a/contrib/wpa/src/drivers/driver_hostap.h b/contrib/wpa/src/drivers/driver_hostap.h new file mode 100644 index 0000000..a2508ed --- /dev/null +++ b/contrib/wpa/src/drivers/driver_hostap.h @@ -0,0 +1,153 @@ +/* + * WPA Supplicant - driver interaction with Linux Host AP driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * 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. + */ + +#ifndef HOSTAP_DRIVER_H +#define HOSTAP_DRIVER_H + +#define PRISM2_IOCTL_PRISM2_PARAM (SIOCIWFIRSTPRIV + 0) +#define PRISM2_IOCTL_RESET (SIOCIWFIRSTPRIV + 6) +#define PRISM2_IOCTL_HOSTAPD (SIOCDEVPRIVATE + 14) + +/* PRISM2_IOCTL_PRISM2_PARAM ioctl() subtypes: */ +enum { + /* PRISM2_PARAM_PTYPE = 1, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_TXRATECTRL = 2, + PRISM2_PARAM_BEACON_INT = 3, + PRISM2_PARAM_PSEUDO_IBSS = 4, + PRISM2_PARAM_ALC = 5, + /* PRISM2_PARAM_TXPOWER = 6, */ /* REMOVED 2003-10-22 */ + PRISM2_PARAM_DUMP = 7, + PRISM2_PARAM_OTHER_AP_POLICY = 8, + PRISM2_PARAM_AP_MAX_INACTIVITY = 9, + PRISM2_PARAM_AP_BRIDGE_PACKETS = 10, + PRISM2_PARAM_DTIM_PERIOD = 11, + PRISM2_PARAM_AP_NULLFUNC_ACK = 12, + PRISM2_PARAM_MAX_WDS = 13, + PRISM2_PARAM_AP_AUTOM_AP_WDS = 14, + PRISM2_PARAM_AP_AUTH_ALGS = 15, + PRISM2_PARAM_MONITOR_ALLOW_FCSERR = 16, + PRISM2_PARAM_HOST_ENCRYPT = 17, + PRISM2_PARAM_HOST_DECRYPT = 18, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_RX = 19, + PRISM2_PARAM_BUS_MASTER_THRESHOLD_TX = 20, + PRISM2_PARAM_HOST_ROAMING = 21, + PRISM2_PARAM_BCRX_STA_KEY = 22, + PRISM2_PARAM_IEEE_802_1X = 23, + PRISM2_PARAM_ANTSEL_TX = 24, + PRISM2_PARAM_ANTSEL_RX = 25, + PRISM2_PARAM_MONITOR_TYPE = 26, + PRISM2_PARAM_WDS_TYPE = 27, + PRISM2_PARAM_HOSTSCAN = 28, + PRISM2_PARAM_AP_SCAN = 29, + PRISM2_PARAM_ENH_SEC = 30, + PRISM2_PARAM_IO_DEBUG = 31, + PRISM2_PARAM_BASIC_RATES = 32, + PRISM2_PARAM_OPER_RATES = 33, + PRISM2_PARAM_HOSTAPD = 34, + PRISM2_PARAM_HOSTAPD_STA = 35, + PRISM2_PARAM_WPA = 36, + PRISM2_PARAM_PRIVACY_INVOKED = 37, + PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, + PRISM2_PARAM_DROP_UNENCRYPTED = 39, + PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, +}; + +/* PRISM2_IOCTL_HOSTAPD ioctl() cmd: */ +enum { + PRISM2_HOSTAPD_FLUSH = 1, + PRISM2_HOSTAPD_ADD_STA = 2, + PRISM2_HOSTAPD_REMOVE_STA = 3, + PRISM2_HOSTAPD_GET_INFO_STA = 4, + /* REMOVED: PRISM2_HOSTAPD_RESET_TXEXC_STA = 5, */ + PRISM2_SET_ENCRYPTION = 6, + PRISM2_GET_ENCRYPTION = 7, + PRISM2_HOSTAPD_SET_FLAGS_STA = 8, + PRISM2_HOSTAPD_GET_RID = 9, + PRISM2_HOSTAPD_SET_RID = 10, + PRISM2_HOSTAPD_SET_ASSOC_AP_ADDR = 11, + PRISM2_HOSTAPD_SET_GENERIC_ELEMENT = 12, + PRISM2_HOSTAPD_MLME = 13, + PRISM2_HOSTAPD_SCAN_REQ = 14, + PRISM2_HOSTAPD_STA_CLEAR_STATS = 15, +}; + +#define PRISM2_HOSTAPD_MAX_BUF_SIZE 1024 +#define PRISM2_HOSTAPD_RID_HDR_LEN \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.rid.data)) +#define PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN \ +((size_t) (&((struct prism2_hostapd_param *) 0)->u.generic_elem.data)) + +/* Maximum length for algorithm names (-1 for nul termination) used in ioctl() + */ +#define HOSTAP_CRYPT_ALG_NAME_LEN 16 + + +struct prism2_hostapd_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u16 aid; + u16 capability; + u8 tx_supp_rates; + } add_sta; + struct { + u32 inactive_sec; + } get_info_sta; + struct { + u8 alg[HOSTAP_CRYPT_ALG_NAME_LEN]; + u32 flags; + u32 err; + u8 idx; + u8 seq[8]; /* sequence counter (set: RX, get: TX) */ + u16 key_len; + u8 key[0]; + } crypt; + struct { + u32 flags_and; + u32 flags_or; + } set_flags_sta; + struct { + u16 rid; + u16 len; + u8 data[0]; + } rid; + struct { + u8 len; + u8 data[0]; + } generic_elem; + struct { +#define MLME_STA_DEAUTH 0 +#define MLME_STA_DISASSOC 1 + u16 cmd; + u16 reason_code; + } mlme; + struct { + u8 ssid_len; + u8 ssid[32]; + } scan_req; + } u; +}; + +#define HOSTAP_CRYPT_FLAG_SET_TX_KEY 0x01 +#define HOSTAP_CRYPT_FLAG_PERMANENT 0x02 + +#define HOSTAP_CRYPT_ERR_UNKNOWN_ALG 2 +#define HOSTAP_CRYPT_ERR_UNKNOWN_ADDR 3 +#define HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define HOSTAP_CRYPT_ERR_KEY_SET_FAILED 5 +#define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 + +#endif /* HOSTAP_DRIVER_H */ diff --git a/contrib/wpa/src/drivers/driver_iphone.m b/contrib/wpa/src/drivers/driver_iphone.m new file mode 100644 index 0000000..8e64ffd --- /dev/null +++ b/contrib/wpa/src/drivers/driver_iphone.m @@ -0,0 +1,466 @@ +/* + * WPA Supplicant - iPhone/iPod touch Apple80211 driver interface + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * 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" +#define Boolean __DummyBoolean +#include <CoreFoundation/CoreFoundation.h> +#undef Boolean + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#include "MobileApple80211.h" + +struct wpa_driver_iphone_data { + void *ctx; + Apple80211Ref wireless_ctx; + CFArrayRef scan_results; + int ctrl_power; +}; + + +static const void * cfdict_get_key_str(CFDictionaryRef dict, const char *key) +{ + const void *res; + CFStringRef str = CFStringCreateWithCString(kCFAllocatorDefault, key, + kCFStringEncodingMacRoman); + if (str == NULL) + return NULL; + + res = CFDictionaryGetValue(dict, str); + CFRelease(str); + return res; +} + + +static int wpa_driver_iphone_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_iphone_data *drv = priv; + CFDataRef data; + int err, len; + + err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_SSID, 0, + &data); + if (err != 0) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(SSID) " + "failed: %d", err); + return -1; + } + + len = CFDataGetLength(data); + if (len > 32) { + CFRelease(data); + return -1; + } + os_memcpy(ssid, CFDataGetBytePtr(data), len); + CFRelease(data); + + return len; +} + + +static int wpa_driver_iphone_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_iphone_data *drv = priv; + CFStringRef data; + int err; + int a1, a2, a3, a4, a5, a6; + + err = Apple80211CopyValue(drv->wireless_ctx, APPLE80211_VALUE_BSSID, 0, + &data); + if (err != 0) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211CopyValue(BSSID) " + "failed: %d", err); + return -1; + } + + sscanf(CFStringGetCStringPtr(data, kCFStringEncodingMacRoman), + "%x:%x:%x:%x:%x:%x", &a1, &a2, &a3, &a4, &a5, &a6); + bssid[0] = a1; + bssid[1] = a2; + bssid[2] = a3; + bssid[3] = a4; + bssid[4] = a5; + bssid[5] = a6; + + CFRelease(data); + + return 0; +} + + +static void wpa_driver_iphone_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_iphone_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_iphone_data *drv = priv; + int err; + + if (drv->scan_results) { + CFRelease(drv->scan_results); + drv->scan_results = NULL; + } + + err = Apple80211Scan(drv->wireless_ctx, &drv->scan_results, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Scan failed: %d", + err); + return -1; + } + + eloop_register_timeout(0, 0, wpa_driver_iphone_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static int wpa_driver_iphone_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_iphone_data *drv = priv; + size_t i, num; + + if (drv->scan_results == NULL) + return 0; + + num = CFArrayGetCount(drv->scan_results); + if (num > max_size) + num = max_size; + os_memset(results, 0, num * sizeof(struct wpa_scan_result)); + + for (i = 0; i < num; i++) { + struct wpa_scan_result *res = &results[i]; + CFDictionaryRef dict = + CFArrayGetValueAtIndex(drv->scan_results, i); + CFDataRef data; + CFStringRef str; + CFNumberRef num; + int val; + + data = cfdict_get_key_str(dict, "SSID"); + if (data) { + res->ssid_len = CFDataGetLength(data); + if (res->ssid_len > 32) + res->ssid_len = 32; + os_memcpy(res->ssid, CFDataGetBytePtr(data), + res->ssid_len); + } + + str = cfdict_get_key_str(dict, "BSSID"); + if (str) { + int a1, a2, a3, a4, a5, a6; + sscanf(CFStringGetCStringPtr( + str, kCFStringEncodingMacRoman), + "%x:%x:%x:%x:%x:%x", + &a1, &a2, &a3, &a4, &a5, &a6); + res->bssid[0] = a1; + res->bssid[1] = a2; + res->bssid[2] = a3; + res->bssid[3] = a4; + res->bssid[4] = a5; + res->bssid[5] = a6; + } + + num = cfdict_get_key_str(dict, "CAPABILITIES"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->caps = val; + } + + num = cfdict_get_key_str(dict, "CHANNEL"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->freq = 2407 + val * 5; + } + + num = cfdict_get_key_str(dict, "RSSI"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->level = val; + } + + num = cfdict_get_key_str(dict, "NOISE"); + if (num) { + if (CFNumberGetValue(num, kCFNumberSInt32Type, &val)) + res->noise = val; + } + + data = cfdict_get_key_str(dict, "IE"); + if (data) { + u8 *ptr = (u8 *) CFDataGetBytePtr(data); + int len = CFDataGetLength(data); + u8 *pos = ptr, *end = ptr + len; + + while (pos + 2 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_RSN && + pos[1] <= SSID_MAX_WPA_IE_LEN) { + os_memcpy(res->rsn_ie, pos, + 2 + pos[1]); + res->rsn_ie_len = 2 + pos[1]; + } + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] > 4 && pos[2] == 0x00 && + pos[3] == 0x50 && pos[4] == 0xf2 && + pos[5] == 0x01) { + os_memcpy(res->wpa_ie, pos, + 2 + pos[1]); + res->wpa_ie_len = 2 + pos[1]; + } + + pos = pos + 2 + pos[1]; + } + } + } + + return num; +} + + +static void wpa_driver_iphone_assoc_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_iphone_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + + if (wpa_driver_iphone_get_bssid(drv, bssid) != 0) { + eloop_register_timeout(1, 0, wpa_driver_iphone_assoc_timeout, + drv, drv->ctx); + return; + } + + wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); +} + + +static int wpa_driver_iphone_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_iphone_data *drv = priv; + int i, num, err; + size_t ssid_len; + CFDictionaryRef bss = NULL; + + /* + * TODO: Consider generating parameters instead of just using an entry + * from scan results in order to support ap_scan=2. + */ + + if (drv->scan_results == NULL) { + wpa_printf(MSG_DEBUG, "iPhone: No scan results - cannot " + "associate"); + return -1; + } + + num = CFArrayGetCount(drv->scan_results); + + for (i = 0; i < num; i++) { + CFDictionaryRef dict = + CFArrayGetValueAtIndex(drv->scan_results, i); + CFDataRef data; + + data = cfdict_get_key_str(dict, "SSID"); + if (data == NULL) + continue; + + ssid_len = CFDataGetLength(data); + if (ssid_len != params->ssid_len || + os_memcmp(CFDataGetBytePtr(data), params->ssid, ssid_len) + != 0) + continue; + + bss = dict; + break; + } + + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "iPhone: Could not find SSID from scan " + "results - cannot associate"); + return -1; + } + + wpa_printf(MSG_DEBUG, "iPhone: Trying to associate with a BSS found " + "from scan results"); + + err = Apple80211Associate(drv->wireless_ctx, bss, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Associate() failed: " + "%d", err); + return -1; + } + + /* + * Driver is actually already associated; report association from an + * eloop callback. + */ + eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); + eloop_register_timeout(0, 0, wpa_driver_iphone_assoc_timeout, drv, + drv->ctx); + + return 0; +} + + +static int wpa_driver_iphone_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, + size_t key_len) +{ + /* + * TODO: Need to either support configuring PMK for 4-way handshake or + * PTK for TKIP/CCMP. + */ + return -1; +} + + +static int wpa_driver_iphone_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + return 0; +} + + +static void * wpa_driver_iphone_init(void *ctx, const char *ifname) +{ + struct wpa_driver_iphone_data *drv; + int err; + char power; + CFStringRef name; + CFDictionaryRef dict; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + err = Apple80211Open(&drv->wireless_ctx); + if (err) { + wpa_printf(MSG_ERROR, "iPhone: Apple80211Open failed: %d", + err); + os_free(drv); + return NULL; + } + + name = CFStringCreateWithCString(kCFAllocatorDefault, ifname, + kCFStringEncodingISOLatin1); + if (name == NULL) { + wpa_printf(MSG_ERROR, "iPhone: ifname -> CFString failed"); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + + err = Apple80211BindToInterface(drv->wireless_ctx, name); + CFRelease(name); + + if (err) { + wpa_printf(MSG_ERROR, "iPhone: Apple80211BindToInterface " + "failed: %d", err); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + + err = Apple80211GetPower(drv->wireless_ctx, &power); + if (err) + wpa_printf(MSG_DEBUG, "iPhone: Apple80211GetPower failed: %d", + err); + + wpa_printf(MSG_DEBUG, "iPhone: Power=%d", power); + + if (!power) { + drv->ctrl_power = 1; + err = Apple80211SetPower(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower " + "failed: %d", err); + Apple80211Close(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + err = Apple80211GetInfoCopy(drv->wireless_ctx, &dict); + if (err == 0) { + CFShow(dict); + CFRelease(dict); + } else { + printf("Apple80211GetInfoCopy: %d\n", err); + } + + return drv; +} + + +static void wpa_driver_iphone_deinit(void *priv) +{ + struct wpa_driver_iphone_data *drv = priv; + int err; + + eloop_cancel_timeout(wpa_driver_iphone_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_iphone_assoc_timeout, drv, drv->ctx); + + if (drv->ctrl_power) { + wpa_printf(MSG_DEBUG, "iPhone: Power down the interface"); + err = Apple80211SetPower(drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211SetPower(0) " + "failed: %d", err); + } + } + + err = Apple80211Close(drv->wireless_ctx); + if (err) { + wpa_printf(MSG_DEBUG, "iPhone: Apple80211Close failed: %d", + err); + } + + if (drv->scan_results) + CFRelease(drv->scan_results); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_iphone_ops = { + .name = "iphone", + .desc = "iPhone/iPod touch Apple80211 driver", + .get_ssid = wpa_driver_iphone_get_ssid, + .get_bssid = wpa_driver_iphone_get_bssid, + .init = wpa_driver_iphone_init, + .deinit = wpa_driver_iphone_deinit, + .scan = wpa_driver_iphone_scan, + .get_scan_results = wpa_driver_iphone_get_scan_results, + .associate = wpa_driver_iphone_associate, + .set_key = wpa_driver_iphone_set_key, + .get_capa = wpa_driver_iphone_get_capa, +}; diff --git a/contrib/wpa/src/drivers/driver_ipw.c b/contrib/wpa/src/drivers/driver_ipw.c new file mode 100644 index 0000000..3c19ccc --- /dev/null +++ b/contrib/wpa/src/drivers/driver_ipw.c @@ -0,0 +1,463 @@ +/* + * WPA Supplicant - driver interaction with Linux ipw2100/2200 drivers + * Copyright (c) 2005 Zhu Yi <yi.zhu@intel.com> + * Copyright (c) 2004 Lubomir Gelo <lgelo@cnc.sk> + * Copyright (c) 2003-2004, Jouni Malinen <j@w1.fi> + * + * 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. + * + * Please note that ipw2100/2200 drivers change to use generic Linux wireless + * extensions if the kernel includes support for WE-18 or newer (Linux 2.6.13 + * or newer). driver_wext.c should be used in those cases. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_ipw_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +/* following definitions must be kept in sync with ipw2100.c and ipw2200.c */ + +#define IPW_IOCTL_WPA_SUPPLICANT SIOCIWFIRSTPRIV+30 + +#define IPW_CMD_SET_WPA_PARAM 1 +#define IPW_CMD_SET_WPA_IE 2 +#define IPW_CMD_SET_ENCRYPTION 3 +#define IPW_CMD_MLME 4 + +#define IPW_PARAM_WPA_ENABLED 1 +#define IPW_PARAM_TKIP_COUNTERMEASURES 2 +#define IPW_PARAM_DROP_UNENCRYPTED 3 +#define IPW_PARAM_PRIVACY_INVOKED 4 +#define IPW_PARAM_AUTH_ALGS 5 +#define IPW_PARAM_IEEE_802_1X 6 + +#define IPW_MLME_STA_DEAUTH 1 +#define IPW_MLME_STA_DISASSOC 2 + +#define IPW_CRYPT_ERR_UNKNOWN_ALG 2 +#define IPW_CRYPT_ERR_UNKNOWN_ADDR 3 +#define IPW_CRYPT_ERR_CRYPT_INIT_FAILED 4 +#define IPW_CRYPT_ERR_KEY_SET_FAILED 5 +#define IPW_CRYPT_ERR_TX_KEY_SET_FAILED 6 +#define IPW_CRYPT_ERR_CARD_CONF_FAILED 7 + +#define IPW_CRYPT_ALG_NAME_LEN 16 + +struct ipw_param { + u32 cmd; + u8 sta_addr[ETH_ALEN]; + union { + struct { + u8 name; + u32 value; + } wpa_param; + struct { + u32 len; + u8 reserved[32]; + u8 data[0]; + } wpa_ie; + struct{ + u32 command; + u32 reason_code; + } mlme; + struct { + u8 alg[IPW_CRYPT_ALG_NAME_LEN]; + u8 set_tx; + u32 err; + u8 idx; + u8 seq[8]; + u16 key_len; + u8 key[0]; + } crypt; + + } u; +}; + +/* end of ipw2100.c and ipw2200.c code */ + +static int ipw_ioctl(struct wpa_driver_ipw_data *drv, + struct ipw_param *param, int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, IPW_IOCTL_WPA_SUPPLICANT, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[IPW_IOCTL_WPA_SUPPLICANT]"); + return ret; + } + + return 0; +} + + +static void ipw_show_set_key_error(struct ipw_param *param) +{ + switch (param->u.crypt.err) { + case IPW_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe ieee80211_crypt_wep' for" + " WEP."); + break; + case IPW_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case IPW_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case IPW_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case IPW_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case IPW_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int ipw_set_wpa_ie(struct wpa_driver_ipw_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct ipw_param *param; + int ret; + size_t blen = sizeof(*param) + wpa_ie_len; + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = IPW_CMD_SET_WPA_IE; + param->u.wpa_ie.len = wpa_ie_len; + os_memcpy(param->u.wpa_ie.data, wpa_ie, wpa_ie_len); + + ret = ipw_ioctl(drv, param, blen, 1); + + os_free(param); + return ret; +} + + +static int ipw_set_wpa_param(struct wpa_driver_ipw_data *drv, u8 name, + u32 value) +{ + struct ipw_param param; + + os_memset(¶m, 0, sizeof(param)); + param.cmd = IPW_CMD_SET_WPA_PARAM; + param.u.wpa_param.name = name; + param.u.wpa_param.value = value; + + return ipw_ioctl(drv, ¶m, sizeof(param), 1); +} + + +static int ipw_mlme(struct wpa_driver_ipw_data *drv, const u8 *addr, + int cmd, int reason) +{ + struct ipw_param param; + + os_memset(¶m, 0, sizeof(param)); + os_memcpy(param.sta_addr, addr, ETH_ALEN); + param.cmd = IPW_CMD_MLME; + param.u.mlme.command = cmd; + param.u.mlme.reason_code = reason; + + return ipw_ioctl(drv, ¶m, sizeof(param), 1); +} + + +static int wpa_driver_ipw_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + + if (!enabled && ipw_set_wpa_ie(drv, NULL, 0) < 0) + ret = -1; + + if (ipw_set_wpa_param(drv, IPW_PARAM_WPA_ENABLED, enabled) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_ipw_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ipw_data *drv = priv; + struct ipw_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct ipw_param *) buf; + param->cmd = IPW_CMD_SET_ENCRYPTION; + os_memset(param->sta_addr, 0xff, ETH_ALEN); + os_strlcpy((char *) param->u.crypt.alg, alg_name, + IPW_CRYPT_ALG_NAME_LEN); + param->u.crypt.set_tx = set_tx ? 1 : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (ipw_ioctl(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + ipw_show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_ipw_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ipw_set_wpa_param(drv, IPW_PARAM_TKIP_COUNTERMEASURES, + enabled); + +} + + +static int wpa_driver_ipw_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ipw_set_wpa_param(drv, IPW_PARAM_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_ipw_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ipw_data *drv = priv; + return ipw_mlme(drv, addr, IPW_MLME_STA_DEAUTH, reason_code); +} + + +static int wpa_driver_ipw_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ipw_data *drv = priv; + return ipw_mlme(drv, addr, IPW_MLME_STA_DISASSOC, reason_code); +} + + +static int +wpa_driver_ipw_associate(void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ipw_data *drv = priv; + int ret = 0; + int unencrypted_eapol; + + if (ipw_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + unencrypted_eapol = 0; + else + unencrypted_eapol = 1; + + if (ipw_set_wpa_param(drv, IPW_PARAM_IEEE_802_1X, + unencrypted_eapol) < 0) { + wpa_printf(MSG_DEBUG, "ipw: Failed to configure " + "ieee_802_1x param"); + } + + return ret; +} + + +static int wpa_driver_ipw_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_ipw_data *drv = priv; + int algs = 0; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= 1; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= 2; + if (auth_alg & AUTH_ALG_LEAP) + algs |= 4; + if (algs == 0) + algs = 1; /* at least one algorithm should be set */ + + wpa_printf(MSG_DEBUG, "%s: auth_alg=0x%x", __FUNCTION__, algs); + return ipw_set_wpa_param(drv, IPW_PARAM_AUTH_ALGS, algs); +} + + +static int wpa_driver_ipw_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_ipw_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_ipw_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_driver_ipw_get_scan_results(void *priv) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_ipw_set_operstate(void *priv, int state) +{ + struct wpa_driver_ipw_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_ipw_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ipw_data *drv; + int ver; + + wpa_printf(MSG_DEBUG, "%s is called", __FUNCTION__); + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + ver = wpa_driver_wext_get_version(drv->wext); + if (ver >= 18) { + wpa_printf(MSG_WARNING, "Linux wireless extensions version %d " + "detected.", ver); + wpa_printf(MSG_WARNING, "ipw2x00 driver uses driver_wext " + "(-Dwext) instead of driver_ipw."); + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_ipw_deinit(void *priv) +{ + struct wpa_driver_ipw_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_ipw_ops = { + .name = "ipw", + .desc = "Intel ipw2100/2200 driver (old; use wext with Linux 2.6.13 " + "or newer)", + .get_bssid = wpa_driver_ipw_get_bssid, + .get_ssid = wpa_driver_ipw_get_ssid, + .set_wpa = wpa_driver_ipw_set_wpa, + .set_key = wpa_driver_ipw_set_key, + .set_countermeasures = wpa_driver_ipw_set_countermeasures, + .set_drop_unencrypted = wpa_driver_ipw_set_drop_unencrypted, + .scan = wpa_driver_ipw_scan, + .get_scan_results2 = wpa_driver_ipw_get_scan_results, + .deauthenticate = wpa_driver_ipw_deauthenticate, + .disassociate = wpa_driver_ipw_disassociate, + .associate = wpa_driver_ipw_associate, + .set_auth_alg = wpa_driver_ipw_set_auth_alg, + .init = wpa_driver_ipw_init, + .deinit = wpa_driver_ipw_deinit, + .set_operstate = wpa_driver_ipw_set_operstate, +}; diff --git a/contrib/wpa/src/drivers/driver_madwifi.c b/contrib/wpa/src/drivers/driver_madwifi.c new file mode 100644 index 0000000..7521037 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_madwifi.c @@ -0,0 +1,601 @@ +/* + * WPA Supplicant - driver interaction with MADWIFI 802.11 driver + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * 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. + * + * Please note that madwifi supports WPA configuration via Linux wireless + * extensions and if the kernel includes support for this, driver_wext.c should + * be used instead of this driver wrapper. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "eloop.h" +#include "ieee802_11_defs.h" +#include "wireless_copy.h" + +/* + * Avoid conflicts with wpa_supplicant definitions by undefining a definition. + */ +#undef WME_OUI_TYPE + +#include <include/compat.h> +#include <net80211/ieee80211.h> +#ifdef WME_NUM_AC +/* Assume this is built against BSD branch of madwifi driver. */ +#define MADWIFI_BSD +#include <net80211/_ieee80211.h> +#endif /* WME_NUM_AC */ +#include <net80211/ieee80211_crypto.h> +#include <net80211/ieee80211_ioctl.h> + + +#ifdef IEEE80211_IOCTL_SETWMMPARAMS +/* Assume this is built against madwifi-ng */ +#define MADWIFI_NG +#endif /* IEEE80211_IOCTL_SETWMMPARAMS */ + +struct wpa_driver_madwifi_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +static int +set80211priv(struct wpa_driver_madwifi_data *drv, int op, void *data, int len, + int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + if (len < IFNAMSIZ && + op != IEEE80211_IOCTL_SET_APPIEBUF) { + /* + * Argument data fits inline; put it there. + */ + os_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->sock, op, &iwr) < 0) { + if (show_err) { +#ifdef MADWIFI_NG + int first = IEEE80211_IOCTL_SETPARAM; + int last = IEEE80211_IOCTL_KICKMAC; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETMODE]", + "ioctl[IEEE80211_IOCTL_GETMODE]", + "ioctl[IEEE80211_IOCTL_SETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_GETWMMPARAMS]", + "ioctl[IEEE80211_IOCTL_SETCHANLIST]", + "ioctl[IEEE80211_IOCTL_GETCHANLIST]", + "ioctl[IEEE80211_IOCTL_CHANSWITCH]", + NULL, + "ioctl[IEEE80211_IOCTL_SET_APPIEBUF]", + "ioctl[IEEE80211_IOCTL_GETSCANRESULTS]", + NULL, + "ioctl[IEEE80211_IOCTL_GETCHANINFO]", + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_WDSDELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_KICKMAC]", + }; +#else /* MADWIFI_NG */ + int first = IEEE80211_IOCTL_SETPARAM; + int last = IEEE80211_IOCTL_CHANLIST; + static const char *opnames[] = { + "ioctl[IEEE80211_IOCTL_SETPARAM]", + "ioctl[IEEE80211_IOCTL_GETPARAM]", + "ioctl[IEEE80211_IOCTL_SETKEY]", + "ioctl[IEEE80211_IOCTL_GETKEY]", + "ioctl[IEEE80211_IOCTL_DELKEY]", + NULL, + "ioctl[IEEE80211_IOCTL_SETMLME]", + NULL, + "ioctl[IEEE80211_IOCTL_SETOPTIE]", + "ioctl[IEEE80211_IOCTL_GETOPTIE]", + "ioctl[IEEE80211_IOCTL_ADDMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_DELMAC]", + NULL, + "ioctl[IEEE80211_IOCTL_CHANLIST]", + }; +#endif /* MADWIFI_NG */ + int idx = op - first; + if (first <= op && op <= last && + idx < (int) (sizeof(opnames) / sizeof(opnames[0])) + && opnames[idx]) + perror(opnames[idx]); + else + perror("ioctl[unknown???]"); + } + return -1; + } + return 0; +} + +static int +set80211param(struct wpa_driver_madwifi_data *drv, int op, int arg, + int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.mode = op; + os_memcpy(iwr.u.name+sizeof(u32), &arg, sizeof(arg)); + + if (ioctl(drv->sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + if (show_err) + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + return -1; + } + return 0; +} + +static int +wpa_driver_madwifi_set_wpa_ie(struct wpa_driver_madwifi_data *drv, + const u8 *wpa_ie, size_t wpa_ie_len) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* NB: SETOPTIE is not fixed-size so must not be inlined */ + iwr.u.data.pointer = (void *) wpa_ie; + iwr.u.data.length = wpa_ie_len; + + if (ioctl(drv->sock, IEEE80211_IOCTL_SETOPTIE, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETOPTIE]"); + return -1; + } + return 0; +} + +static int +wpa_driver_madwifi_del_key(struct wpa_driver_madwifi_data *drv, int key_idx, + const u8 *addr) +{ + struct ieee80211req_del_key wk; + + wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __FUNCTION__, key_idx); + os_memset(&wk, 0, sizeof(wk)); + wk.idk_keyix = key_idx; + if (addr != NULL) + os_memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + + return set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk), 1); +} + +static int +wpa_driver_madwifi_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_key wk; + char *alg_name; + u_int8_t cipher; + + if (alg == WPA_ALG_NONE) + return wpa_driver_madwifi_del_key(drv, key_idx, addr); + + switch (alg) { + case WPA_ALG_WEP: + if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN) == 0) { + /* + * madwifi did not seem to like static WEP key + * configuration with IEEE80211_IOCTL_SETKEY, so use + * Linux wireless extensions ioctl for this. + */ + return wpa_driver_wext_set_key(drv->wext, alg, addr, + key_idx, set_tx, + seq, seq_len, + key, key_len); + } + alg_name = "WEP"; + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + cipher = IEEE80211_CIPHER_AES_CCM; + break; + default: + wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d", + __FUNCTION__, alg); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > sizeof(u_int64_t)) { + wpa_printf(MSG_DEBUG, "%s: seq_len %lu too big", + __FUNCTION__, (unsigned long) seq_len); + return -2; + } + if (key_len > sizeof(wk.ik_keydata)) { + wpa_printf(MSG_DEBUG, "%s: key length %lu too big", + __FUNCTION__, (unsigned long) key_len); + return -3; + } + + os_memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV; + if (addr == NULL || + os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + wk.ik_flags |= IEEE80211_KEY_GROUP; + if (set_tx) { + wk.ik_flags |= IEEE80211_KEY_XMIT | IEEE80211_KEY_DEFAULT; + os_memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + } else + os_memset(wk.ik_macaddr, 0, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_keylen = key_len; +#ifdef WORDS_BIGENDIAN +#define WPA_KEY_RSC_LEN 8 + { + size_t i; + u8 tmp[WPA_KEY_RSC_LEN]; + os_memset(tmp, 0, sizeof(tmp)); + for (i = 0; i < seq_len; i++) + tmp[WPA_KEY_RSC_LEN - i - 1] = seq[i]; + os_memcpy(&wk.ik_keyrsc, tmp, WPA_KEY_RSC_LEN); + } +#else /* WORDS_BIGENDIAN */ + os_memcpy(&wk.ik_keyrsc, seq, seq_len); +#endif /* WORDS_BIGENDIAN */ + os_memcpy(wk.ik_keydata, key, key_len); + + return set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk), 1); +} + +static int +wpa_driver_madwifi_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_madwifi_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled, 1); +} + + +static int +wpa_driver_madwifi_set_drop_unencrypted(void *priv, int enabled) +{ + struct wpa_driver_madwifi_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_DROPUNENCRYPTED, enabled, 1); +} + +static int +wpa_driver_madwifi_deauthenticate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); +} + +static int +wpa_driver_madwifi_disassociate(void *priv, const u8 *addr, int reason_code) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + os_memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + return set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme), 1); +} + +static int +wpa_driver_madwifi_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret = 0, privacy = 1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * NB: Don't need to set the freq or cipher-related state as + * this is implied by the bssid which is used to locate + * the scanned node state which holds it. The ssid is + * needed to disambiguate an AP that broadcasts multiple + * ssid's but uses the same bssid. + */ + /* XXX error handling is wrong but unclear what to do... */ + if (wpa_driver_madwifi_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + + if (params->pairwise_suite == CIPHER_NONE && + params->group_suite == CIPHER_NONE && + params->key_mgmt_suite == KEY_MGMT_NONE && + params->wpa_ie_len == 0) + privacy = 0; + + if (set80211param(drv, IEEE80211_PARAM_PRIVACY, privacy, 1) < 0) + ret = -1; + + if (params->wpa_ie_len && + set80211param(drv, IEEE80211_PARAM_WPA, + params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1, 1) < 0) + ret = -1; + + if (params->bssid == NULL) { + /* ap_scan=2 mode - driver takes care of AP selection and + * roaming */ + /* FIX: this does not seem to work; would probably need to + * change something in the driver */ + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) + ret = -1; + + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + } else { + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + os_memset(&mlme, 0, sizeof(mlme)); + mlme.im_op = IEEE80211_MLME_ASSOC; + os_memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme), 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: SETMLME[ASSOC] failed", + __func__); + ret = -1; + } + } + + return ret; +} + +static int +wpa_driver_madwifi_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_madwifi_data *drv = priv; + int authmode; + + if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) && + (auth_alg & AUTH_ALG_SHARED_KEY)) + authmode = IEEE80211_AUTH_AUTO; + else if (auth_alg & AUTH_ALG_SHARED_KEY) + authmode = IEEE80211_AUTH_SHARED; + else + authmode = IEEE80211_AUTH_OPEN; + + return set80211param(drv, IEEE80211_PARAM_AUTHMODE, authmode, 1); +} + +static int +wpa_driver_madwifi_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_madwifi_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + /* set desired ssid before scan */ + /* FIX: scan should not break the current association, so using + * set_ssid may not be the best way of doing this.. */ + if (wpa_driver_wext_set_ssid(drv->wext, ssid, ssid_len) < 0) + ret = -1; + + if (ioctl(drv->sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* + * madwifi delivers a scan complete event so no need to poll, but + * register a backup timeout anyway to make sure that we recover even + * if the driver does not send this event for any reason. This timeout + * will only be used if the event is not delivered (event handler will + * cancel the timeout). + */ + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + eloop_register_timeout(30, 0, wpa_driver_wext_scan_timeout, drv->wext, + drv->ctx); + + return ret; +} + +static int wpa_driver_madwifi_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_madwifi_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static struct wpa_scan_results * +wpa_driver_madwifi_get_scan_results(void *priv) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_madwifi_set_operstate(void *priv, int state) +{ + struct wpa_driver_madwifi_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static int wpa_driver_madwifi_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct ieee80211req_getset_appiebuf *probe_req_ie; + int ret; + + probe_req_ie = os_malloc(sizeof(*probe_req_ie) + ies_len); + if (probe_req_ie == NULL) + return -1; + + probe_req_ie->app_frmtype = IEEE80211_APPIE_FRAME_PROBE_REQ; + probe_req_ie->app_buflen = ies_len; + os_memcpy(probe_req_ie->app_buf, ies, ies_len); + + ret = set80211priv(priv, IEEE80211_IOCTL_SET_APPIEBUF, probe_req_ie, + sizeof(struct ieee80211req_getset_appiebuf) + + ies_len, 1); + + os_free(probe_req_ie); + + return ret; +} + + +static void * wpa_driver_madwifi_init(void *ctx, const char *ifname) +{ + struct wpa_driver_madwifi_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) + goto fail; + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) + goto fail2; + + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 2, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based " + "roaming", __FUNCTION__); + goto fail3; + } + + if (set80211param(drv, IEEE80211_PARAM_WPA, 3, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support", + __FUNCTION__); + goto fail3; + } + + return drv; + +fail3: + close(drv->sock); +fail2: + wpa_driver_wext_deinit(drv->wext); +fail: + os_free(drv); + return NULL; +} + + +static void wpa_driver_madwifi_deinit(void *priv) +{ + struct wpa_driver_madwifi_data *drv = priv; + + if (wpa_driver_madwifi_set_wpa_ie(drv, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to clear WPA IE", + __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_ROAMING, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to enable driver-based " + "roaming", __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_PRIVACY, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to disable forced Privacy " + "flag", __FUNCTION__); + } + if (set80211param(drv, IEEE80211_PARAM_WPA, 0, 1) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed to disable WPA", + __FUNCTION__); + } + + wpa_driver_wext_deinit(drv->wext); + + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_madwifi_ops = { + .name = "madwifi", + .desc = "MADWIFI 802.11 support (Atheros, etc.)", + .get_bssid = wpa_driver_madwifi_get_bssid, + .get_ssid = wpa_driver_madwifi_get_ssid, + .set_key = wpa_driver_madwifi_set_key, + .init = wpa_driver_madwifi_init, + .deinit = wpa_driver_madwifi_deinit, + .set_countermeasures = wpa_driver_madwifi_set_countermeasures, + .set_drop_unencrypted = wpa_driver_madwifi_set_drop_unencrypted, + .scan = wpa_driver_madwifi_scan, + .get_scan_results2 = wpa_driver_madwifi_get_scan_results, + .deauthenticate = wpa_driver_madwifi_deauthenticate, + .disassociate = wpa_driver_madwifi_disassociate, + .associate = wpa_driver_madwifi_associate, + .set_auth_alg = wpa_driver_madwifi_set_auth_alg, + .set_operstate = wpa_driver_madwifi_set_operstate, + .set_probe_req_ie = wpa_driver_madwifi_set_probe_req_ie, +}; diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c index b22109b..0bda20a 100644 --- a/contrib/wpa/src/drivers/driver_ndis.c +++ b/contrib/wpa/src/drivers/driver_ndis.c @@ -56,9 +56,14 @@ static int wpa_driver_ndis_adapter_open(struct wpa_driver_ndis_data *drv); static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv); +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + /* FIX: to be removed once this can be compiled with the complete NDIS * header files */ #ifndef OID_802_11_BSSID +#define OID_802_3_MULTICAST_LIST 0x01010103 #define OID_802_11_BSSID 0x0d010101 #define OID_802_11_SSID 0x0d010102 #define OID_802_11_INFRASTRUCTURE_MODE 0x0d010108 @@ -610,12 +615,7 @@ static int wpa_driver_ndis_get_bssid(void *priv, u8 *bssid) * Report PAE group address as the "BSSID" for wired * connection. */ - bssid[0] = 0x01; - bssid[1] = 0x80; - bssid[2] = 0xc2; - bssid[3] = 0x00; - bssid[4] = 0x00; - bssid[5] = 0x03; + os_memcpy(bssid, pae_group_addr, ETH_ALEN); return 0; } @@ -2704,6 +2704,19 @@ static void wpa_driver_ndis_adapter_close(struct wpa_driver_ndis_data *drv) } +static int ndis_add_multicast(struct wpa_driver_ndis_data *drv) +{ + if (ndis_set_oid(drv, OID_802_3_MULTICAST_LIST, + (const char *) pae_group_addr, ETH_ALEN) < 0) { + wpa_printf(MSG_DEBUG, "NDIS: Failed to add PAE group address " + "to the multicast list"); + return -1; + } + + return 0; +} + + static void * wpa_driver_ndis_init(void *ctx, const char *ifname) { struct wpa_driver_ndis_data *drv; @@ -2799,6 +2812,7 @@ static void * wpa_driver_ndis_init(void *ctx, const char *ifname) "any wireless capabilities - assume it is " "a wired interface"); drv->wired = 1; + ndis_add_multicast(drv); } } diff --git a/contrib/wpa/src/drivers/driver_ndis_.c b/contrib/wpa/src/drivers/driver_ndis_.c new file mode 100644 index 0000000..4bee9aa --- /dev/null +++ b/contrib/wpa/src/drivers/driver_ndis_.c @@ -0,0 +1,105 @@ +/* + * WPA Supplicant - Windows/NDIS driver interface - event processing + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * 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 "common.h" +#include "driver.h" +#include "eloop.h" + +/* Keep this event processing in a separate file and without WinPcap headers to + * avoid conflicts with some of the header files. */ +struct _ADAPTER; +typedef struct _ADAPTER * LPADAPTER; +#include "driver_ndis.h" + + +void wpa_driver_ndis_event_connect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_disconnect(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_media_specific(struct wpa_driver_ndis_data *drv, + const u8 *data, size_t data_len); +void wpa_driver_ndis_event_adapter_arrival(struct wpa_driver_ndis_data *drv); +void wpa_driver_ndis_event_adapter_removal(struct wpa_driver_ndis_data *drv); + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, + EVENT_MEDIA_SPECIFIC, EVENT_ADAPTER_ARRIVAL, + EVENT_ADAPTER_REMOVAL }; + +/* Event data: + * enum event_types (as int, i.e., 4 octets) + * data length (2 octets (big endian), optional) + * data (variable len, optional) + */ + + +static void wpa_driver_ndis_event_process(struct wpa_driver_ndis_data *drv, + u8 *buf, size_t len) +{ + u8 *pos, *data = NULL; + enum event_types type; + size_t data_len = 0; + + wpa_hexdump(MSG_MSGDUMP, "NDIS: received event data", buf, len); + if (len < sizeof(int)) + return; + type = *((int *) buf); + pos = buf + sizeof(int); + wpa_printf(MSG_DEBUG, "NDIS: event - type %d", type); + + if (buf + len - pos > 2) { + data_len = (int) *pos++ << 8; + data_len += *pos++; + if (data_len > (size_t) (buf + len - pos)) { + wpa_printf(MSG_DEBUG, "NDIS: event data overflow"); + return; + } + data = pos; + wpa_hexdump(MSG_MSGDUMP, "NDIS: event data", data, data_len); + } + + switch (type) { + case EVENT_CONNECT: + wpa_driver_ndis_event_connect(drv); + break; + case EVENT_DISCONNECT: + wpa_driver_ndis_event_disconnect(drv); + break; + case EVENT_MEDIA_SPECIFIC: + wpa_driver_ndis_event_media_specific(drv, data, data_len); + break; + case EVENT_ADAPTER_ARRIVAL: + wpa_driver_ndis_event_adapter_arrival(drv); + break; + case EVENT_ADAPTER_REMOVAL: + wpa_driver_ndis_event_adapter_removal(drv); + break; + } +} + + +void wpa_driver_ndis_event_pipe_cb(void *eloop_data, void *user_data) +{ + struct wpa_driver_ndis_data *drv = eloop_data; + u8 buf[512]; + DWORD len; + + ResetEvent(drv->event_avail); + if (ReadFile(drv->events_pipe, buf, sizeof(buf), &len, NULL)) + wpa_driver_ndis_event_process(drv, buf, len); + else { + wpa_printf(MSG_DEBUG, "%s: ReadFile() failed: %d", __func__, + (int) GetLastError()); + } +} diff --git a/contrib/wpa/src/drivers/driver_ndiswrapper.c b/contrib/wpa/src/drivers/driver_ndiswrapper.c new file mode 100644 index 0000000..b5c534a --- /dev/null +++ b/contrib/wpa/src/drivers/driver_ndiswrapper.c @@ -0,0 +1,370 @@ +/* + * WPA Supplicant - driver interaction with Linux ndiswrapper + * Copyright (c) 2004-2006, Giridhar Pemmasani <giri@lmc.cs.sunysb.edu> + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * 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. + * + * Please note that ndiswrapper supports WPA configuration via Linux wireless + * extensions and if the kernel includes support for this, driver_wext.c should + * be used instead of this driver wrapper. + */ + +#include "includes.h" +#include <sys/ioctl.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" + +struct wpa_driver_ndiswrapper_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + + +struct wpa_key +{ + wpa_alg alg; + const u8 *addr; + int key_index; + int set_tx; + const u8 *seq; + size_t seq_len; + const u8 *key; + size_t key_len; +}; + +struct wpa_assoc_info +{ + const u8 *bssid; + const u8 *ssid; + size_t ssid_len; + int freq; + const u8 *wpa_ie; + size_t wpa_ie_len; + wpa_cipher pairwise_suite; + wpa_cipher group_suite; + wpa_key_mgmt key_mgmt_suite; + int auth_alg; + int mode; +}; + +#define PRIV_RESET SIOCIWFIRSTPRIV+0 +#define WPA_SET_WPA SIOCIWFIRSTPRIV+1 +#define WPA_SET_KEY SIOCIWFIRSTPRIV+2 +#define WPA_ASSOCIATE SIOCIWFIRSTPRIV+3 +#define WPA_DISASSOCIATE SIOCIWFIRSTPRIV+4 +#define WPA_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+5 +#define WPA_SET_COUNTERMEASURES SIOCIWFIRSTPRIV+6 +#define WPA_DEAUTHENTICATE SIOCIWFIRSTPRIV+7 +#define WPA_SET_AUTH_ALG SIOCIWFIRSTPRIV+8 +#define WPA_INIT SIOCIWFIRSTPRIV+9 +#define WPA_DEINIT SIOCIWFIRSTPRIV+10 +#define WPA_GET_CAPA SIOCIWFIRSTPRIV+11 + +static int get_socket(void) +{ + static const int families[] = { + AF_INET, AF_IPX, AF_AX25, AF_APPLETALK + }; + unsigned int i; + int sock; + + for (i = 0; i < sizeof(families) / sizeof(int); ++i) { + sock = socket(families[i], SOCK_DGRAM, 0); + if (sock >= 0) + return sock; + } + + return -1; +} + +static int iw_set_ext(struct wpa_driver_ndiswrapper_data *drv, int request, + struct iwreq *pwrq) +{ + os_strlcpy(pwrq->ifr_name, drv->ifname, IFNAMSIZ); + return ioctl(drv->sock, request, pwrq); +} + +static int wpa_ndiswrapper_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + struct iwreq priv_req; + int ret = 0; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.data.flags = enabled; + if (iw_set_ext(drv, WPA_SET_WPA, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + struct wpa_key wpa_key; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + wpa_key.alg = alg; + wpa_key.addr = addr; + wpa_key.key_index = key_idx; + wpa_key.set_tx = set_tx; + wpa_key.seq = seq; + wpa_key.seq_len = seq_len; + wpa_key.key = key; + wpa_key.key_len = key_len; + + priv_req.u.data.pointer = (void *)&wpa_key; + priv_req.u.data.length = sizeof(wpa_key); + + if (iw_set_ext(drv, WPA_SET_KEY, &priv_req) < 0) + ret = -1; + + if (alg == WPA_ALG_NONE) { + /* + * ndiswrapper did not seem to be clearing keys properly in + * some cases with WPA_SET_KEY. For example, roaming from WPA + * enabled AP to plaintext one seemed to fail since the driver + * did not associate. Try to make sure the keys are cleared so + * that plaintext APs can be used in all cases. + */ + wpa_driver_wext_set_key(drv->wext, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); + } + + return ret; +} + +static int wpa_ndiswrapper_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = enabled; + if (iw_set_ext(drv, WPA_SET_COUNTERMEASURES, &priv_req) < 0) + ret = -1; + + return ret; +} + +static int wpa_ndiswrapper_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = enabled; + if (iw_set_ext(drv, WPA_DROP_UNENCRYPTED, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = reason_code; + os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); + if (iw_set_ext(drv, WPA_DEAUTHENTICATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + os_memcpy(&priv_req.u.ap_addr.sa_data, addr, ETH_ALEN); + if (iw_set_ext(drv, WPA_DISASSOCIATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int +wpa_ndiswrapper_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct wpa_assoc_info wpa_assoc_info; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info)); + + wpa_assoc_info.bssid = params->bssid; + wpa_assoc_info.ssid = params->ssid; + wpa_assoc_info.ssid_len = params->ssid_len; + wpa_assoc_info.freq = params->freq; + wpa_assoc_info.wpa_ie = params->wpa_ie; + wpa_assoc_info.wpa_ie_len = params->wpa_ie_len; + wpa_assoc_info.pairwise_suite = params->pairwise_suite; + wpa_assoc_info.group_suite = params->group_suite; + wpa_assoc_info.key_mgmt_suite = params->key_mgmt_suite; + wpa_assoc_info.auth_alg = params->auth_alg; + wpa_assoc_info.mode = params->mode; + + priv_req.u.data.pointer = (void *)&wpa_assoc_info; + priv_req.u.data.length = sizeof(wpa_assoc_info); + + if (iw_set_ext(drv, WPA_ASSOCIATE, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.param.value = auth_alg; + if (iw_set_ext(drv, WPA_SET_AUTH_ALG, &priv_req) < 0) + ret = -1; + return ret; +} + +static int wpa_ndiswrapper_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_ndiswrapper_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * wpa_ndiswrapper_get_scan_results(void *priv) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_ndiswrapper_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + int ret = 0; + struct iwreq priv_req; + + os_memset(&priv_req, 0, sizeof(priv_req)); + + priv_req.u.data.pointer = (void *) capa; + priv_req.u.data.length = sizeof(*capa); + if (iw_set_ext(drv, WPA_GET_CAPA, &priv_req) < 0) + ret = -1; + return ret; + +} + + +static int wpa_ndiswrapper_set_operstate(void *priv, int state) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) +{ + struct wpa_driver_ndiswrapper_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = get_socket(); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_ndiswrapper_deinit(void *priv) +{ + struct wpa_driver_ndiswrapper_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = { + .name = "ndiswrapper", + .desc = "Linux ndiswrapper (deprecated; use wext)", + .set_wpa = wpa_ndiswrapper_set_wpa, + .set_key = wpa_ndiswrapper_set_key, + .set_countermeasures = wpa_ndiswrapper_set_countermeasures, + .set_drop_unencrypted = wpa_ndiswrapper_set_drop_unencrypted, + .deauthenticate = wpa_ndiswrapper_deauthenticate, + .disassociate = wpa_ndiswrapper_disassociate, + .associate = wpa_ndiswrapper_associate, + .set_auth_alg = wpa_ndiswrapper_set_auth_alg, + + .get_bssid = wpa_ndiswrapper_get_bssid, + .get_ssid = wpa_ndiswrapper_get_ssid, + .scan = wpa_ndiswrapper_scan, + .get_scan_results2 = wpa_ndiswrapper_get_scan_results, + .init = wpa_ndiswrapper_init, + .deinit = wpa_ndiswrapper_deinit, + .get_capa = wpa_ndiswrapper_get_capa, + .set_operstate = wpa_ndiswrapper_set_operstate, +}; diff --git a/contrib/wpa/src/drivers/driver_nl80211.c b/contrib/wpa/src/drivers/driver_nl80211.c new file mode 100644 index 0000000..9ab6d17 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_nl80211.c @@ -0,0 +1,2766 @@ +/* + * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 + * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * + * 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 <net/if_arp.h> +#include <netlink/genl/genl.h> +#include <netlink/genl/family.h> +#include <netlink/genl/ctrl.h> +#include "nl80211_copy.h" +#ifdef CONFIG_CLIENT_MLME +#include <netpacket/packet.h> +#include <linux/if_ether.h> +#include "radiotap.h" +#include "radiotap_iter.h" +#endif /* CONFIG_CLIENT_MLME */ + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "ieee802_11_defs.h" + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ +#endif +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 /* driver signals dormant */ +#endif + +#ifndef IF_OPER_DORMANT +#define IF_OPER_DORMANT 5 +#endif +#ifndef IF_OPER_UP +#define IF_OPER_UP 6 +#endif + + +struct wpa_driver_nl80211_data { + void *ctx; + int wext_event_sock; + int ioctl_sock; + char ifname[IFNAMSIZ + 1]; + int ifindex; + int if_removed; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + struct wpa_driver_capa capa; + int has_capability; + int we_version_compiled; + + /* for set_auth_alg fallback */ + int use_crypt; + int auth_alg_fallback; + + int operstate; + + char mlmedev[IFNAMSIZ + 1]; + + int scan_complete_events; + + struct nl_handle *nl_handle; + struct nl_cache *nl_cache; + struct nl_cb *nl_cb; + struct genl_family *nl80211; + +#ifdef CONFIG_CLIENT_MLME + int monitor_sock; /* socket for monitor */ + int monitor_ifidx; +#endif /* CONFIG_CLIENT_MLME */ +}; + + +static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, + void *timeout_ctx); +static int wpa_driver_nl80211_set_mode(void *priv, int mode); +static int wpa_driver_nl80211_flush_pmkid(void *priv); +static int wpa_driver_nl80211_get_range(void *priv); +static void +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv); + + +/* nl80211 code */ +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err = -ENOMEM; + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(drv->nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) + nl_recvmsgs(drv->nl_handle, cb); + out: + nl_cb_put(cb); + nlmsg_free(msg); + return err; +} + + +struct family_data { + const char *group; + int id; +}; + + +static int family_handler(struct nl_msg *msg, void *arg) +{ + struct family_data *res = arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int i; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; + nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), + nla_len(mcgrp), NULL); + if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || + !tb2[CTRL_ATTR_MCAST_GRP_ID] || + os_strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), + res->group, + nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) + continue; + res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); + break; + }; + + return NL_SKIP; +} + + +static int nl_get_multicast_id(struct wpa_driver_nl80211_data *drv, + const char *family, const char *group) +{ + struct nl_msg *msg; + int ret = -1; + struct family_data res = { group, -ENOENT }; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), + 0, 0, CTRL_CMD_GETFAMILY, 0); + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + + ret = send_and_recv_msgs(drv, msg, family_handler, &res); + msg = NULL; + if (ret == 0) + ret = res.id; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + + +static int wpa_driver_nl80211_send_oper_ifla( + struct wpa_driver_nl80211_data *drv, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = drv->ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(drv->wext_event_sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " + "%s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} + + +static int wpa_driver_nl80211_set_auth_param( + struct wpa_driver_nl80211_data *drv, int idx, u32 value) +{ + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { + if (errno != EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " + "value 0x%x) failed: %s)", + idx, value, strerror(errno)); + } + ret = errno == EOPNOTSUPP ? -2 : -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + + +static int wpa_driver_nl80211_set_bssid(void *priv, const u8 *bssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + else + os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { + perror("ioctl[SIOCSIWAP]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0' && + drv->we_version_compiled < 21) + ret--; + } + + return ret; +} + + +static int wpa_driver_nl80211_set_ssid(void *priv, const u8 *ssid, + size_t ssid_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + char buf[33]; + + if (ssid_len > 32) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ + iwr.u.essid.flags = (ssid_len != 0); + os_memset(buf, 0, sizeof(buf)); + os_memcpy(buf, ssid, ssid_len); + iwr.u.essid.pointer = (caddr_t) buf; + if (drv->we_version_compiled < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (ssid_len) + ssid_len++; + } + iwr.u.essid.length = ssid_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_set_freq(void *priv, int freq) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.freq.m = freq * 100000; + iwr.u.freq.e = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + ret = -1; + } + + return ret; +} + + +static void +wpa_driver_nl80211_event_wireless_custom(void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", + custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast ") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { + char *spos; + int bytes; + + spos = custom + 17; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + return; + bytes /= 2; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.req_ies, bytes); + + spos += bytes * 2; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + spos += 9; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + goto done; + bytes /= 2; + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.resp_ies, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { + if (hwaddr_aton(custom + 17, data.stkstart.peer)) { + wpa_printf(MSG_DEBUG, "WEXT: unrecognized " + "STKSTART.request '%s'", custom + 17); + return; + } + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +#endif /* CONFIG_PEERKEY */ + } +} + + +static int wpa_driver_nl80211_event_wireless_michaelmicfailure( + void *ctx, const char *ev, size_t len) +{ + const struct iw_michaelmicfailure *mic; + union wpa_event_data data; + + if (len < sizeof(*mic)) + return -1; + + mic = (const struct iw_michaelmicfailure *) ev; + + wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " + "flags=0x%x src_addr=" MACSTR, mic->flags, + MAC2STR(mic->src_addr.sa_data)); + + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_pmkidcand( + struct wpa_driver_nl80211_data *drv, const char *ev, size_t len) +{ + const struct iw_pmkid_cand *cand; + union wpa_event_data data; + const u8 *addr; + + if (len < sizeof(*cand)) + return -1; + + cand = (const struct iw_pmkid_cand *) ev; + addr = (const u8 *) cand->bssid.sa_data; + + wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " + "flags=0x%x index=%d bssid=" MACSTR, cand->flags, + cand->index, MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); + data.pmkid_candidate.index = cand->index; + data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_assocreqie( + struct wpa_driver_nl80211_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = os_malloc(len); + if (drv->assoc_req_ies == NULL) { + drv->assoc_req_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_req_ies, ev, len); + drv->assoc_req_ies_len = len; + + return 0; +} + + +static int wpa_driver_nl80211_event_wireless_assocrespie( + struct wpa_driver_nl80211_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = os_malloc(len); + if (drv->assoc_resp_ies == NULL) { + drv->assoc_resp_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_resp_ies, ev, len); + drv->assoc_resp_ies_len = len; + + return 0; +} + + +static void wpa_driver_nl80211_event_assoc_ies(struct wpa_driver_nl80211_data *drv) +{ + union wpa_event_data data; + + if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + if (drv->assoc_req_ies) { + data.assoc_info.req_ies = drv->assoc_req_ies; + drv->assoc_req_ies = NULL; + data.assoc_info.req_ies_len = drv->assoc_req_ies_len; + } + if (drv->assoc_resp_ies) { + data.assoc_info.resp_ies = drv->assoc_resp_ies; + drv->assoc_resp_ies = NULL; + data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(data.assoc_info.req_ies); + os_free(data.assoc_info.resp_ies); +} + + +static void wpa_driver_nl80211_event_wireless(struct wpa_driver_nl80211_data *drv, + void *ctx, 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. */ + os_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_compiled > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVASSOCRESPIE || + iwe->cmd == IWEVPMKIDCAND)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + wpa_printf(MSG_DEBUG, "Wireless event: new AP: " + MACSTR, + MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); + if (is_zero_ether_addr( + (const u8 *) iwe->u.ap_addr.sa_data) || + os_memcmp(iwe->u.ap_addr.sa_data, + "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == + 0) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + + } else { + wpa_driver_nl80211_event_assoc_ies(drv); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } + break; + case IWEVMICHAELMICFAILURE: + wpa_driver_nl80211_event_wireless_michaelmicfailure( + ctx, custom, iwe->u.data.length); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + wpa_driver_nl80211_event_wireless_custom(ctx, buf); + os_free(buf); + break; + case IWEVASSOCREQIE: + wpa_driver_nl80211_event_wireless_assocreqie( + drv, custom, iwe->u.data.length); + break; + case IWEVASSOCRESPIE: + wpa_driver_nl80211_event_wireless_assocrespie( + drv, custom, iwe->u.data.length); + break; + case IWEVPMKIDCAND: + wpa_driver_nl80211_event_wireless_pmkidcand( + drv, custom, iwe->u.data.length); + break; + } + + pos += iwe->len; + } +} + + +static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv, + void *ctx, char *buf, size_t len, + int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { + if (del) + drv->if_removed = 1; + else + drv->if_removed = 0; + } + + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv, + struct nlmsghdr *h) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr *attr; + + ifi = NLMSG_DATA(h); + + _nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - _nlmsg_len; + if (attrlen < 0) + return 0; + + attr = (struct rtattr *) (((char *) ifi) + _nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, drv->ifname) + == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv, + int ifindex, struct nlmsghdr *h) +{ + if (drv->ifindex == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, h)) { + drv->ifindex = if_nametoindex(drv->ifname); + wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed " + "interface"); + wpa_driver_nl80211_finish_drv_init(drv); + return 1; + } + + return 0; +} + + +static void wpa_driver_nl80211_event_rtm_newlink(struct wpa_driver_nl80211_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (!wpa_driver_nl80211_own_ifindex(drv, ifi->ifi_index, h)) { + wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + /* + * Some drivers send the association event before the operup event--in + * this case, lifting operstate in wpa_driver_nl80211_set_operstate() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + wpa_driver_nl80211_send_oper_ifla(drv, -1, IF_OPER_UP); + + _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) { + wpa_driver_nl80211_event_wireless( + drv, ctx, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } else if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_nl80211_event_link( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_nl80211_event_rtm_dellink(struct wpa_driver_nl80211_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, _nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + _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_IFNAME) { + wpa_driver_nl80211_event_link( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_nl80211_event_receive_wext(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + 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) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_nl80211_event_rtm_newlink(eloop_ctx, sock_ctx, + h, plen); + break; + case RTM_DELLINK: + wpa_driver_nl80211_event_rtm_dellink(eloop_ctx, sock_ctx, + h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + + +static int no_seq_check(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + + +static int process_event(struct nl_msg *msg, void *arg) +{ + struct wpa_driver_nl80211_data *drv = arg; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + + if (tb[NL80211_ATTR_IFINDEX]) { + int ifindex = nla_get_u32(tb[NL80211_ATTR_IFINDEX]); + if (ifindex != drv->ifindex) { + wpa_printf(MSG_DEBUG, "nl80211: Ignored event (cmd=%d)" + " for foreign interface (ifindex %d)", + gnlh->cmd, ifindex); + return NL_SKIP; + } + } + + switch (gnlh->cmd) { + case NL80211_CMD_TRIGGER_SCAN: + wpa_printf(MSG_DEBUG, "nl80211: Scan trigger"); + break; + case NL80211_CMD_NEW_SCAN_RESULTS: + wpa_printf(MSG_DEBUG, "nl80211: New scan results available"); + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + case NL80211_CMD_SCAN_ABORTED: + wpa_printf(MSG_DEBUG, "nl80211: Scan aborted"); + /* + * Need to indicate that scan results are available in order + * not to make wpa_supplicant stop its scanning. + */ + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, + drv->ctx); + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + default: + wpa_printf(MSG_DEBUG, "nl0211: Ignored unknown event (cmd=%d)", + gnlh->cmd); + break; + } + + return NL_SKIP; +} + + +static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct nl_cb *cb; + struct wpa_driver_nl80211_data *drv = eloop_ctx; + + wpa_printf(MSG_DEBUG, "nl80211: Event message available"); + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + return; + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, no_seq_check, NULL); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, process_event, drv); + nl_recvmsgs(drv->nl_handle, cb); + nl_cb_put(cb); +} + + +static int wpa_driver_nl80211_get_ifflags_ifname(struct wpa_driver_nl80211_data *drv, + const char *ifname, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +/** + * wpa_driver_nl80211_get_ifflags - Get interface flags (SIOCGIFFLAGS) + * @drv: driver_nl80211 private data + * @flags: Pointer to returned flags value + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_get_ifflags(struct wpa_driver_nl80211_data *drv, + int *flags) +{ + return wpa_driver_nl80211_get_ifflags_ifname(drv, drv->ifname, flags); +} + + +static int wpa_driver_nl80211_set_ifflags_ifname( + struct wpa_driver_nl80211_data *drv, + const char *ifname, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + + +/** + * wpa_driver_nl80211_set_ifflags - Set interface flags (SIOCSIFFLAGS) + * @drv: driver_nl80211 private data + * @flags: New value for flags + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_set_ifflags(struct wpa_driver_nl80211_data *drv, + int flags) +{ + return wpa_driver_nl80211_set_ifflags_ifname(drv, drv->ifname, flags); +} + + +/** + * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain + * @priv: driver_nl80211 private data + * @alpha2_arg: country to which to switch to + * Returns: 0 on success, -1 on failure + * + * This asks nl80211 to set the regulatory domain for given + * country ISO / IEC alpha2. + */ +static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg) +{ + struct wpa_driver_nl80211_data *drv = priv; + char alpha2[3]; + struct nl_msg *msg; + + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + alpha2[0] = alpha2_arg[0]; + alpha2[1] = alpha2_arg[1]; + alpha2[2] = '\0'; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, + 0, NL80211_CMD_REQ_SET_REG, 0); + + NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2); + if (send_and_recv_msgs(drv, msg, NULL, NULL)) + return -EINVAL; + return 0; +nla_put_failure: + return -EINVAL; +} + + +static int wpa_driver_nl80211_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_MGMT_EXTRA_IE, 0); + + NLA_PUT_U8(msg, NL80211_ATTR_MGMT_SUBTYPE, 4 /* ProbeReq */); + if (ies) + NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies); + + ret = 0; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +} + + +#ifdef CONFIG_CLIENT_MLME + +static int nl80211_set_vif(struct wpa_driver_nl80211_data *drv, + int drop_unencrypted, int userspace_mlme) +{ +#ifdef NL80211_CMD_SET_VIF + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_SET_VIF, 0); + + if (drop_unencrypted >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_VIF_DROP_UNENCRYPTED, + drop_unencrypted); + if (userspace_mlme >= 0) + NLA_PUT_U8(msg, NL80211_ATTR_VIF_USERSPACE_MLME, + userspace_mlme); + + ret = 0; + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +#else /* NL80211_CMD_SET_VIF */ + return -1; +#endif /* NL80211_CMD_SET_VIF */ +} + + +static int wpa_driver_nl80211_set_userspace_mlme( + struct wpa_driver_nl80211_data *drv, int enabled) +{ + return nl80211_set_vif(drv, -1, enabled); +} + + +static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, + int ifidx) +{ + struct nl_msg *msg; + + 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 (send_and_recv_msgs(drv, msg, NULL, NULL) == 0) + return; +nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to remove interface."); +} + + +static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv, + const char *ifname, enum nl80211_iftype iftype) +{ + struct nl_msg *msg, *flags = NULL; + int ifidx, err; + int ret = -ENOBUFS; + + 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->ifname)); + NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype); + + if (iftype == NL80211_IFTYPE_MONITOR) { + 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; + } + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to create interface %d", + ret); + return ret; + } + + ifidx = if_nametoindex(ifname); + if (ifidx <= 0) + return -1; + + return ifidx; +} + + +static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct wpa_driver_nl80211_data *drv = eloop_ctx; + int len; + unsigned char buf[3000]; + struct ieee80211_radiotap_iterator iter; + int ret; + int injected = 0, failed = 0, rxflags = 0; + struct ieee80211_rx_status rx_status; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len)) { + wpa_printf(MSG_DEBUG, "nl80211: received invalid radiotap " + "frame"); + return; + } + + os_memset(&rx_status, 0, sizeof(rx_status)); + + while (1) { + ret = ieee80211_radiotap_iterator_next(&iter); + if (ret == -ENOENT) + break; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: received invalid " + "radiotap frame (%d)", 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((*(u16 *) 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 + * rx_status.channel = XXX; + */ + break; + case IEEE80211_RADIOTAP_RATE: + break; + case IEEE80211_RADIOTAP_DB_ANTSIGNAL: + rx_status.ssi = *iter.this_arg; + break; + } + } + + if (rxflags && injected) + return; + + if (!injected) { + wpa_supplicant_sta_rx(drv->ctx, buf + iter.max_length, + len - iter.max_length, &rx_status); + } else if (failed) { + /* TX failure callback */ + } else { + /* TX success (ACK) callback */ + } +} + + +static int wpa_driver_nl80211_create_monitor_interface( + struct wpa_driver_nl80211_data *drv) +{ + char buf[IFNAMSIZ]; + struct sockaddr_ll ll; + int optval, flags; + socklen_t optlen; + + os_snprintf(buf, IFNAMSIZ, "mon.%s", drv->ifname); + buf[IFNAMSIZ - 1] = '\0'; + + drv->monitor_ifidx = + nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR); + + if (drv->monitor_ifidx < 0) + return -1; + + if (wpa_driver_nl80211_get_ifflags_ifname(drv, buf, &flags) != 0 || + wpa_driver_nl80211_set_ifflags_ifname(drv, buf, flags | IFF_UP) != + 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not set interface '%s' " + "UP", buf); + goto error; + } + + os_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)) { + wpa_printf(MSG_ERROR, "nl80211: Could not register monitor " + "read socket"); + goto error; + } + + return 0; + + error: + nl80211_remove_iface(drv, drv->monitor_ifidx); + return -1; +} + +#endif /* CONFIG_CLIENT_MLME */ + + +/** + * wpa_driver_nl80211_init - Initialize nl80211 driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +static void * wpa_driver_nl80211_init(void *ctx, const char *ifname) +{ + int s, ret; + struct sockaddr_nl local; + struct wpa_driver_nl80211_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT); + if (drv->nl_cb == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + goto err1; + } + + drv->nl_handle = nl_handle_alloc_cb(drv->nl_cb); + if (drv->nl_handle == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink " + "callbacks"); + goto err2; + } + + if (genl_connect(drv->nl_handle)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to connect to generic " + "netlink"); + goto err3; + } + + drv->nl_cache = genl_ctrl_alloc_cache(drv->nl_handle); + if (drv->nl_cache == NULL) { + wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic " + "netlink cache"); + goto err3; + } + + drv->nl80211 = genl_ctrl_search_by_name(drv->nl_cache, "nl80211"); + if (drv->nl80211 == NULL) { + wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not " + "found"); + goto err4; + } + + ret = nl_get_multicast_id(drv, "nl80211", "scan"); + if (ret >= 0) + ret = nl_socket_add_membership(drv->nl_handle, ret); + if (ret < 0) { + wpa_printf(MSG_ERROR, "nl80211: Could not add multicast " + "membership for scan events: %d (%s)", + ret, strerror(-ret)); + goto err4; + } + eloop_register_read_sock(nl_socket_get_fd(drv->nl_handle), + wpa_driver_nl80211_event_receive, drv, ctx); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + goto err5; + } + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + goto err6; + } + + os_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); + goto err6; + } + + eloop_register_read_sock(s, wpa_driver_nl80211_event_receive_wext, drv, + ctx); + drv->wext_event_sock = s; + + wpa_driver_nl80211_finish_drv_init(drv); + + return drv; + +err6: + close(drv->ioctl_sock); +err5: + genl_family_put(drv->nl80211); +err4: + nl_cache_free(drv->nl_cache); +err3: + nl_handle_destroy(drv->nl_handle); +err2: + nl_cb_put(drv->nl_cb); +err1: + os_free(drv); + return NULL; +} + + +static void +wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv) +{ + int flags; + + drv->ifindex = if_nametoindex(drv->ifname); + + if (wpa_driver_nl80211_set_mode(drv, 0) < 0) { + printf("Could not configure driver to use managed mode\n"); + } + + if (wpa_driver_nl80211_get_ifflags(drv, &flags) != 0) + printf("Could not get interface '%s' flags\n", drv->ifname); + else if (!(flags & IFF_UP)) { + if (wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP) != 0) { + printf("Could not set interface '%s' UP\n", + drv->ifname); + } + } + + /* + * Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_nl80211_flush_pmkid(drv); + + wpa_driver_nl80211_get_range(drv); + + wpa_driver_nl80211_send_oper_ifla(drv, 1, IF_OPER_DORMANT); +} + + +/** + * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface + * @priv: Pointer to private nl80211 data from wpa_driver_nl80211_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_nl80211_init(). + */ +static void wpa_driver_nl80211_deinit(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + int flags; + +#ifdef CONFIG_CLIENT_MLME + if (drv->monitor_sock >= 0) { + eloop_unregister_read_sock(drv->monitor_sock); + close(drv->monitor_sock); + } + if (drv->monitor_ifidx > 0) + nl80211_remove_iface(drv, drv->monitor_ifidx); + if (drv->capa.flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME) + wpa_driver_nl80211_set_userspace_mlme(drv, 0); +#endif /* CONFIG_CLIENT_MLME */ + + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + + /* + * Clear possibly configured driver parameters in order to make it + * easier to use the driver after wpa_supplicant has been terminated. + */ + (void) wpa_driver_nl80211_set_bssid(drv, + (u8 *) "\x00\x00\x00\x00\x00\x00"); + + wpa_driver_nl80211_send_oper_ifla(priv, 0, IF_OPER_UP); + + eloop_unregister_read_sock(drv->wext_event_sock); + + if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) + (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); + + close(drv->wext_event_sock); + close(drv->ioctl_sock); + os_free(drv->assoc_req_ies); + os_free(drv->assoc_resp_ies); + + eloop_unregister_read_sock(nl_socket_get_fd(drv->nl_handle)); + genl_family_put(drv->nl80211); + nl_cache_free(drv->nl_cache); + nl_handle_destroy(drv->nl_handle); + nl_cb_put(drv->nl_cb); + + os_free(drv); +} + + +/** + * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Unused + * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +/** + * wpa_driver_nl80211_scan - Request the driver to initiate scan + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for + * all SSIDs (either active scan with broadcast SSID or passive + * scan + * @ssid_len: Length of the SSID + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = 0, timeout; + struct nl_msg *msg, *ssids; + + msg = nlmsg_alloc(); + ssids = nlmsg_alloc(); + if (!msg || !ssids) { + nlmsg_free(msg); + nlmsg_free(ssids); + return -1; + } + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, 0, + NL80211_CMD_TRIGGER_SCAN, 0); + + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + if (ssid && ssid_len) { + /* Request an active scan for a specific SSID */ + NLA_PUT(ssids, 1, ssid_len, ssid); + } else { + /* Request an active scan for wildcard SSID */ + NLA_PUT(ssids, 1, 0, ""); + } + nla_put_nested(msg, NL80211_ATTR_SCAN_SSIDS, ssids); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + msg = NULL; + if (ret) { + wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d " + "(%s)", ret, strerror(-ret)); + goto nla_put_failure; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 10; + if (drv->scan_complete_events) { + /* + * The driver seems to deliver SIOCGIWSCAN events to notify + * when scan is complete, so use longer timeout to avoid race + * conditions with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout, + drv, drv->ctx); + +nla_put_failure: + nlmsg_free(ssids); + nlmsg_free(msg); + return ret; +} + + +static int bss_info_handler(struct nl_msg *msg, void *arg) +{ + struct nlattr *tb[NL80211_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *bss[NL80211_BSS_MAX + 1]; + static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = { + [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC }, + [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 }, + [NL80211_BSS_TSF] = { .type = NLA_U64 }, + [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 }, + [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 }, + [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC }, + [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 }, + [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 }, + }; + struct wpa_scan_results *res = arg; + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + const u8 *ie; + size_t ie_len; + + nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[NL80211_ATTR_BSS]) + return NL_SKIP; + if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS], + bss_policy)) + return NL_SKIP; + if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) { + ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]); + } else { + ie = NULL; + ie_len = 0; + } + + r = os_zalloc(sizeof(*r) + ie_len); + if (r == NULL) + return NL_SKIP; + if (bss[NL80211_BSS_BSSID]) + os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]), + ETH_ALEN); + if (bss[NL80211_BSS_FREQUENCY]) + r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]); + if (bss[NL80211_BSS_BEACON_INTERVAL]) + r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]); + if (bss[NL80211_BSS_CAPABILITY]) + r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]); + if (bss[NL80211_BSS_SIGNAL_UNSPEC]) + r->qual = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]); + if (bss[NL80211_BSS_SIGNAL_MBM]) + r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]); + if (bss[NL80211_BSS_TSF]) + r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]); + r->ie_len = ie_len; + if (ie) + os_memcpy(r + 1, ie, ie_len); + + tmp = os_realloc(res->res, + (res->num + 1) * sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return NL_SKIP; + } + tmp[res->num++] = r; + res->res = tmp; + + return NL_SKIP; +} + + +/** + * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * Returns: Scan results on success, -1 on failure + */ +static struct wpa_scan_results * +wpa_driver_nl80211_get_scan_results(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + struct wpa_scan_results *res; + int ret; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return 0; + msg = nlmsg_alloc(); + if (!msg) + goto nla_put_failure; + + genlmsg_put(msg, 0, 0, genl_family_get_id(drv->nl80211), 0, NLM_F_DUMP, + NL80211_CMD_GET_SCAN, 0); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + ret = send_and_recv_msgs(drv, msg, bss_info_handler, res); + msg = NULL; + if (ret == 0) { + wpa_printf(MSG_DEBUG, "Received scan results (%lu BSSes)", + (unsigned long) res->num); + return res; + } + wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d " + "(%s)", ret, strerror(-ret)); +nla_put_failure: + nlmsg_free(msg); + wpa_scan_results_free(res); + return NULL; +} + + +static int wpa_driver_nl80211_get_range(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + /* + * 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; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, 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]"); + os_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->has_capability = 1; + drv->we_version_compiled = range->we_version_compiled; + if (range->enc_capa & IW_ENC_CAPA_WPA) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + if (range->enc_capa & IW_ENC_CAPA_WPA2) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + } + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x", + drv->capa.key_mgmt, drv->capa.enc); + } else { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " + "assuming WPA is not supported"); + } + + os_free(range); + return 0; +} + + +static int wpa_driver_nl80211_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_WPA_ENABLED, + enabled); +} + + +static int wpa_driver_nl80211_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, + size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + int err; + struct nl_msg *msg; + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d " + "seq_len=%lu key_len=%lu", + __func__, alg, addr, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + msg = nlmsg_alloc(); + if (msg == NULL) + return -1; + + if (alg == WPA_ALG_NONE) { + 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); + switch (alg) { + case WPA_ALG_WEP: + if (key_len == 5) + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC01); + else + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, + 0x000FAC05); + break; + case WPA_ALG_TKIP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC02); + break; + case WPA_ALG_CCMP: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC04); + break; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER, 0x000FAC06); + break; +#endif /* CONFIG_IEEE80211W */ + default: + nlmsg_free(msg); + return -1; + } + } + + if (addr && os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) + { + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + } + NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + + err = send_and_recv_msgs(drv, msg, NULL, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d", err); + return -1; + } + + if (set_tx && alg != WPA_ALG_NONE) { + msg = nlmsg_alloc(); + if (msg == NULL) + return -1; + + 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, key_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex); + NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT); + + err = send_and_recv_msgs(drv, msg, NULL, NULL); + if (err) { + wpa_printf(MSG_DEBUG, "nl80211: set default key " + "failed; err=%d", err); + return -1; + } + } + + return 0; + +nla_put_failure: + return -ENOBUFS; +} + + +static int wpa_driver_nl80211_set_countermeasures(void *priv, + int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_TKIP_COUNTERMEASURES, + enabled); +} + + +static int wpa_driver_nl80211_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->use_crypt = enabled; + return wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct iwreq iwr; + struct iw_mlme mlme; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.cmd = cmd; + mlme.reason_code = reason_code; + mlme.addr.sa_family = ARPHRD_ETHER; + os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); + iwr.u.data.pointer = (caddr_t) &mlme; + iwr.u.data.length = sizeof(mlme); + + if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { + perror("ioctl[SIOCSIWMLME]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); +} + + +static int wpa_driver_nl80211_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_nl80211_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_nl80211_mlme(drv, addr, IW_MLME_DISASSOC, + reason_code); +} + + +static int wpa_driver_nl80211_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_cipher2wext(int cipher) +{ + switch (cipher) { + case CIPHER_NONE: + return IW_AUTH_CIPHER_NONE; + case CIPHER_WEP40: + return IW_AUTH_CIPHER_WEP40; + case CIPHER_TKIP: + return IW_AUTH_CIPHER_TKIP; + case CIPHER_CCMP: + return IW_AUTH_CIPHER_CCMP; + case CIPHER_WEP104: + return IW_AUTH_CIPHER_WEP104; + default: + return 0; + } +} + + +static int wpa_driver_nl80211_keymgmt2wext(int keymgmt) +{ + switch (keymgmt) { + case KEY_MGMT_802_1X: + case KEY_MGMT_802_1X_NO_WPA: + return IW_AUTH_KEY_MGMT_802_1X; + case KEY_MGMT_PSK: + return IW_AUTH_KEY_MGMT_PSK; + default: + return 0; + } +} + + +static int +wpa_driver_nl80211_auth_alg_fallback(struct wpa_driver_nl80211_data *drv, + struct wpa_driver_associate_params *params) +{ + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " + "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* Just changing mode, not actual keys */ + iwr.u.encoding.flags = 0; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + + /* + * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two + * different things. Here they are used to indicate Open System vs. + * Shared Key authentication algorithm. However, some drivers may use + * them to select between open/restricted WEP encrypted (open = allow + * both unencrypted and encrypted frames; restricted = only allow + * encrypted frames). + */ + + if (!drv->use_crypt) { + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + } else { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + iwr.u.encoding.flags |= IW_ENCODE_OPEN; + if (params->auth_alg & AUTH_ALG_SHARED_KEY) + iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + int value; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * If the driver did not support SIOCSIWAUTH, fallback to + * SIOCSIWENCODE here. + */ + if (drv->auth_alg_fallback && + wpa_driver_nl80211_auth_alg_fallback(drv, params) < 0) + ret = -1; + + if (!params->bssid && + wpa_driver_nl80211_set_bssid(drv, NULL) < 0) + ret = -1; + + /* TODO: should consider getting wpa version and cipher/key_mgmt suites + * from configuration, not from here, where only the selected suite is + * available */ + if (wpa_driver_nl80211_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + < 0) + ret = -1; + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_nl80211_cipher2wext(params->pairwise_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_nl80211_cipher2wext(params->group_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_nl80211_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + value = params->key_mgmt_suite != KEY_MGMT_NONE || + params->pairwise_suite != CIPHER_NONE || + params->group_suite != CIPHER_NONE || + params->wpa_ie_len; + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_PRIVACY_INVOKED, value) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (wpa_driver_nl80211_set_auth_param(drv, + IW_AUTH_RX_UNENCRYPTED_EAPOL, + allow_unencrypted_eapol) < 0) + ret = -1; + if (params->freq && wpa_driver_nl80211_set_freq(drv, params->freq) < 0) + ret = -1; + if (wpa_driver_nl80211_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + if (params->bssid && + wpa_driver_nl80211_set_bssid(drv, params->bssid) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_nl80211_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_nl80211_data *drv = priv; + int algs = 0, res; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= IW_AUTH_ALG_OPEN_SYSTEM; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= IW_AUTH_ALG_SHARED_KEY; + if (auth_alg & AUTH_ALG_LEAP) + algs |= IW_AUTH_ALG_LEAP; + if (algs == 0) { + /* at least one algorithm should be set */ + algs = IW_AUTH_ALG_OPEN_SYSTEM; + } + + res = wpa_driver_nl80211_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, + algs); + drv->auth_alg_fallback = res == -2; + return res; +} + + +/** + * wpa_driver_nl80211_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE + * @priv: Pointer to private wext data from wpa_driver_nl80211_init() + * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS + * Returns: 0 on success, -1 on failure + */ +static int wpa_driver_nl80211_set_mode(void *priv, int mode) +{ + struct wpa_driver_nl80211_data *drv = priv; + int ret = -1, flags; + 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, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, + mode ? NL80211_IFTYPE_ADHOC : NL80211_IFTYPE_STATION); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (!ret) + return 0; + else + goto try_again; + +nla_put_failure: + wpa_printf(MSG_ERROR, "nl80211: Failed to set interface mode: %d (%s)", + ret, strerror(-ret)); + return -1; + +try_again: + /* mac80211 doesn't allow mode changes while the device is up, so + * take the device down, try to set the mode again, and bring the + * device back up. + */ + if (wpa_driver_nl80211_get_ifflags(drv, &flags) == 0) { + (void) wpa_driver_nl80211_set_ifflags(drv, flags & ~IFF_UP); + + /* Try to set the mode again while the interface is down */ + 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, drv->ifindex); + NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, + mode ? NL80211_IFTYPE_ADHOC : + NL80211_IFTYPE_STATION); + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + if (ret) { + wpa_printf(MSG_ERROR, "Failed to set interface %s " + "mode(try_again): %d (%s)", + drv->ifname, ret, strerror(-ret)); + } + + /* Ignore return value of get_ifflags to ensure that the device + * is always up like it was before this function was called. + */ + (void) wpa_driver_nl80211_get_ifflags(drv, &flags); + (void) wpa_driver_nl80211_set_ifflags(drv, flags | IFF_UP); + } + + return ret; +} + + +static int wpa_driver_nl80211_pmksa(struct wpa_driver_nl80211_data *drv, + u32 cmd, const u8 *bssid, const u8 *pmkid) +{ + struct iwreq iwr; + struct iw_pmksa pmksa; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&pmksa, 0, sizeof(pmksa)); + pmksa.cmd = cmd; + pmksa.bssid.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); + if (pmkid) + os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); + iwr.u.data.pointer = (caddr_t) &pmksa; + iwr.u.data.length = sizeof(pmksa); + + if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { + if (errno != EOPNOTSUPP) + perror("ioctl[SIOCSIWPMKSA]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_nl80211_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); +} + + +static int wpa_driver_nl80211_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); +} + + +static int wpa_driver_nl80211_flush_pmkid(void *priv) +{ + struct wpa_driver_nl80211_data *drv = priv; + return wpa_driver_nl80211_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); +} + + +static int wpa_driver_nl80211_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct wpa_driver_nl80211_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +static int wpa_driver_nl80211_set_operstate(void *priv, int state) +{ + struct wpa_driver_nl80211_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return wpa_driver_nl80211_send_oper_ifla( + drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +#ifdef CONFIG_CLIENT_MLME +static int wpa_driver_nl80211_open_mlme(struct wpa_driver_nl80211_data *drv) +{ + if (wpa_driver_nl80211_set_userspace_mlme(drv, 1) < 0) { + wpa_printf(MSG_ERROR, "nl80211: Failed to enable userspace " + "MLME"); + return -1; + } + if (wpa_driver_nl80211_create_monitor_interface(drv)) { + wpa_printf(MSG_ERROR, "nl80211: Failed to create monitor " + "interface"); + return -1; + } + return 0; +} +#endif /* CONFIG_CLIENT_MLME */ + + +static int wpa_driver_nl80211_set_param(void *priv, const char *param) +{ +#ifdef CONFIG_CLIENT_MLME + struct wpa_driver_nl80211_data *drv = priv; + + if (param == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + + if (os_strstr(param, "use_mlme=1")) { + wpa_printf(MSG_DEBUG, "nl80211: Using user space MLME"); + drv->capa.flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + + if (wpa_driver_nl80211_open_mlme(drv)) + return -1; + } +#endif /* CONFIG_CLIENT_MLME */ + + return 0; +} + + +#ifdef CONFIG_CLIENT_MLME + +struct phy_info_arg { + u16 *num_modes; + struct wpa_hw_modes *modes; +}; + + +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 wpa_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 = os_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)]; + os_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 = os_zalloc(mode->num_channels * + sizeof(struct wpa_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 |= WPA_CHAN_W_SCAN | + WPA_CHAN_W_ACTIVE_SCAN | + WPA_CHAN_W_IBSS; + + if (!mode_is_set) { + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) + mode->mode = WPA_MODE_IEEE80211B; + else + mode->mode = WPA_MODE_IEEE80211A; + mode_is_set = 1; + } + + /* crude heuristic */ + if (mode->channels[idx].freq < 4000) { + if (mode->channels[idx].freq == 2484) + 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 &= ~WPA_CHAN_W_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN]) + mode->channels[idx].flag &= + ~WPA_CHAN_W_ACTIVE_SCAN; + if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS]) + mode->channels[idx].flag &= ~WPA_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 = os_zalloc(mode->num_rates * + sizeof(struct wpa_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 == WPA_MODE_IEEE80211B && + mode->rates[idx].rate > 200) + mode->mode = WPA_MODE_IEEE80211G; + + if (tb_rate[NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE]) + mode->rates[idx].flags |= WPA_RATE_PREAMBLE2; + + idx++; + } + } + + return NL_SKIP; +} + + +static struct wpa_hw_modes * +wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + struct phy_info_arg result = { + .num_modes = num_modes, + .modes = NULL, + }; + + *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, drv->ifindex); + + if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) + return result.modes; +nla_put_failure: + return NULL; +} + + +static int wpa_driver_nl80211_set_channel(void *priv, wpa_hw_mode phymode, + int chan, int freq) +{ + return wpa_driver_nl80211_set_freq(priv, freq); +} + + +static int wpa_driver_nl80211_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + __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 iovec iov[2] = { + { + .iov_base = &rtap_hdr, + .iov_len = sizeof(rtap_hdr), + }, + { + .iov_base = (void *) data, + .iov_len = data_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, + }; + + if (sendmsg(drv->monitor_sock, &msg, 0) < 0) { + perror("send[MLME]"); + return -1; + } + + return 0; +} + + +static int wpa_driver_nl80211_mlme_add_sta(void *priv, const u8 *addr, + const u8 *supp_rates, + size_t supp_rates_len) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + 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, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + /* TODO: Get proper Association ID and listen interval */ + NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1); + NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, supp_rates_len, + supp_rates); + NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL, 1); + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + /* ignore EEXIST, this happens if a STA associates while associated */ + if (ret == -EEXIST || ret >= 0) + ret = 0; + +nla_put_failure: + return ret; +} + + +static int wpa_driver_nl80211_mlme_remove_sta(void *priv, const u8 *addr) +{ + struct wpa_driver_nl80211_data *drv = priv; + struct nl_msg *msg; + int ret = -1; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + + 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, drv->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr); + + ret = 0; + + ret = send_and_recv_msgs(drv, msg, NULL, NULL); + return ret; + +nla_put_failure: + return -ENOBUFS; +} + +#endif /* CONFIG_CLIENT_MLME */ + + +const struct wpa_driver_ops wpa_driver_nl80211_ops = { + .name = "nl80211", + .desc = "Linux nl80211/cfg80211", + .get_bssid = wpa_driver_nl80211_get_bssid, + .get_ssid = wpa_driver_nl80211_get_ssid, + .set_wpa = wpa_driver_nl80211_set_wpa, + .set_key = wpa_driver_nl80211_set_key, + .set_countermeasures = wpa_driver_nl80211_set_countermeasures, + .set_drop_unencrypted = wpa_driver_nl80211_set_drop_unencrypted, + .scan = wpa_driver_nl80211_scan, + .get_scan_results2 = wpa_driver_nl80211_get_scan_results, + .deauthenticate = wpa_driver_nl80211_deauthenticate, + .disassociate = wpa_driver_nl80211_disassociate, + .set_mode = wpa_driver_nl80211_set_mode, + .associate = wpa_driver_nl80211_associate, + .set_auth_alg = wpa_driver_nl80211_set_auth_alg, + .init = wpa_driver_nl80211_init, + .deinit = wpa_driver_nl80211_deinit, + .set_param = wpa_driver_nl80211_set_param, + .add_pmkid = wpa_driver_nl80211_add_pmkid, + .remove_pmkid = wpa_driver_nl80211_remove_pmkid, + .flush_pmkid = wpa_driver_nl80211_flush_pmkid, + .get_capa = wpa_driver_nl80211_get_capa, + .set_operstate = wpa_driver_nl80211_set_operstate, + .set_country = wpa_driver_nl80211_set_country, + .set_probe_req_ie = wpa_driver_nl80211_set_probe_req_ie, +#ifdef CONFIG_CLIENT_MLME + .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data, + .set_channel = wpa_driver_nl80211_set_channel, + .set_ssid = wpa_driver_nl80211_set_ssid, + .set_bssid = wpa_driver_nl80211_set_bssid, + .send_mlme = wpa_driver_nl80211_send_mlme, + .mlme_add_sta = wpa_driver_nl80211_mlme_add_sta, + .mlme_remove_sta = wpa_driver_nl80211_mlme_remove_sta, +#endif /* CONFIG_CLIENT_MLME */ +}; diff --git a/contrib/wpa/src/drivers/driver_osx.m b/contrib/wpa/src/drivers/driver_osx.m new file mode 100644 index 0000000..93d7df0 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_osx.m @@ -0,0 +1,432 @@ +/* + * WPA Supplicant - Mac OS X Apple80211 driver interface + * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * + * 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" +#define Boolean __DummyBoolean +#include <CoreFoundation/CoreFoundation.h> +#undef Boolean + +#include "common.h" +#include "driver.h" +#include "eloop.h" + +#include "Apple80211.h" + +struct wpa_driver_osx_data { + void *ctx; + WirelessRef wireless_ctx; + CFArrayRef scan_results; +}; + + +#ifndef CONFIG_NO_STDOUT_DEBUG +extern int wpa_debug_level; + +static void dump_dict_cb(const void *key, const void *value, void *context) +{ + if (MSG_DEBUG < wpa_debug_level) + return; + + wpa_printf(MSG_DEBUG, "Key:"); + CFShow(key); + wpa_printf(MSG_DEBUG, "Value:"); + CFShow(value); +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +static void wpa_driver_osx_dump_dict(CFDictionaryRef dict, const char *title) +{ +#ifndef CONFIG_NO_STDOUT_DEBUG + wpa_printf(MSG_DEBUG, "OSX: Dump dictionary %s - %u entries", + title, (unsigned int) CFDictionaryGetCount(dict)); + CFDictionaryApplyFunction(dict, dump_dict_cb, NULL); +#endif /* CONFIG_NO_STDOUT_DEBUG */ +} + + +static int wpa_driver_osx_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + WirelessInfo info; + int len; + + err = WirelessGetInfo(drv->wireless_ctx, &info); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", + (int) err); + return -1; + } + if (!info.power) { + wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); + return -1; + } + + for (len = 0; len < 32; len++) + if (info.ssid[len] == 0) + break; + + os_memcpy(ssid, info.ssid, len); + return len; +} + + +static int wpa_driver_osx_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + WirelessInfo info; + + err = WirelessGetInfo(drv->wireless_ctx, &info); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessGetInfo failed: %d", + (int) err); + return -1; + } + if (!info.power) { + wpa_printf(MSG_DEBUG, "OSX: Wireless device power off"); + return -1; + } + + os_memcpy(bssid, info.bssID, ETH_ALEN); + return 0; +} + + +static void wpa_driver_osx_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +static int wpa_driver_osx_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + if (drv->scan_results) { + CFRelease(drv->scan_results); + drv->scan_results = NULL; + } + + if (ssid) { + CFStringRef data; + data = CFStringCreateWithBytes(kCFAllocatorDefault, + ssid, ssid_len, + kCFStringEncodingISOLatin1, + FALSE); + if (data == NULL) { + wpa_printf(MSG_DEBUG, "CFStringCreateWithBytes " + "failed"); + return -1; + } + + err = WirelessDirectedScan(drv->wireless_ctx, + &drv->scan_results, 0, data); + CFRelease(data); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessDirectedScan " + "failed: 0x%08x", (unsigned int) err); + return -1; + } + } else { + err = WirelessScan(drv->wireless_ctx, &drv->scan_results, 0); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessScan failed: " + "0x%08x", (unsigned int) err); + return -1; + } + } + + eloop_register_timeout(0, 0, wpa_driver_osx_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static int wpa_driver_osx_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_osx_data *drv = priv; + size_t i, num; + + if (drv->scan_results == NULL) + return 0; + + num = CFArrayGetCount(drv->scan_results); + if (num > max_size) + num = max_size; + os_memset(results, 0, num * sizeof(struct wpa_scan_result)); + + for (i = 0; i < num; i++) { + struct wpa_scan_result *res = &results[i]; + WirelessNetworkInfo *info; + info = (WirelessNetworkInfo *) + CFDataGetBytePtr(CFArrayGetValueAtIndex( + drv->scan_results, i)); + + os_memcpy(res->bssid, info->bssid, ETH_ALEN); + if (info->ssid_len > 32) { + wpa_printf(MSG_DEBUG, "OSX: Invalid SSID length %d in " + "scan results", (int) info->ssid_len); + continue; + } + os_memcpy(res->ssid, info->ssid, info->ssid_len); + res->ssid_len = info->ssid_len; + res->caps = info->capability; + res->freq = 2407 + info->channel * 5; + res->level = info->signal; + res->noise = info->noise; + } + + return num; +} + + +static void wpa_driver_osx_assoc_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_osx_data *drv = eloop_ctx; + u8 bssid[ETH_ALEN]; + CFDictionaryRef ai; + + if (wpa_driver_osx_get_bssid(drv, bssid) != 0) { + eloop_register_timeout(1, 0, wpa_driver_osx_assoc_timeout, + drv, drv->ctx); + return; + } + + ai = WirelessGetAssociationInfo(drv->wireless_ctx); + if (ai) { + wpa_driver_osx_dump_dict(ai, "WirelessGetAssociationInfo"); + CFRelease(ai); + } else { + wpa_printf(MSG_DEBUG, "OSX: Failed to get association info"); + } + + wpa_supplicant_event(timeout_ctx, EVENT_ASSOC, NULL); +} + + +static int wpa_driver_osx_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + CFDataRef ssid; + CFStringRef key; + int assoc_type; + + ssid = CFDataCreate(kCFAllocatorDefault, params->ssid, + params->ssid_len); + if (ssid == NULL) + return -1; + + /* TODO: support for WEP */ + if (params->key_mgmt_suite == KEY_MGMT_PSK) { + if (params->passphrase == NULL) + return -1; + key = CFStringCreateWithCString(kCFAllocatorDefault, + params->passphrase, + kCFStringEncodingISOLatin1); + if (key == NULL) { + CFRelease(ssid); + return -1; + } + } else + key = NULL; + + if (params->key_mgmt_suite == KEY_MGMT_NONE) + assoc_type = 0; + else + assoc_type = 4; + + wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate(type=%d key=%p)", + assoc_type, key); + err = WirelessAssociate(drv->wireless_ctx, assoc_type, ssid, key); + CFRelease(ssid); + if (key) + CFRelease(key); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessAssociate failed: 0x%08x", + (unsigned int) err); + return -1; + } + + /* + * Driver is actually already associated; report association from an + * eloop callback. + */ + eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); + eloop_register_timeout(0, 0, wpa_driver_osx_assoc_timeout, drv, + drv->ctx); + + return 0; +} + + +static int wpa_driver_osx_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, + size_t key_len) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + if (alg == WPA_ALG_WEP) { + err = WirelessSetKey(drv->wireless_ctx, 1, key_idx, key_len, + key); + if (err != 0) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetKey failed: " + "0x%08x", (unsigned int) err); + return -1; + } + + return 0; + } + + if (alg == WPA_ALG_PMK) { + err = WirelessSetWPAKey(drv->wireless_ctx, 1, key_len, key); + if (err != 0) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetWPAKey failed: " + "0x%08x", (unsigned int) err); + return -1; + } + return 0; + } + + wpa_printf(MSG_DEBUG, "OSX: Unsupported set_key alg %d", alg); + return -1; +} + + +static int wpa_driver_osx_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + + return 0; +} + + +static void * wpa_driver_osx_init(void *ctx, const char *ifname) +{ + struct wpa_driver_osx_data *drv; + WirelessError err; + u8 enabled, power; + + if (!WirelessIsAvailable()) { + wpa_printf(MSG_ERROR, "OSX: No wireless interface available"); + return NULL; + } + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + err = WirelessAttach(&drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_ERROR, "OSX: WirelessAttach failed: %d", + (int) err); + os_free(drv); + return NULL; + } + + err = WirelessGetEnabled(drv->wireless_ctx, &enabled); + if (err) + wpa_printf(MSG_DEBUG, "OSX: WirelessGetEnabled failed: 0x%08x", + (unsigned int) err); + err = WirelessGetPower(drv->wireless_ctx, &power); + if (err) + wpa_printf(MSG_DEBUG, "OSX: WirelessGetPower failed: 0x%08x", + (unsigned int) err); + + wpa_printf(MSG_DEBUG, "OSX: Enabled=%d Power=%d", enabled, power); + + if (!enabled) { + err = WirelessSetEnabled(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetEnabled failed:" + " 0x%08x", (unsigned int) err); + WirelessDetach(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + if (!power) { + err = WirelessSetPower(drv->wireless_ctx, 1); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower failed: " + "0x%08x", (unsigned int) err); + WirelessDetach(drv->wireless_ctx); + os_free(drv); + return NULL; + } + } + + return drv; +} + + +static void wpa_driver_osx_deinit(void *priv) +{ + struct wpa_driver_osx_data *drv = priv; + WirelessError err; + + eloop_cancel_timeout(wpa_driver_osx_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_osx_assoc_timeout, drv, drv->ctx); + + err = WirelessSetPower(drv->wireless_ctx, 0); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessSetPower(0) failed: " + "0x%08x", (unsigned int) err); + } + + err = WirelessDetach(drv->wireless_ctx); + if (err) { + wpa_printf(MSG_DEBUG, "OSX: WirelessDetach failed: 0x%08x", + (unsigned int) err); + } + + if (drv->scan_results) + CFRelease(drv->scan_results); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_osx_ops = { + .name = "osx", + .desc = "Mac OS X Apple80211 driver", + .get_ssid = wpa_driver_osx_get_ssid, + .get_bssid = wpa_driver_osx_get_bssid, + .init = wpa_driver_osx_init, + .deinit = wpa_driver_osx_deinit, + .scan = wpa_driver_osx_scan, + .get_scan_results = wpa_driver_osx_get_scan_results, + .associate = wpa_driver_osx_associate, + .set_key = wpa_driver_osx_set_key, + .get_capa = wpa_driver_osx_get_capa, +}; diff --git a/contrib/wpa/src/drivers/driver_prism54.c b/contrib/wpa/src/drivers/driver_prism54.c new file mode 100644 index 0000000..e64e762 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_prism54.c @@ -0,0 +1,381 @@ +/* + * WPA Supplicant - driver interaction with Linux Prism54.org driver + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Luis R. Rodriguez <mcgrof@ruslug.rutgers.edu> + * + * 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 "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "driver_wext.h" +#include "driver_hostap.h" + +struct wpa_driver_prism54_data { + void *wext; /* private data for driver_wext */ + void *ctx; + char ifname[IFNAMSIZ + 1]; + int sock; +}; + +#define PRISM54_SET_WPA SIOCIWFIRSTPRIV+12 +#define PRISM54_HOSTAPD SIOCIWFIRSTPRIV+25 +#define PRISM54_DROP_UNENCRYPTED SIOCIWFIRSTPRIV+26 + +static void show_set_key_error(struct prism2_hostapd_param *); + +static int hostapd_ioctl_prism54(struct wpa_driver_prism54_data *drv, + struct prism2_hostapd_param *param, + int len, int show_err) +{ + struct iwreq iwr; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) param; + iwr.u.data.length = len; + + if (ioctl(drv->sock, PRISM54_HOSTAPD, &iwr) < 0) { + int ret = errno; + if (show_err) + perror("ioctl[PRISM54_HOSTAPD]"); + return ret; + } + + return 0; +} + + +static int wpa_driver_prism54_set_wpa_ie(struct wpa_driver_prism54_data *drv, + const u8 *wpa_ie, + size_t wpa_ie_len) +{ + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN + wpa_ie_len; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM2_HOSTAPD_SET_GENERIC_ELEMENT; + param->u.generic_elem.len = wpa_ie_len; + os_memcpy(param->u.generic_elem.data, wpa_ie, wpa_ie_len); + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +/* This is called at wpa_supplicant daemon init time */ +static int wpa_driver_prism54_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM54_SET_WPA; + param->u.generic_elem.len = 0; + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int wpa_driver_prism54_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + u8 *buf; + size_t blen; + int ret = 0; + char *alg_name; + + switch (alg) { + case WPA_ALG_NONE: + alg_name = "none"; + return -1; + break; + case WPA_ALG_WEP: + alg_name = "WEP"; + return -1; + break; + case WPA_ALG_TKIP: + alg_name = "TKIP"; + break; + case WPA_ALG_CCMP: + alg_name = "CCMP"; + return -1; + break; + default: + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: alg=%s key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg_name, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + if (seq_len > 8) + return -2; + + blen = sizeof(*param) + key_len; + buf = os_zalloc(blen); + if (buf == NULL) + return -1; + + param = (struct prism2_hostapd_param *) buf; + param->cmd = PRISM2_SET_ENCRYPTION; + /* TODO: In theory, STA in client mode can use five keys; four default + * keys for receiving (with keyidx 0..3) and one individual key for + * both transmitting and receiving (keyidx 0) _unicast_ packets. Now, + * keyidx 0 is reserved for this unicast use and default keys can only + * use keyidx 1..3 (i.e., default key with keyidx 0 is not supported). + * This should be fine for more or less all cases, but for completeness + * sake, the driver could be enhanced to support the missing key. */ +#if 0 + if (addr == NULL) + os_memset(param->sta_addr, 0xff, ETH_ALEN); + else + os_memcpy(param->sta_addr, addr, ETH_ALEN); +#else + os_memset(param->sta_addr, 0xff, ETH_ALEN); +#endif + os_strlcpy((char *) param->u.crypt.alg, alg_name, + HOSTAP_CRYPT_ALG_NAME_LEN); + param->u.crypt.flags = set_tx ? HOSTAP_CRYPT_FLAG_SET_TX_KEY : 0; + param->u.crypt.idx = key_idx; + os_memcpy(param->u.crypt.seq, seq, seq_len); + param->u.crypt.key_len = key_len; + os_memcpy((u8 *) (param + 1), key, key_len); + + if (hostapd_ioctl_prism54(drv, param, blen, 1)) { + wpa_printf(MSG_WARNING, "Failed to set encryption."); + show_set_key_error(param); + ret = -1; + } + os_free(buf); + + return ret; +} + + +static int wpa_driver_prism54_set_countermeasures(void *priv, + int enabled) +{ + /* FIX */ + printf("wpa_driver_prism54_set_countermeasures - not yet " + "implemented\n"); + return 0; +} + + +static int wpa_driver_prism54_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_prism54_data *drv = priv; + struct prism2_hostapd_param *param; + int res; + size_t blen = PRISM2_HOSTAPD_GENERIC_ELEMENT_HDR_LEN; + if (blen < sizeof(*param)) + blen = sizeof(*param); + + param = os_zalloc(blen); + if (param == NULL) + return -1; + + param->cmd = PRISM54_DROP_UNENCRYPTED; + param->u.generic_elem.len = 0; + res = hostapd_ioctl_prism54(drv, param, blen, 1); + + os_free(param); + + return res; +} + + +static int wpa_driver_prism54_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + /* FIX */ + printf("wpa_driver_prism54_deauthenticate - not yet implemented\n"); + return 0; +} + + +static int wpa_driver_prism54_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + /* FIX */ + printf("wpa_driver_prism54_disassociate - not yet implemented\n"); + return 0; +} + + +static int +wpa_driver_prism54_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_prism54_data *drv = priv; + int ret = 0; + + if (wpa_driver_prism54_set_wpa_ie(drv, params->wpa_ie, + params->wpa_ie_len) < 0) + ret = -1; + if (wpa_driver_wext_set_freq(drv->wext, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv->wext, params->ssid, + params->ssid_len) < 0) + ret = -1; + if (wpa_driver_wext_set_bssid(drv->wext, params->bssid) < 0) + ret = -1; + + return ret; +} + +static void show_set_key_error(struct prism2_hostapd_param *param) +{ + switch (param->u.crypt.err) { + case HOSTAP_CRYPT_ERR_UNKNOWN_ALG: + wpa_printf(MSG_INFO, "Unknown algorithm '%s'.", + param->u.crypt.alg); + wpa_printf(MSG_INFO, "You may need to load kernel module to " + "register that algorithm."); + wpa_printf(MSG_INFO, "E.g., 'modprobe hostap_crypt_wep' for " + "WEP."); + break; + case HOSTAP_CRYPT_ERR_UNKNOWN_ADDR: + wpa_printf(MSG_INFO, "Unknown address " MACSTR ".", + MAC2STR(param->sta_addr)); + break; + case HOSTAP_CRYPT_ERR_CRYPT_INIT_FAILED: + wpa_printf(MSG_INFO, "Crypt algorithm initialization failed."); + break; + case HOSTAP_CRYPT_ERR_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "Key setting failed."); + break; + case HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED: + wpa_printf(MSG_INFO, "TX key index setting failed."); + break; + case HOSTAP_CRYPT_ERR_CARD_CONF_FAILED: + wpa_printf(MSG_INFO, "Card configuration failed."); + break; + } +} + + +static int wpa_driver_prism54_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_bssid(drv->wext, bssid); +} + + +static int wpa_driver_prism54_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_ssid(drv->wext, ssid); +} + + +static int wpa_driver_prism54_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); +} + + +static struct wpa_scan_results * +wpa_driver_prism54_get_scan_results(void *priv) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_get_scan_results(drv->wext); +} + + +static int wpa_driver_prism54_set_operstate(void *priv, int state) +{ + struct wpa_driver_prism54_data *drv = priv; + return wpa_driver_wext_set_operstate(drv->wext, state); +} + + +static void * wpa_driver_prism54_init(void *ctx, const char *ifname) +{ + struct wpa_driver_prism54_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->wext = wpa_driver_wext_init(ctx, ifname); + if (drv->wext == NULL) { + os_free(drv); + return NULL; + } + + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->sock < 0) { + wpa_driver_wext_deinit(drv->wext); + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wpa_driver_prism54_deinit(void *priv) +{ + struct wpa_driver_prism54_data *drv = priv; + wpa_driver_wext_deinit(drv->wext); + close(drv->sock); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_prism54_ops = { + .name = "prism54", + .desc = "Prism54.org driver (Intersil Prism GT/Duette/Indigo)", + .get_bssid = wpa_driver_prism54_get_bssid, + .get_ssid = wpa_driver_prism54_get_ssid, + .set_wpa = wpa_driver_prism54_set_wpa, + .set_key = wpa_driver_prism54_set_key, + .set_countermeasures = wpa_driver_prism54_set_countermeasures, + .set_drop_unencrypted = wpa_driver_prism54_set_drop_unencrypted, + .scan = wpa_driver_prism54_scan, + .get_scan_results2 = wpa_driver_prism54_get_scan_results, + .deauthenticate = wpa_driver_prism54_deauthenticate, + .disassociate = wpa_driver_prism54_disassociate, + .associate = wpa_driver_prism54_associate, + .init = wpa_driver_prism54_init, + .deinit = wpa_driver_prism54_deinit, + .set_operstate = wpa_driver_prism54_set_operstate, +}; diff --git a/contrib/wpa/src/drivers/driver_privsep.c b/contrib/wpa/src/drivers/driver_privsep.c new file mode 100644 index 0000000..fdf299d --- /dev/null +++ b/contrib/wpa/src/drivers/driver_privsep.c @@ -0,0 +1,820 @@ +/* + * WPA Supplicant - privilege separated driver interface + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> + * + * 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/un.h> + +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "privsep_commands.h" + + +struct wpa_driver_privsep_data { + void *ctx; + u8 own_addr[ETH_ALEN]; + int priv_socket; + char *own_socket_path; + int cmd_socket; + char *own_cmd_path; + struct sockaddr_un priv_addr; + char ifname[16]; +}; + + +static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd) +{ + int res; + + res = sendto(drv->priv_socket, &cmd, sizeof(cmd), 0, + (struct sockaddr *) &drv->priv_addr, + sizeof(drv->priv_addr)); + if (res < 0) + perror("sendto"); + return res < 0 ? -1 : 0; +} + + +static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd, + const void *data, size_t data_len, + void *reply, size_t *reply_len) +{ + struct msghdr msg; + struct iovec io[2]; + + io[0].iov_base = &cmd; + io[0].iov_len = sizeof(cmd); + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = data ? 2 : 1; + msg.msg_name = &drv->priv_addr; + msg.msg_namelen = sizeof(drv->priv_addr); + + if (sendmsg(drv->cmd_socket, &msg, 0) < 0) { + perror("sendmsg(cmd_socket)"); + return -1; + } + + if (reply) { + fd_set rfds; + struct timeval tv; + int res; + + FD_ZERO(&rfds); + FD_SET(drv->cmd_socket, &rfds); + tv.tv_sec = 5; + tv.tv_usec = 0; + res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv); + if (res < 0 && errno != EINTR) { + perror("select"); + return -1; + } + + if (FD_ISSET(drv->cmd_socket, &rfds)) { + res = recv(drv->cmd_socket, reply, *reply_len, 0); + if (res < 0) { + perror("recv"); + return -1; + } + *reply_len = res; + } else { + wpa_printf(MSG_DEBUG, "PRIVSEP: Timeout while waiting " + "for reply (cmd=%d)", cmd); + return -1; + } + } + + return 0; +} + + +static int wpa_driver_privsep_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_WPA, &enabled, + sizeof(enabled), NULL, NULL); +} + + +static int wpa_driver_privsep_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SCAN, ssid, ssid_len, + NULL, NULL); +} + + +static struct wpa_scan_results * +wpa_driver_privsep_get_scan_results2(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + int res, num; + u8 *buf, *pos, *end; + size_t reply_len = 60000; + struct wpa_scan_results *results; + struct wpa_scan_res *r; + + buf = os_malloc(reply_len); + if (buf == NULL) + return NULL; + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SCAN_RESULTS, + NULL, 0, buf, &reply_len); + if (res < 0) { + os_free(buf); + return NULL; + } + + wpa_printf(MSG_DEBUG, "privsep: Received %lu bytes of scan results", + (unsigned long) reply_len); + if (reply_len < sizeof(int)) { + wpa_printf(MSG_DEBUG, "privsep: Invalid scan result len %lu", + (unsigned long) reply_len); + os_free(buf); + return NULL; + } + + pos = buf; + end = buf + reply_len; + os_memcpy(&num, pos, sizeof(int)); + if (num < 0 || num > 1000) { + os_free(buf); + return NULL; + } + pos += sizeof(int); + + results = os_zalloc(sizeof(*results)); + if (results == NULL) { + os_free(buf); + return NULL; + } + + results->res = os_zalloc(num * sizeof(struct wpa_scan_res *)); + if (results->res == NULL) { + os_free(results); + os_free(buf); + return NULL; + } + + while (results->num < (size_t) num && pos + sizeof(int) < end) { + int len; + os_memcpy(&len, pos, sizeof(int)); + pos += sizeof(int); + if (len < 0 || len > 10000 || pos + len > end) + break; + + r = os_malloc(len); + if (r == NULL) + break; + os_memcpy(r, pos, len); + pos += len; + if (sizeof(*r) + r->ie_len > (size_t) len) { + os_free(r); + break; + } + + results->res[results->num++] = r; + } + + os_free(buf); + return results; +} + + +static int wpa_driver_privsep_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_set_key cmd; + + wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", + __func__, priv, alg, key_idx, set_tx); + + os_memset(&cmd, 0, sizeof(cmd)); + cmd.alg = alg; + if (addr) + os_memcpy(cmd.addr, addr, ETH_ALEN); + else + os_memset(cmd.addr, 0xff, ETH_ALEN); + cmd.key_idx = key_idx; + cmd.set_tx = set_tx; + if (seq && seq_len > 0 && seq_len < sizeof(cmd.seq)) { + os_memcpy(cmd.seq, seq, seq_len); + cmd.seq_len = seq_len; + } + if (key && key_len > 0 && key_len < sizeof(cmd.key)) { + os_memcpy(cmd.key, key, key_len); + cmd.key_len = key_len; + } + + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_KEY, &cmd, sizeof(cmd), + NULL, NULL); +} + + +static int wpa_driver_privsep_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_privsep_data *drv = priv; + struct privsep_cmd_associate *data; + int res; + size_t buflen; + + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " + "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", + __func__, priv, params->freq, params->pairwise_suite, + params->group_suite, params->key_mgmt_suite, + params->auth_alg, params->mode); + + buflen = sizeof(*data) + params->wpa_ie_len; + data = os_zalloc(buflen); + if (data == NULL) + return -1; + + if (params->bssid) + os_memcpy(data->bssid, params->bssid, ETH_ALEN); + os_memcpy(data->ssid, params->ssid, params->ssid_len); + data->ssid_len = params->ssid_len; + data->freq = params->freq; + data->pairwise_suite = params->pairwise_suite; + data->group_suite = params->group_suite; + data->key_mgmt_suite = params->key_mgmt_suite; + data->auth_alg = params->auth_alg; + data->mode = params->mode; + data->wpa_ie_len = params->wpa_ie_len; + if (params->wpa_ie) + os_memcpy(data + 1, params->wpa_ie, params->wpa_ie_len); + /* TODO: add support for other assoc parameters */ + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_ASSOCIATE, data, buflen, + NULL, NULL); + os_free(data); + + return res; +} + + +static int wpa_driver_privsep_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_privsep_data *drv = priv; + int res; + size_t len = ETH_ALEN; + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_BSSID, NULL, 0, bssid, &len); + if (res < 0 || len != ETH_ALEN) + return -1; + return 0; +} + + +static int wpa_driver_privsep_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_privsep_data *drv = priv; + int res, ssid_len; + u8 reply[sizeof(int) + 32]; + size_t len = sizeof(reply); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_SSID, NULL, 0, reply, &len); + if (res < 0 || len < sizeof(int)) + return -1; + os_memcpy(&ssid_len, reply, sizeof(int)); + if (ssid_len < 0 || ssid_len > 32 || sizeof(int) + ssid_len > len) { + wpa_printf(MSG_DEBUG, "privsep: Invalid get SSID reply"); + return -1; + } + os_memcpy(ssid, &reply[sizeof(int)], ssid_len); + return ssid_len; +} + + +static int wpa_driver_privsep_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + //struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + wpa_printf(MSG_DEBUG, "%s - TODO", __func__); + return 0; +} + + +static int wpa_driver_privsep_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + //struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + wpa_printf(MSG_DEBUG, "%s - TODO", __func__); + return 0; +} + + +static void wpa_driver_privsep_event_assoc(void *ctx, wpa_event_type event, + u8 *buf, size_t len) +{ + union wpa_event_data data; + int inc_data = 0; + u8 *pos, *end; + int ie_len; + + os_memset(&data, 0, sizeof(data)); + + pos = buf; + end = buf + len; + + if (end - pos < (int) sizeof(int)) + return; + os_memcpy(&ie_len, pos, sizeof(int)); + pos += sizeof(int); + if (ie_len < 0 || ie_len > end - pos) + return; + if (ie_len) { + data.assoc_info.req_ies = pos; + data.assoc_info.req_ies_len = ie_len; + pos += ie_len; + inc_data = 1; + } + + wpa_supplicant_event(ctx, event, inc_data ? &data : NULL); +} + + +static void wpa_driver_privsep_event_interface_status(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + int ievent; + + if (len < sizeof(int) || + len - sizeof(int) > sizeof(data.interface_status.ifname)) + return; + + os_memcpy(&ievent, buf, sizeof(int)); + + os_memset(&data, 0, sizeof(data)); + data.interface_status.ievent = ievent; + os_memcpy(data.interface_status.ifname, buf + sizeof(int), + len - sizeof(int)); + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &data); +} + + +static void wpa_driver_privsep_event_michael_mic_failure( + void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + + if (len != sizeof(int)) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.michael_mic_failure.unicast, buf, sizeof(int)); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); +} + + +static void wpa_driver_privsep_event_pmkid_candidate(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + + if (len != sizeof(struct pmkid_candidate)) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.pmkid_candidate, buf, len); + wpa_supplicant_event(ctx, EVENT_PMKID_CANDIDATE, &data); +} + + +static void wpa_driver_privsep_event_stkstart(void *ctx, u8 *buf, size_t len) +{ + union wpa_event_data data; + + if (len != ETH_ALEN) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.stkstart.peer, buf, ETH_ALEN); + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +} + + +static void wpa_driver_privsep_event_ft_response(void *ctx, u8 *buf, + size_t len) +{ + union wpa_event_data data; + + if (len < sizeof(int) + ETH_ALEN) + return; + + os_memset(&data, 0, sizeof(data)); + os_memcpy(&data.ft_ies.ft_action, buf, sizeof(int)); + os_memcpy(data.ft_ies.target_ap, buf + sizeof(int), ETH_ALEN); + data.ft_ies.ies = buf + sizeof(int) + ETH_ALEN; + data.ft_ies.ies_len = len - sizeof(int) - ETH_ALEN; + wpa_supplicant_event(ctx, EVENT_FT_RESPONSE, &data); +} + + +static void wpa_driver_privsep_event_rx_eapol(void *ctx, u8 *buf, size_t len) +{ + if (len < ETH_ALEN) + return; + + wpa_supplicant_rx_eapol(ctx, buf, buf + ETH_ALEN, len - ETH_ALEN); +} + + +static void wpa_driver_privsep_event_sta_rx(void *ctx, u8 *buf, size_t len) +{ +#ifdef CONFIG_CLIENT_MLME + struct ieee80211_rx_status *rx_status; + + if (len < sizeof(*rx_status)) + return; + rx_status = (struct ieee80211_rx_status *) buf; + buf += sizeof(*rx_status); + len -= sizeof(*rx_status); + + wpa_supplicant_sta_rx(ctx, buf, len, rx_status); +#endif /* CONFIG_CLIENT_MLME */ +} + + +static void wpa_driver_privsep_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_privsep_data *drv = eloop_ctx; + u8 *buf, *event_buf; + size_t event_len; + int res, event; + enum privsep_event e; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + const size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + res = recvfrom(sock, buf, buflen, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(priv_socket)"); + os_free(buf); + return; + } + + wpa_printf(MSG_DEBUG, "privsep_driver: received %u bytes", res); + + if (res < (int) sizeof(int)) { + wpa_printf(MSG_DEBUG, "Too short event message (len=%d)", res); + return; + } + + os_memcpy(&event, buf, sizeof(int)); + event_buf = &buf[sizeof(int)]; + event_len = res - sizeof(int); + wpa_printf(MSG_DEBUG, "privsep: Event %d received (len=%lu)", + event, (unsigned long) event_len); + + e = event; + switch (e) { + case PRIVSEP_EVENT_SCAN_RESULTS: + wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, NULL); + break; + case PRIVSEP_EVENT_ASSOC: + wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOC, + event_buf, event_len); + break; + case PRIVSEP_EVENT_DISASSOC: + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + break; + case PRIVSEP_EVENT_ASSOCINFO: + wpa_driver_privsep_event_assoc(drv->ctx, EVENT_ASSOCINFO, + event_buf, event_len); + break; + case PRIVSEP_EVENT_MICHAEL_MIC_FAILURE: + wpa_driver_privsep_event_michael_mic_failure( + drv->ctx, event_buf, event_len); + break; + case PRIVSEP_EVENT_INTERFACE_STATUS: + wpa_driver_privsep_event_interface_status(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_PMKID_CANDIDATE: + wpa_driver_privsep_event_pmkid_candidate(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_STKSTART: + wpa_driver_privsep_event_stkstart(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_FT_RESPONSE: + wpa_driver_privsep_event_ft_response(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_RX_EAPOL: + wpa_driver_privsep_event_rx_eapol(drv->ctx, event_buf, + event_len); + break; + case PRIVSEP_EVENT_STA_RX: + wpa_driver_privsep_event_sta_rx(drv->ctx, event_buf, + event_len); + break; + } + + os_free(buf); +} + + +static void * wpa_driver_privsep_init(void *ctx, const char *ifname) +{ + struct wpa_driver_privsep_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + drv->priv_socket = -1; + drv->cmd_socket = -1; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + return drv; +} + + +static void wpa_driver_privsep_deinit(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + + if (drv->priv_socket >= 0) { + wpa_priv_reg_cmd(drv, PRIVSEP_CMD_UNREGISTER); + eloop_unregister_read_sock(drv->priv_socket); + close(drv->priv_socket); + } + + if (drv->own_socket_path) { + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + } + + if (drv->cmd_socket >= 0) { + eloop_unregister_read_sock(drv->cmd_socket); + close(drv->cmd_socket); + } + + if (drv->own_cmd_path) { + unlink(drv->own_cmd_path); + os_free(drv->own_cmd_path); + } + + os_free(drv); +} + + +static int wpa_driver_privsep_set_param(void *priv, const char *param) +{ + struct wpa_driver_privsep_data *drv = priv; + const char *pos; + char *own_dir, *priv_dir; + static unsigned int counter = 0; + size_t len; + struct sockaddr_un addr; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + if (param == NULL) + pos = NULL; + else + pos = os_strstr(param, "own_dir="); + if (pos) { + char *end; + own_dir = os_strdup(pos + 8); + if (own_dir == NULL) + return -1; + end = os_strchr(own_dir, ' '); + if (end) + *end = '\0'; + } else { + own_dir = os_strdup("/tmp"); + if (own_dir == NULL) + return -1; + } + + if (param == NULL) + pos = NULL; + else + pos = os_strstr(param, "priv_dir="); + if (pos) { + char *end; + priv_dir = os_strdup(pos + 9); + if (priv_dir == NULL) { + os_free(own_dir); + return -1; + } + end = os_strchr(priv_dir, ' '); + if (end) + *end = '\0'; + } else { + priv_dir = os_strdup("/var/run/wpa_priv"); + if (priv_dir == NULL) { + os_free(own_dir); + return -1; + } + } + + len = os_strlen(own_dir) + 50; + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path == NULL) { + os_free(priv_dir); + os_free(own_dir); + return -1; + } + os_snprintf(drv->own_socket_path, len, "%s/wpa_privsep-%d-%d", + own_dir, getpid(), counter++); + + len = os_strlen(own_dir) + 50; + drv->own_cmd_path = os_malloc(len); + if (drv->own_cmd_path == NULL) { + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + os_free(priv_dir); + os_free(own_dir); + return -1; + } + os_snprintf(drv->own_cmd_path, len, "%s/wpa_privsep-%d-%d", + own_dir, getpid(), counter++); + + os_free(own_dir); + + drv->priv_addr.sun_family = AF_UNIX; + os_snprintf(drv->priv_addr.sun_path, sizeof(drv->priv_addr.sun_path), + "%s/%s", priv_dir, drv->ifname); + os_free(priv_dir); + + drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->priv_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); + if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) < + 0) { + perror("bind(PF_UNIX)"); + close(drv->priv_socket); + drv->priv_socket = -1; + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + eloop_register_read_sock(drv->priv_socket, wpa_driver_privsep_receive, + drv, NULL); + + drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->cmd_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_cmd_path); + drv->own_cmd_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path)); + if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0) + { + perror("bind(PF_UNIX)"); + close(drv->cmd_socket); + drv->cmd_socket = -1; + unlink(drv->own_cmd_path); + os_free(drv->own_cmd_path); + drv->own_cmd_path = NULL; + return -1; + } + + if (wpa_priv_reg_cmd(drv, PRIVSEP_CMD_REGISTER) < 0) { + wpa_printf(MSG_ERROR, "Failed to register with wpa_priv"); + return -1; + } + + return 0; +} + + +static int wpa_driver_privsep_get_capa(void *priv, + struct wpa_driver_capa *capa) +{ + struct wpa_driver_privsep_data *drv = priv; + int res; + size_t len = sizeof(*capa); + + res = wpa_priv_cmd(drv, PRIVSEP_CMD_GET_CAPA, NULL, 0, capa, &len); + if (res < 0 || len != sizeof(*capa)) + return -1; + return 0; +} + + +static const u8 * wpa_driver_privsep_get_mac_addr(void *priv) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + return drv->own_addr; +} + + +static int wpa_driver_privsep_set_mode(void *priv, int mode) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s mode=%d", __func__, mode); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_MODE, &mode, sizeof(mode), + NULL, NULL); +} + + +static int wpa_driver_privsep_set_country(void *priv, const char *alpha2) +{ + struct wpa_driver_privsep_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s country='%s'", __func__, alpha2); + return wpa_priv_cmd(drv, PRIVSEP_CMD_SET_COUNTRY, alpha2, + os_strlen(alpha2), NULL, NULL); +} + + +struct wpa_driver_ops wpa_driver_privsep_ops = { + "privsep", + "wpa_supplicant privilege separated driver", + wpa_driver_privsep_get_bssid, + wpa_driver_privsep_get_ssid, + wpa_driver_privsep_set_wpa, + wpa_driver_privsep_set_key, + wpa_driver_privsep_init, + wpa_driver_privsep_deinit, + wpa_driver_privsep_set_param, + NULL /* set_countermeasures */, + NULL /* set_drop_unencrypted */, + wpa_driver_privsep_scan, + NULL /* get_scan_results */, + wpa_driver_privsep_deauthenticate, + wpa_driver_privsep_disassociate, + wpa_driver_privsep_associate, + NULL /* set_auth_alg */, + NULL /* add_pmkid */, + NULL /* remove_pmkid */, + NULL /* flush_pmkid */, + wpa_driver_privsep_get_capa, + NULL /* poll */, + NULL /* get_ifname */, + wpa_driver_privsep_get_mac_addr, + NULL /* send_eapol */, + NULL /* set_operstate */, + NULL /* mlme_setprotection */, + NULL /* get_hw_feature_data */, + NULL /* set_channel */, + NULL /* set_ssid */, + NULL /* set_bssid */, + NULL /* send_mlme */, + NULL /* mlme_add_sta */, + NULL /* mlme_remove_sta */, + NULL /* update_ft_ies */, + NULL /* send_ft_action */, + wpa_driver_privsep_get_scan_results2, + NULL /* set_probe_req_ie */, + wpa_driver_privsep_set_mode, + wpa_driver_privsep_set_country, + NULL /* global_init */, + NULL /* global_deinit */, + NULL /* init2 */, + NULL /* get_interfaces */ +}; + + +struct wpa_driver_ops *wpa_supplicant_drivers[] = +{ + &wpa_driver_privsep_ops, + NULL +}; diff --git a/contrib/wpa/src/drivers/driver_ps3.c b/contrib/wpa/src/drivers/driver_ps3.c new file mode 100644 index 0000000..fde3425 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_ps3.c @@ -0,0 +1,186 @@ +/* + * WPA Supplicant - PS3 Linux wireless extension driver interface + * Copyright 2007, 2008 Sony Corporation + * + * 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 "wireless_copy.h" +#include "common.h" +#include "wpa_common.h" +#include "driver.h" +#include "eloop.h" +#include "driver_wext.h" +#include "ieee802_11_defs.h" + +static int wpa_driver_ps3_set_wpa_key(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret, i; + struct iwreq iwr; + char *buf, *str; + + if (!params->psk && !params->passphrase) { + wpa_printf(MSG_INFO, "%s:no PSK error", __func__); + return -EINVAL; + } + + os_memset(&iwr, 0, sizeof(iwr)); + if (params->psk) { + /* includes null */ + iwr.u.data.length = PMK_LEN * 2 + 1; + buf = os_malloc(iwr.u.data.length); + if (!buf) + return -ENOMEM; + str = buf; + for (i = 0; i < PMK_LEN; i++) { + str += snprintf(str, iwr.u.data.length - (str - buf), + "%02x", params->psk[i]); + } + } else if (params->passphrase) { + /* including quotations and null */ + iwr.u.data.length = strlen(params->passphrase) + 3; + buf = os_malloc(iwr.u.data.length); + if (!buf) + return -ENOMEM; + buf[0] = '"'; + os_memcpy(buf + 1, params->passphrase, iwr.u.data.length - 3); + buf[iwr.u.data.length - 2] = '"'; + buf[iwr.u.data.length - 1] = '\0'; + } else + return -EINVAL; + iwr.u.data.pointer = (caddr_t) buf; + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + ret = ioctl(drv->ioctl_sock, SIOCIWFIRSTPRIV, &iwr); + os_free(buf); + + return ret; +} + +static int wpa_driver_ps3_set_wep_keys(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + int ret, i; + struct iwreq iwr; + + for (i = 0; i < 4; i++) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = i + 1; + if (params->wep_key_len[i]) { + iwr.u.encoding.pointer = (caddr_t) params->wep_key[i]; + iwr.u.encoding.length = params->wep_key_len[i]; + } else + iwr.u.encoding.flags = IW_ENCODE_NOKEY | + IW_ENCODE_DISABLED; + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + } + return ret; +} + +static int wpa_driver_ps3_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_wext_data *drv = priv; + int ret, value; + + wpa_printf(MSG_DEBUG, "%s: <-", __func__); + + /* clear BSSID */ + if (!params->bssid && + wpa_driver_wext_set_bssid(drv, NULL) < 0) + ret = -1; + + if (wpa_driver_wext_set_mode(drv, params->mode) < 0) + ret = -1; + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->pairwise_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->group_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + + /* set selected BSSID */ + if (params->bssid && + wpa_driver_wext_set_bssid(drv, params->bssid) < 0) + ret = -1; + + switch (params->group_suite) { + case CIPHER_NONE: + ret = 0; + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + ret = wpa_driver_ps3_set_wep_keys(drv, params); + break; + case CIPHER_TKIP: + case CIPHER_CCMP: + ret = wpa_driver_ps3_set_wpa_key(drv, params); + break; + } + + /* start to associate */ + ret = wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len); + + wpa_printf(MSG_DEBUG, "%s: ->", __func__); + + return ret; +} + +static int wpa_driver_ps3_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + int ret; + wpa_printf(MSG_DEBUG, "%s:<-", __func__); + + ret = wpa_driver_wext_get_capa(priv, capa); + if (ret) { + wpa_printf(MSG_INFO, "%s: base wext returns error %d", + __func__, ret); + return ret; + } + /* PS3 hypervisor does association and 4way handshake by itself */ + capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + wpa_printf(MSG_DEBUG, "%s:->", __func__); + return 0; +} + +const struct wpa_driver_ops wpa_driver_ps3_ops = { + .name = "ps3", + .desc = "PLAYSTATION3 Linux wireless extension driver", + .get_bssid = wpa_driver_wext_get_bssid, + .get_ssid = wpa_driver_wext_get_ssid, + .scan = wpa_driver_wext_scan, + .get_scan_results2 = wpa_driver_wext_get_scan_results, + .associate = wpa_driver_ps3_associate, /* PS3 */ + .init = wpa_driver_wext_init, + .deinit = wpa_driver_wext_deinit, + .get_capa = wpa_driver_ps3_get_capa, /* PS3 */ +}; diff --git a/contrib/wpa/src/drivers/driver_ralink.c b/contrib/wpa/src/drivers/driver_ralink.c new file mode 100644 index 0000000..e9313cb --- /dev/null +++ b/contrib/wpa/src/drivers/driver_ralink.c @@ -0,0 +1,1505 @@ +/* + * WPA Supplicant - driver interaction with Ralink Wireless Client + * Copyright (c) 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> + * + * 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 "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "l2_packet/l2_packet.h" +#include "eloop.h" +#include "ieee802_11_defs.h" +#include "priv_netlink.h" +#include "driver_ralink.h" + +static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx); + +#define MAX_SSID_LEN 32 + +struct wpa_driver_ralink_data { + void *ctx; + int ioctl_sock; + int event_sock; + char ifname[IFNAMSIZ + 1]; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + int no_of_pmkid; + struct ndis_pmkid_entry *pmkid; + int we_version_compiled; + int ap_scan; + int scanning_done; + u8 g_driver_down; +}; + +static int ralink_set_oid(struct wpa_driver_ralink_data *drv, + unsigned short oid, char *data, int len) +{ + char *buf; + struct iwreq iwr; + + buf = os_zalloc(len); + if (buf == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.flags = oid; + iwr.u.data.flags |= OID_GET_SET_TOGGLE; + + if (data) + os_memcpy(buf, data, len); + + iwr.u.data.pointer = (caddr_t) buf; + iwr.u.data.length = len; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: oid=0x%x len (%d) failed", + __func__, oid, len); + os_free(buf); + return -1; + } + os_free(buf); + return 0; +} + +static int +ralink_get_new_driver_flag(struct wpa_driver_ralink_data *drv) +{ + struct iwreq iwr; + UCHAR enabled = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (UCHAR*) &enabled; + iwr.u.data.flags = RT_OID_NEW_DRIVER; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed", __func__); + return 0; + } + + return (enabled == 1) ? 1 : 0; +} + +static int wpa_driver_ralink_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + +static int wpa_driver_ralink_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_ralink_data *drv = priv; +#if 0 + struct wpa_supplicant *wpa_s = drv->ctx; + struct wpa_ssid *entry; +#endif + int ssid_len; + u8 bssid[ETH_ALEN]; + u8 ssid_str[MAX_SSID_LEN]; + struct iwreq iwr; +#if 0 + int result = 0; +#endif + int ret = 0; +#if 0 + BOOLEAN ieee8021x_mode = FALSE; + BOOLEAN ieee8021x_required_key = FALSE; +#endif + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + if (ret <= 0) + return ret; + + ssid_len = ret; + os_memset(ssid_str, 0, MAX_SSID_LEN); + os_memcpy(ssid_str, ssid, ssid_len); + + if (drv->ap_scan == 0) { + /* Read BSSID form driver */ + if (wpa_driver_ralink_get_bssid(priv, bssid) < 0) { + wpa_printf(MSG_WARNING, "Could not read BSSID from " + "driver."); + return ret; + } + +#if 0 + entry = wpa_s->conf->ssid; + while (entry) { + if (!entry->disabled && ssid_len == entry->ssid_len && + os_memcmp(ssid_str, entry->ssid, ssid_len) == 0 && + (!entry->bssid_set || + os_memcmp(bssid, entry->bssid, ETH_ALEN) == 0)) { + /* match the config of driver */ + result = 1; + break; + } + entry = entry->next; + } + + if (result) { + wpa_printf(MSG_DEBUG, "Ready to set 802.1x mode and " + "ieee_required_keys parameters to driver"); + + /* set 802.1x mode and ieee_required_keys parameter */ + if (entry->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((entry->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) + ieee8021x_required_key = TRUE; + ieee8021x_mode = TRUE; + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, (char *) &ieee8021x_mode, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set OID_802_11_SET_IEEE8021X(%d)", (int) ieee8021x_mode); + } + else + { + wpa_printf(MSG_DEBUG, "ieee8021x_mode is %s", ieee8021x_mode ? "TRUE" : "FALSE"); + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", (int) ieee8021x_required_key); + } + else + { + wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d)", ieee8021x_required_key ? "TRUE" : "FALSE", + entry->eapol_flags); + } + } +#endif + } + + return ret; +} + +static int wpa_driver_ralink_set_ssid(struct wpa_driver_ralink_data *drv, + const u8 *ssid, size_t ssid_len) +{ + NDIS_802_11_SSID *buf; + int ret = 0; + struct iwreq iwr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + buf = os_zalloc(sizeof(NDIS_802_11_SSID)); + if (buf == NULL) + return -1; + os_memset(buf, 0, sizeof(buf)); + buf->SsidLength = ssid_len; + os_memcpy(buf->Ssid, ssid, ssid_len); + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + iwr.u.data.flags = OID_802_11_SSID; + iwr.u.data.flags |= OID_GET_SET_TOGGLE; + iwr.u.data.pointer = (caddr_t) buf; + iwr.u.data.length = sizeof(NDIS_802_11_SSID); + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + perror("ioctl[RT_PRIV_IOCTL] -- OID_802_11_SSID"); + ret = -1; + } + os_free(buf); + return ret; +} + +static void wpa_driver_ralink_event_pmkid(struct wpa_driver_ralink_data *drv, + const u8 *data, size_t data_len) +{ + NDIS_802_11_PMKID_CANDIDATE_LIST *pmkid; + size_t i; + union wpa_event_data event; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (data_len < 8) { + wpa_printf(MSG_DEBUG, "RALINK: Too short PMKID Candidate List " + "Event (len=%lu)", (unsigned long) data_len); + return; + } + pmkid = (NDIS_802_11_PMKID_CANDIDATE_LIST *) data; + wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List Event - Version %d" + " NumCandidates %d", + (int) pmkid->Version, (int) pmkid->NumCandidates); + + if (pmkid->Version != 1) { + wpa_printf(MSG_DEBUG, "RALINK: Unsupported PMKID Candidate " + "List Version %d", (int) pmkid->Version); + return; + } + + if (data_len < 8 + pmkid->NumCandidates * sizeof(PMKID_CANDIDATE)) { + wpa_printf(MSG_DEBUG, "RALINK: PMKID Candidate List " + "underflow"); + + return; + } + + + + os_memset(&event, 0, sizeof(event)); + for (i = 0; i < pmkid->NumCandidates; i++) { + PMKID_CANDIDATE *p = &pmkid->CandidateList[i]; + wpa_printf(MSG_DEBUG, "RALINK: %lu: " MACSTR " Flags 0x%x", + (unsigned long) i, MAC2STR(p->BSSID), + (int) p->Flags); + os_memcpy(event.pmkid_candidate.bssid, p->BSSID, ETH_ALEN); + event.pmkid_candidate.index = i; + event.pmkid_candidate.preauth = + p->Flags & NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, + &event); + } +} + +static int wpa_driver_ralink_set_pmkid(struct wpa_driver_ralink_data *drv) +{ + int len, count, i, ret; + struct ndis_pmkid_entry *entry; + NDIS_802_11_PMKID *p; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + count = 0; + entry = drv->pmkid; + while (entry) { + count++; + if (count >= drv->no_of_pmkid) + break; + entry = entry->next; + } + len = 8 + count * sizeof(BSSID_INFO); + p = os_zalloc(len); + if (p == NULL) + return -1; + p->Length = len; + p->BSSIDInfoCount = count; + entry = drv->pmkid; + for (i = 0; i < count; i++) { + os_memcpy(&p->BSSIDInfo[i].BSSID, entry->bssid, ETH_ALEN); + os_memcpy(&p->BSSIDInfo[i].PMKID, entry->pmkid, 16); + entry = entry->next; + } + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID", + (const u8 *) p, len); + ret = ralink_set_oid(drv, OID_802_11_PMKID, (char *) p, len); + os_free(p); + return ret; +} + +static int wpa_driver_ralink_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + prev = NULL; + entry = drv->pmkid; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0) + break; + prev = entry; + entry = entry->next; + } + + if (entry) { + /* Replace existing entry for this BSSID and move it into the + * beginning of the list. */ + os_memcpy(entry->pmkid, pmkid, 16); + if (prev) { + prev->next = entry->next; + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } else { + entry = os_malloc(sizeof(*entry)); + if (entry) { + os_memcpy(entry->bssid, bssid, ETH_ALEN); + os_memcpy(entry->pmkid, pmkid, 16); + entry->next = drv->pmkid; + drv->pmkid = entry; + } + } + + return wpa_driver_ralink_set_pmkid(drv); +} + + +static int wpa_driver_ralink_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ndis_pmkid_entry *entry, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + entry = drv->pmkid; + prev = NULL; + drv->pmkid = NULL; + while (entry) { + if (os_memcmp(entry->bssid, bssid, ETH_ALEN) == 0 && + os_memcmp(entry->pmkid, pmkid, 16) == 0) { + if (prev) + prev->next = entry->next; + else + drv->pmkid = entry->next; + os_free(entry); + break; + } + prev = entry; + entry = entry->next; + } + return wpa_driver_ralink_set_pmkid(drv); +} + + +static int wpa_driver_ralink_flush_pmkid(void *priv) +{ + struct wpa_driver_ralink_data *drv = priv; + NDIS_802_11_PMKID p; + struct ndis_pmkid_entry *pmkid, *prev; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->no_of_pmkid == 0) + return 0; + + pmkid = drv->pmkid; + drv->pmkid = NULL; + while (pmkid) { + prev = pmkid; + pmkid = pmkid->next; + os_free(prev); + } + + os_memset(&p, 0, sizeof(p)); + p.Length = 8; + p.BSSIDInfoCount = 0; + wpa_hexdump(MSG_MSGDUMP, "NDIS: OID_802_11_PMKID (flush)", + (const u8 *) &p, 8); + return ralink_set_oid(drv, OID_802_11_PMKID, (char *) &p, 8); +} + +static void +wpa_driver_ralink_event_wireless_custom(struct wpa_driver_ralink_data *drv, + void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + /* receive a MICFAILURE report */ + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO_ReqIEs=", 17) == 0) { + /* receive assoc. req. IEs */ + char *spos; + int bytes; + + spos = custom + 17; + /*get IE's length */ + /* + * bytes = strlen(spos); ==> bug, bytes may less than original + * size by using this way to get size. snowpin 20070312 + * if (!bytes) + * return; + */ + bytes = drv->assoc_req_ies_len; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + os_memcpy(data.assoc_info.req_ies, spos, bytes); + + /* skip the '\0' byte */ + spos += bytes + 1; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + /* receive assoc. resp. IEs */ + spos += 9; + /* get IE's length */ + bytes = os_strlen(spos); + if (!bytes) + goto done; + + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + os_memcpy(data.assoc_info.resp_ies, spos, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + /* free allocated memory */ + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); + } +} + +static void +wpa_driver_ralink_event_wireless(struct wpa_driver_ralink_data *drv, + void *ctx, char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf, *assoc_info_buf, *info_pos; +#if 0 + BOOLEAN ieee8021x_required_key = FALSE; +#endif + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + assoc_info_buf = info_pos = NULL; + 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. */ + os_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_compiled > 18 && 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; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_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 = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + + if (drv->ap_scan == 1) { + if ((iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) + || (iwe->u.data.flags == + RT_REQIE_EVENT_FLAG) || + (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) + || (iwe->u.data.flags == + RT_ASSOCINFO_EVENT_FLAG)) { + if (drv->scanning_done == 0) { + os_free(buf); + return; + } + } + } + + if (iwe->u.data.flags == RT_ASSOC_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ASSOCIATED_EVENT !!!"); + /* determine whether the dynamic-WEP is used or + * not */ +#if 0 + if (wpa_s && wpa_s->current_ssid && + wpa_s->current_ssid->key_mgmt == + WPA_KEY_MGMT_IEEE8021X_NO_WPA) { + if ((wpa_s->current_ssid->eapol_flags & + (EAPOL_FLAG_REQUIRE_KEY_UNICAST | EAPOL_FLAG_REQUIRE_KEY_BROADCAST))) { + //wpa_printf(MSG_DEBUG, "The current ssid - (%s), eapol_flag = %d.\n", + // wpa_ssid_txt(wpa_s->current_ssid->ssid, wpa_s->current_ssid->ssid_len),wpa_s->current_ssid->eapol_flags); + ieee8021x_required_key = TRUE; + } + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X_REQUIRE_KEY, (char *) &ieee8021x_required_key, sizeof(BOOLEAN)) < 0) + { + wpa_printf(MSG_DEBUG, "ERROR: Failed to set OID_802_11_SET_IEEE8021X_REQUIRE_KEY(%d)", + (int) ieee8021x_required_key); + } + + wpa_printf(MSG_DEBUG, "ieee8021x_required_key is %s and eapol_flag(%d).\n", ieee8021x_required_key ? "TRUE" : "FALSE", + wpa_s->current_ssid->eapol_flags); + } +#endif + + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } else if (iwe->u.data.flags == RT_REQIE_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ReqIEs !!!"); + drv->assoc_req_ies = + os_malloc(iwe->u.data.length); + if (drv->assoc_req_ies == NULL) { + os_free(buf); + return; + } + + drv->assoc_req_ies_len = iwe->u.data.length; + os_memcpy(drv->assoc_req_ies, custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == RT_RESPIE_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive RespIEs !!!"); + drv->assoc_resp_ies = + os_malloc(iwe->u.data.length); + if (drv->assoc_resp_ies == NULL) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(buf); + return; + } + + drv->assoc_resp_ies_len = iwe->u.data.length; + os_memcpy(drv->assoc_resp_ies, custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == + RT_ASSOCINFO_EVENT_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive ASSOCINFO_EVENT !!!"); + + assoc_info_buf = + os_zalloc(drv->assoc_req_ies_len + + drv->assoc_resp_ies_len + 1); + + if (assoc_info_buf == NULL) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + os_free(buf); + return; + } + + if (drv->assoc_req_ies) { + os_memcpy(assoc_info_buf, + drv->assoc_req_ies, + drv->assoc_req_ies_len); + } + info_pos = assoc_info_buf + + drv->assoc_req_ies_len; + if (drv->assoc_resp_ies) { + os_memcpy(info_pos, + drv->assoc_resp_ies, + drv->assoc_resp_ies_len); + } + assoc_info_buf[drv->assoc_req_ies_len + + drv->assoc_resp_ies_len] = '\0'; + wpa_driver_ralink_event_wireless_custom( + drv, ctx, assoc_info_buf); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + os_free(assoc_info_buf); + } else if (iwe->u.data.flags == RT_DISASSOC_EVENT_FLAG) + { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive DISASSOCIATED_EVENT !!!"); + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + } else if (iwe->u.data.flags == RT_PMKIDCAND_FLAG) { + wpa_printf(MSG_DEBUG, "Custom wireless event: " + "receive PMKIDCAND_EVENT !!!"); + wpa_driver_ralink_event_pmkid( + drv, (const u8 *) custom, + iwe->u.data.length); + } else if (iwe->u.data.flags == RT_INTERFACE_DOWN) { + drv->g_driver_down = 1; + eloop_terminate(); + } else if (iwe->u.data.flags == RT_REPORT_AP_INFO) { + if (drv->ap_scan != 1) { + typedef struct PACKED { + UCHAR bssid[MAC_ADDR_LEN]; + UCHAR ssid[MAX_LEN_OF_SSID]; + INT ssid_len; + UCHAR wpa_ie[40]; + INT wpa_ie_len; + UCHAR rsn_ie[40]; + INT rsn_ie_len; + INT freq; + USHORT caps; + } *PAPINFO; + + wpa_printf(MSG_DEBUG, "Custom wireless" + " event: receive " + "RT_REPORT_AP_INFO !!!"); + //printf("iwe->u.data.length = %d\n", iwe->u.data.length); + //wpa_hexdump(MSG_DEBUG, "AP_Info: ", buf, iwe->u.data.length); +#if 0 + wpa_s->num_scan_results = 1; + if (wpa_s->scan_results) + os_free(wpa_s->scan_results); + wpa_s->scan_results = os_malloc(sizeof(struct wpa_scan_result) + 1); + if (wpa_s->scan_results) { + PAPINFO pApInfo = (PAPINFO)buf; + os_memcpy(wpa_s->scan_results[0].bssid, pApInfo->bssid, ETH_ALEN); + os_memcpy(wpa_s->scan_results[0].ssid, pApInfo->ssid, pApInfo->ssid_len); + wpa_s->scan_results[0].ssid_len = pApInfo->ssid_len; + if (pApInfo->wpa_ie_len > 0) { + os_memcpy(wpa_s->scan_results[0].wpa_ie, pApInfo->wpa_ie, pApInfo->wpa_ie_len); + wpa_s->scan_results[0].wpa_ie_len = pApInfo->wpa_ie_len; + } else if (pApInfo->rsn_ie_len > 0) { + os_memcpy(wpa_s->scan_results[0].rsn_ie, pApInfo->rsn_ie, pApInfo->rsn_ie_len); + wpa_s->scan_results[0].rsn_ie_len = pApInfo->rsn_ie_len; + } + wpa_s->scan_results[0].caps = pApInfo->caps; + wpa_s->scan_results[0].freq = pApInfo->freq; + } else { + wpa_printf("wpa_s->scan_" + "results fail to " + "os_malloc!!\n"); + } +#endif + } + } else { + wpa_driver_ralink_event_wireless_custom( + drv, ctx, buf); + } + os_free(buf); + break; + } + + pos += iwe->len; + } +} + +static void +wpa_driver_ralink_event_rtm_newlink(struct wpa_driver_ralink_data *drv, + void *ctx, struct nlmsghdr *h, int len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (len < (int) sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + wpa_hexdump(MSG_DEBUG, "ifi: ", (u8 *) ifi, sizeof(struct ifinfomsg)); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + wpa_printf(MSG_DEBUG, "attrlen=%d", attrlen); + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + wpa_hexdump(MSG_DEBUG, "attr1: ", (u8 *) attr, sizeof(struct rtattr)); + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + wpa_hexdump(MSG_DEBUG, "attr2: ", (u8 *)attr,rta_len); + while (RTA_OK(attr, attrlen)) { + wpa_printf(MSG_DEBUG, "rta_type=%02x\n", attr->rta_type); + if (attr->rta_type == IFLA_WIRELESS) { + wpa_driver_ralink_event_wireless( + drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + wpa_hexdump(MSG_DEBUG, "attr3: ", + (u8 *) attr, sizeof(struct rtattr)); + } +} + +static void wpa_driver_ralink_event_receive(int sock, void *ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + 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; + wpa_hexdump(MSG_DEBUG, "h: ", (u8 *)h, h->nlmsg_len); + + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_ralink_event_rtm_newlink(ctx, sock_ctx, h, + plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + +} + +static int +ralink_get_we_version_compiled(struct wpa_driver_ralink_data *drv) +{ + struct iwreq iwr; + UINT we_version_compiled = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) &we_version_compiled; + iwr.u.data.flags = RT_OID_WE_VERSION_COMPILED; + + if (ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "%s: failed", __func__); + return -1; + } + + drv->we_version_compiled = we_version_compiled; + + return 0; +} + +static int +ralink_set_iface_flags(void *priv, int dev_up) +{ + struct wpa_driver_ralink_data *drv = priv; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->ioctl_sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->ifname); + + 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; + } + + return 0; +} + +static void * wpa_driver_ralink_init(void *ctx, const char *ifname) +{ + int s; + struct wpa_driver_ralink_data *drv; + struct ifreq ifr; + struct sockaddr_nl local; + UCHAR enable_wpa_supplicant = 0; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* open socket to kernel */ + if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { + perror("socket"); + return NULL; + } + /* do it */ + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(s, SIOCGIFINDEX, &ifr) < 0) { + perror(ifr.ifr_name); + return NULL; + } + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + + drv->scanning_done = 1; + drv->ap_scan = 1; /* for now - let's assume ap_scan=1 is used */ + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ioctl_sock = s; + drv->g_driver_down = 0; + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_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); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_ralink_event_receive, drv, ctx); + drv->event_sock = s; + drv->no_of_pmkid = 4; /* Number of PMKID saved supported */ + + ralink_set_iface_flags(drv, 1); /* mark up during setup */ + ralink_get_we_version_compiled(drv); + wpa_driver_ralink_flush_pmkid(drv); + + if (drv->ap_scan == 1) + enable_wpa_supplicant = 1; + else + enable_wpa_supplicant = 2; + /* trigger driver support wpa_supplicant */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (PCHAR) &enable_wpa_supplicant, sizeof(UCHAR)) < 0) + { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", + (int) enable_wpa_supplicant); + wpa_printf(MSG_ERROR, "RALINK: Driver does not support " + "wpa_supplicant"); + close(s); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + if (drv->ap_scan == 1) + drv->scanning_done = 0; + + return drv; +} + +static void wpa_driver_ralink_deinit(void *priv) +{ + struct wpa_driver_ralink_data *drv = priv; + UCHAR enable_wpa_supplicant; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + enable_wpa_supplicant = 0; + + if (drv->g_driver_down == 0) { + /* trigger driver disable wpa_supplicant support */ + if (ralink_set_oid(drv, RT_OID_WPA_SUPPLICANT_SUPPORT, + (char *) &enable_wpa_supplicant, + sizeof(BOOLEAN)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "RT_OID_WPA_SUPPLICANT_SUPPORT(%d)", + (int) enable_wpa_supplicant); + } + + wpa_driver_ralink_flush_pmkid(drv); + + sleep(1); + ralink_set_iface_flags(drv, 0); + } + + eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); + eloop_unregister_read_sock(drv->event_sock); + close(drv->event_sock); + close(drv->ioctl_sock); + os_free(drv); +} + +static void wpa_driver_ralink_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_ralink_data *drv = eloop_ctx; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); + + drv->scanning_done = 1; + +} + +static int wpa_driver_ralink_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_ralink_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (ssid_len > IW_ESSID_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", + __FUNCTION__, (unsigned long) ssid_len); + return -1; + } + + /* wpa_driver_ralink_set_ssid(drv, ssid, ssid_len); */ + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + eloop_cancel_timeout(wpa_driver_ralink_scan_timeout, drv, drv->ctx); + eloop_register_timeout(4, 0, wpa_driver_ralink_scan_timeout, drv, + drv->ctx); + + drv->scanning_done = 0; + + return ret; +} + +static int +wpa_driver_ralink_get_scan_results(void *priv, + struct wpa_scan_result *results, + size_t max_size) +{ + struct wpa_driver_ralink_data *drv = priv; + UCHAR *buf = NULL; + NDIS_802_11_BSSID_LIST_EX *wsr; + NDIS_WLAN_BSSID_EX *wbi; + struct iwreq iwr; + int rv = 0; + size_t ap_num; + u8 *pos, *end; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (drv->we_version_compiled >= 17) { + buf = os_zalloc(8192); + iwr.u.data.length = 8192; + } else { + buf = os_zalloc(4096); + iwr.u.data.length = 4096; + } + if (buf == NULL) + return -1; + + wsr = (NDIS_802_11_BSSID_LIST_EX *) buf; + + wsr->NumberOfItems = 0; + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (void *) buf; + iwr.u.data.flags = OID_802_11_BSSID_LIST; + + if ((rv = ioctl(drv->ioctl_sock, RT_PRIV_IOCTL, &iwr)) < 0) { + wpa_printf(MSG_DEBUG, "ioctl fail: rv = %d", rv); + os_free(buf); + return -1; + } + + os_memset(results, 0, max_size * sizeof(struct wpa_scan_result)); + + for (ap_num = 0, wbi = wsr->Bssid; ap_num < wsr->NumberOfItems; + ++ap_num) { + os_memcpy(results[ap_num].bssid, &wbi->MacAddress, ETH_ALEN); + os_memcpy(results[ap_num].ssid, wbi->Ssid.Ssid, + wbi->Ssid.SsidLength); + results[ap_num].ssid_len = wbi->Ssid.SsidLength; + results[ap_num].freq = (wbi->Configuration.DSConfig / 1000); + + /* get ie's */ + wpa_hexdump(MSG_DEBUG, "RALINK: AP IEs", + (u8 *) wbi + sizeof(*wbi) - 1, wbi->IELength); + + pos = (u8 *) wbi + sizeof(*wbi) - 1; + end = (u8 *) wbi + sizeof(*wbi) + wbi->IELength; + + if (wbi->IELength < sizeof(NDIS_802_11_FIXED_IEs)) + break; + + pos += sizeof(NDIS_802_11_FIXED_IEs) - 2; + os_memcpy(&results[ap_num].caps, pos, 2); + pos += 2; + + while (pos + 1 < end && pos + 2 + pos[1] <= end) { + u8 ielen = 2 + pos[1]; + + if (ielen > SSID_MAX_WPA_IE_LEN) { + pos += ielen; + continue; + } + + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && + pos[1] >= 4 && + os_memcmp(pos + 2, "\x00\x50\xf2\x01", 4) == 0) { + os_memcpy(results[ap_num].wpa_ie, pos, ielen); + results[ap_num].wpa_ie_len = ielen; + } else if (pos[0] == WLAN_EID_RSN) { + os_memcpy(results[ap_num].rsn_ie, pos, ielen); + results[ap_num].rsn_ie_len = ielen; + } + pos += ielen; + } + + wbi = (NDIS_WLAN_BSSID_EX *) ((u8 *) wbi + wbi->Length); + } + + os_free(buf); + return ap_num; +} + +static int ralink_set_auth_mode(struct wpa_driver_ralink_data *drv, + NDIS_802_11_AUTHENTICATION_MODE mode) +{ + NDIS_802_11_AUTHENTICATION_MODE auth_mode = mode; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (ralink_set_oid(drv, OID_802_11_AUTHENTICATION_MODE, + (char *) &auth_mode, sizeof(auth_mode)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_AUTHENTICATION_MODE (%d)", + (int) auth_mode); + return -1; + } + return 0; +} + +static int wpa_driver_ralink_remove_key(struct wpa_driver_ralink_data *drv, + int key_idx, const u8 *addr, + const u8 *bssid, int pairwise) +{ + NDIS_802_11_REMOVE_KEY rkey; + NDIS_802_11_KEY_INDEX _index; + int res, res2; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + os_memset(&rkey, 0, sizeof(rkey)); + + rkey.Length = sizeof(rkey); + rkey.KeyIndex = key_idx; + + if (pairwise) + rkey.KeyIndex |= 1 << 30; + + os_memcpy(rkey.BSSID, bssid, ETH_ALEN); + + res = ralink_set_oid(drv, OID_802_11_REMOVE_KEY, (char *) &rkey, + sizeof(rkey)); + + /* AlbertY@20060210 removed it */ + if (0 /* !pairwise */) { + res2 = ralink_set_oid(drv, OID_802_11_REMOVE_WEP, + (char *) &_index, sizeof(_index)); + } else + res2 = 0; + + if (res < 0 && res2 < 0) + return res; + return 0; +} + +static int wpa_driver_ralink_add_wep(struct wpa_driver_ralink_data *drv, + int pairwise, int key_idx, int set_tx, + const u8 *key, size_t key_len) +{ + NDIS_802_11_WEP *wep; + size_t len; + int res; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + len = 12 + key_len; + wep = os_zalloc(len); + if (wep == NULL) + return -1; + + wep->Length = len; + wep->KeyIndex = key_idx; + + if (set_tx) + wep->KeyIndex |= 0x80000000; + + wep->KeyLength = key_len; + os_memcpy(wep->KeyMaterial, key, key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_WEP", + (const u8 *) wep, len); + res = ralink_set_oid(drv, OID_802_11_ADD_WEP, (char *) wep, len); + + os_free(wep); + + return res; +} + +static int wpa_driver_ralink_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_ralink_data *drv = priv; + size_t len, i; + NDIS_802_11_KEY *nkey; + int res, pairwise; + u8 bssid[ETH_ALEN]; + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (addr == NULL || os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", + ETH_ALEN) == 0) { + /* Group Key */ + pairwise = 0; + wpa_driver_ralink_get_bssid(drv, bssid); + } else { + /* Pairwise Key */ + pairwise = 1; + os_memcpy(bssid, addr, ETH_ALEN); + } + + if (alg == WPA_ALG_NONE || key_len == 0) { + return wpa_driver_ralink_remove_key(drv, key_idx, addr, bssid, + pairwise); + } + + if (alg == WPA_ALG_WEP) { + return wpa_driver_ralink_add_wep(drv, pairwise, key_idx, + set_tx, key, key_len); + } + + len = 12 + 6 + 6 + 8 + key_len; + + nkey = os_zalloc(len); + if (nkey == NULL) + return -1; + + nkey->Length = len; + nkey->KeyIndex = key_idx; + + if (set_tx) + nkey->KeyIndex |= 1 << 31; + + if (pairwise) + nkey->KeyIndex |= 1 << 30; + + if (seq && seq_len) + nkey->KeyIndex |= 1 << 29; + + nkey->KeyLength = key_len; + os_memcpy(nkey->BSSID, bssid, ETH_ALEN); + + if (seq && seq_len) { + for (i = 0; i < seq_len; i++) + nkey->KeyRSC |= seq[i] << (i * 8); + } + if (alg == WPA_ALG_TKIP && key_len == 32) { + os_memcpy(nkey->KeyMaterial, key, 16); + os_memcpy(nkey->KeyMaterial + 16, key + 24, 8); + os_memcpy(nkey->KeyMaterial + 24, key + 16, 8); + } else { + os_memcpy(nkey->KeyMaterial, key, key_len); + } + + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", __FUNCTION__, alg, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + wpa_hexdump_key(MSG_MSGDUMP, "RALINK: OID_802_11_ADD_KEY", + (const u8 *) nkey, len); + res = ralink_set_oid(drv, OID_802_11_ADD_KEY, (char *) nkey, len); + os_free(nkey); + + return res; +} + +static int wpa_driver_ralink_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ralink_data *drv = priv; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (ralink_set_oid(drv, OID_802_11_DISASSOCIATE, " ", 4) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_DISASSOCIATE"); + } + + return 0; +} + +static int wpa_driver_ralink_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_ralink_data *drv = priv; + + wpa_printf(MSG_DEBUG, "g_driver_down = %d", drv->g_driver_down); + + if (drv->g_driver_down == 1) + return -1; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + if (ralink_get_new_driver_flag(drv) == 0) { + return wpa_driver_ralink_disassociate(priv, addr, reason_code); + } else { + MLME_DEAUTH_REQ_STRUCT mlme; + os_memset(&mlme, 0, sizeof(MLME_DEAUTH_REQ_STRUCT)); + mlme.Reason = reason_code; + os_memcpy(mlme.Addr, addr, MAC_ADDR_LEN); + return ralink_set_oid(drv, OID_802_11_DEAUTHENTICATION, + (char *) &mlme, + sizeof(MLME_DEAUTH_REQ_STRUCT)); + } +} + +static int +wpa_driver_ralink_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_ralink_data *drv = priv; + + NDIS_802_11_NETWORK_INFRASTRUCTURE mode; + NDIS_802_11_AUTHENTICATION_MODE auth_mode; + NDIS_802_11_WEP_STATUS encr; + BOOLEAN ieee8021xMode; + + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (params->mode == IEEE80211_MODE_IBSS) + mode = Ndis802_11IBSS; + else + mode = Ndis802_11Infrastructure; + + if (ralink_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, + (char *) &mode, sizeof(mode)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_INFRASTRUCTURE_MODE (%d)", + (int) mode); + /* Try to continue anyway */ + } + + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { + if (params->auth_alg & AUTH_ALG_SHARED_KEY) { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + auth_mode = Ndis802_11AuthModeAutoSwitch; + else + auth_mode = Ndis802_11AuthModeShared; + } else + auth_mode = Ndis802_11AuthModeOpen; + } else if (params->wpa_ie[0] == WLAN_EID_RSN) { + if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPA2PSK; + else + auth_mode = Ndis802_11AuthModeWPA2; + } else { + if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE) + auth_mode = Ndis802_11AuthModeWPANone; + else if (params->key_mgmt_suite == KEY_MGMT_PSK) + auth_mode = Ndis802_11AuthModeWPAPSK; + else + auth_mode = Ndis802_11AuthModeWPA; + } + + switch (params->pairwise_suite) { + case CIPHER_CCMP: + encr = Ndis802_11Encryption3Enabled; + break; + case CIPHER_TKIP: + encr = Ndis802_11Encryption2Enabled; + break; + case CIPHER_WEP40: + case CIPHER_WEP104: + encr = Ndis802_11Encryption1Enabled; + break; + case CIPHER_NONE: + if (params->group_suite == CIPHER_CCMP) + encr = Ndis802_11Encryption3Enabled; + else if (params->group_suite == CIPHER_TKIP) + encr = Ndis802_11Encryption2Enabled; + else + encr = Ndis802_11EncryptionDisabled; + break; + default: + encr = Ndis802_11EncryptionDisabled; + break; + } + + ralink_set_auth_mode(drv, auth_mode); + + /* notify driver that IEEE8021x mode is enabled */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) + ieee8021xMode = TRUE; + else + ieee8021xMode = FALSE; + + if (ralink_set_oid(drv, OID_802_11_SET_IEEE8021X, + (char *) &ieee8021xMode, sizeof(BOOLEAN)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_SET_IEEE8021X(%d)", + (int) ieee8021xMode); + } + + if (ralink_set_oid(drv, OID_802_11_WEP_STATUS, + (char *) &encr, sizeof(encr)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_WEP_STATUS(%d)", + (int) encr); + } + + if ((ieee8021xMode == FALSE) && + (encr == Ndis802_11Encryption1Enabled)) { + /* static WEP */ + int enabled = 0; + if (ralink_set_oid(drv, OID_802_11_DROP_UNENCRYPTED, + (char *) &enabled, sizeof(enabled)) < 0) { + wpa_printf(MSG_DEBUG, "RALINK: Failed to set " + "OID_802_11_DROP_UNENCRYPTED(%d)", + (int) encr); + } + } + + return wpa_driver_ralink_set_ssid(drv, params->ssid, params->ssid_len); +} + +static int +wpa_driver_ralink_set_countermeasures(void *priv, int enabled) +{ + struct wpa_driver_ralink_data *drv = priv; + if (drv->g_driver_down == 1) + return -1; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return ralink_set_oid(drv, OID_SET_COUNTERMEASURES, (char *) &enabled, + sizeof(int)); +} + +const struct wpa_driver_ops wpa_driver_ralink_ops = { + .name = "ralink", + .desc = "Ralink Wireless Client driver", + .get_bssid = wpa_driver_ralink_get_bssid, + .get_ssid = wpa_driver_ralink_get_ssid, + .set_key = wpa_driver_ralink_set_key, + .init = wpa_driver_ralink_init, + .deinit = wpa_driver_ralink_deinit, + .set_countermeasures = wpa_driver_ralink_set_countermeasures, + .scan = wpa_driver_ralink_scan, + .get_scan_results = wpa_driver_ralink_get_scan_results, + .deauthenticate = wpa_driver_ralink_deauthenticate, + .disassociate = wpa_driver_ralink_disassociate, + .associate = wpa_driver_ralink_associate, + .add_pmkid = wpa_driver_ralink_add_pmkid, + .remove_pmkid = wpa_driver_ralink_remove_pmkid, + .flush_pmkid = wpa_driver_ralink_flush_pmkid, +}; diff --git a/contrib/wpa/src/drivers/driver_ralink.h b/contrib/wpa/src/drivers/driver_ralink.h new file mode 100644 index 0000000..ddf44de --- /dev/null +++ b/contrib/wpa/src/drivers/driver_ralink.h @@ -0,0 +1,382 @@ +/* + * WPA Supplicant - driver_ralink exported functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007, Snowpin Lee <snowpin_lee@ralinktech.com.tw> + * + * 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. + */ + +// Ralink defined OIDs +#if WIRELESS_EXT <= 11 +#ifndef SIOCDEVPRIVATE +#define SIOCDEVPRIVATE 0x8BE0 +#endif +#define SIOCIWFIRSTPRIV SIOCDEVPRIVATE +#endif + +#define RT_PRIV_IOCTL (SIOCIWFIRSTPRIV + 0x0E) +#define RTPRIV_IOCTL_SET (SIOCIWFIRSTPRIV + 0x02) + +// IEEE 802.11 OIDs & Ralink defined OIDs ****** + +// (RaConfig Set/QueryInform) ==> +#define OID_GET_SET_TOGGLE 0x8000 + +#define OID_802_11_ADD_WEP 0x0112 +#define OID_802_11_REMOVE_WEP 0x0113 +#define OID_802_11_DISASSOCIATE 0x0114 +#define OID_802_11_PRIVACY_FILTER 0x0118 +#define OID_802_11_ASSOCIATION_INFORMATION 0x011E +#define OID_802_11_BSSID_LIST_SCAN 0x0508 +#define OID_802_11_SSID 0x0509 +#define OID_802_11_BSSID 0x050A +#define OID_802_11_WEP_STATUS 0x0510 +#define OID_802_11_AUTHENTICATION_MODE 0x0511 +#define OID_802_11_INFRASTRUCTURE_MODE 0x0512 +#define OID_802_11_TX_POWER_LEVEL 0x0517 +#define OID_802_11_REMOVE_KEY 0x0519 +#define OID_802_11_ADD_KEY 0x0520 +#define OID_802_11_DEAUTHENTICATION 0x0526 +#define OID_802_11_DROP_UNENCRYPTED 0x0527 +#define OID_802_11_BSSID_LIST 0x0609 +#define OID_802_3_CURRENT_ADDRESS 0x060A +#define OID_SET_COUNTERMEASURES 0x0616 +#define OID_802_11_SET_IEEE8021X 0x0617 // For IEEE8021x mode +#define OID_802_11_SET_IEEE8021X_REQUIRE_KEY 0x0618 // For DynamicWEP in IEEE802.1x mode +#define OID_802_11_PMKID 0x0620 +#define RT_OID_WPA_SUPPLICANT_SUPPORT 0x0621 // for trigger driver enable/disable wpa_supplicant support +#define RT_OID_WE_VERSION_COMPILED 0x0622 +#define RT_OID_NEW_DRIVER 0x0623 + +#define PACKED __attribute__ ((packed)) + +//wpa_supplicant event flags +#define RT_ASSOC_EVENT_FLAG 0x0101 +#define RT_DISASSOC_EVENT_FLAG 0x0102 +#define RT_REQIE_EVENT_FLAG 0x0103 +#define RT_RESPIE_EVENT_FLAG 0x0104 +#define RT_ASSOCINFO_EVENT_FLAG 0x0105 +#define RT_PMKIDCAND_FLAG 0x0106 +#define RT_INTERFACE_DOWN 0x0107 +#define RT_REPORT_AP_INFO 0x0108 + +// +// IEEE 802.11 Structures and definitions +// +// new types for Media Specific Indications + +#ifndef ULONG +#define CHAR char +#define INT int +#define SHORT int +#define UINT u32 +#undef ULONG +//#define ULONG u32 +#define ULONG unsigned long /* 32-bit in 32-bit CPU or 64-bit in 64-bit CPU */ +#define USHORT unsigned short +#define UCHAR unsigned char + +#define uint32 u32 +#define uint8 u8 + + +#define BOOLEAN u8 +//#define LARGE_INTEGER s64 +#define VOID void +#define LONG long +#define LONGLONG s64 +#define ULONGLONG u64 +typedef VOID *PVOID; +typedef CHAR *PCHAR; +typedef UCHAR *PUCHAR; +typedef USHORT *PUSHORT; +typedef LONG *PLONG; +typedef ULONG *PULONG; + +typedef union _LARGE_INTEGER { + struct { + ULONG LowPart; + LONG HighPart; + }vv; + struct { + ULONG LowPart; + LONG HighPart; + } u; + s64 QuadPart; +} LARGE_INTEGER; + +#endif + +#define NDIS_802_11_LENGTH_SSID 32 +#define NDIS_802_11_LENGTH_RATES 8 +#define NDIS_802_11_LENGTH_RATES_EX 16 +#define MAX_LEN_OF_SSID 32 +#define MAC_ADDR_LEN 6 + +typedef UCHAR NDIS_802_11_MAC_ADDRESS[6]; + +// mask for authentication/integrity fields +#define NDIS_802_11_AUTH_REQUEST_AUTH_FIELDS 0x0f + +#define NDIS_802_11_AUTH_REQUEST_REAUTH 0x01 +#define NDIS_802_11_AUTH_REQUEST_KEYUPDATE 0x02 +#define NDIS_802_11_AUTH_REQUEST_PAIRWISE_ERROR 0x06 +#define NDIS_802_11_AUTH_REQUEST_GROUP_ERROR 0x0E + +// Added new types for OFDM 5G and 2.4G +typedef enum _NDIS_802_11_NETWORK_TYPE +{ + Ndis802_11FH, + Ndis802_11DS, + Ndis802_11OFDM5, + Ndis802_11OFDM24, + Ndis802_11Automode, + Ndis802_11NetworkTypeMax // not a real type, defined as an upper bound +} NDIS_802_11_NETWORK_TYPE, *PNDIS_802_11_NETWORK_TYPE; + +// +// Received Signal Strength Indication +// +typedef LONG NDIS_802_11_RSSI; // in dBm + +typedef struct _NDIS_802_11_CONFIGURATION_FH +{ + ULONG Length; // Length of structure + ULONG HopPattern; // As defined by 802.11, MSB set + ULONG HopSet; // to one if non-802.11 + ULONG DwellTime; // units are Kusec +} NDIS_802_11_CONFIGURATION_FH, *PNDIS_802_11_CONFIGURATION_FH; + +typedef struct _NDIS_802_11_CONFIGURATION +{ + ULONG Length; // Length of structure + ULONG BeaconPeriod; // units are Kusec + ULONG ATIMWindow; // units are Kusec + ULONG DSConfig; // Frequency, units are kHz + NDIS_802_11_CONFIGURATION_FH FHConfig; +} NDIS_802_11_CONFIGURATION, *PNDIS_802_11_CONFIGURATION; + +typedef ULONG NDIS_802_11_KEY_INDEX; +typedef ULONGLONG NDIS_802_11_KEY_RSC; + +// Key mapping keys require a BSSID +typedef struct _NDIS_802_11_KEY +{ + UINT Length; // Length of this structure + UINT KeyIndex; + UINT KeyLength; // length of key in bytes + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_KEY_RSC KeyRSC; + UCHAR KeyMaterial[1]; // variable length depending on above field +} NDIS_802_11_KEY, *PNDIS_802_11_KEY; + +typedef struct _NDIS_802_11_REMOVE_KEY +{ + UINT Length; // Length of this structure + UINT KeyIndex; + NDIS_802_11_MAC_ADDRESS BSSID; +} NDIS_802_11_REMOVE_KEY, *PNDIS_802_11_REMOVE_KEY; + +typedef struct PACKED _NDIS_802_11_WEP +{ + UINT Length; // Length of this structure + UINT KeyIndex; // 0 is the per-client key, 1-N are the + // global keys + UINT KeyLength; // length of key in bytes + UCHAR KeyMaterial[1];// variable length depending on above field +} NDIS_802_11_WEP, *PNDIS_802_11_WEP; + + +typedef enum _NDIS_802_11_NETWORK_INFRASTRUCTURE +{ + Ndis802_11IBSS, + Ndis802_11Infrastructure, + Ndis802_11AutoUnknown, + Ndis802_11InfrastructureMax // Not a real value, defined as upper bound +} NDIS_802_11_NETWORK_INFRASTRUCTURE, *PNDIS_802_11_NETWORK_INFRASTRUCTURE; + +// PMKID Structures +typedef UCHAR NDIS_802_11_PMKID_VALUE[16]; + +typedef struct _BSSID_INFO +{ + NDIS_802_11_MAC_ADDRESS BSSID; + NDIS_802_11_PMKID_VALUE PMKID; +} BSSID_INFO, *PBSSID_INFO; + +typedef struct _NDIS_802_11_PMKID +{ + ULONG Length; + ULONG BSSIDInfoCount; + BSSID_INFO BSSIDInfo[1]; +} NDIS_802_11_PMKID, *PNDIS_802_11_PMKID; + +//Added new types for PMKID Candidate lists. +typedef struct _PMKID_CANDIDATE { + NDIS_802_11_MAC_ADDRESS BSSID; + ULONG Flags; +} PMKID_CANDIDATE, *PPMKID_CANDIDATE; + +typedef struct _NDIS_802_11_PMKID_CANDIDATE_LIST +{ + ULONG Version; // Version of the structure + ULONG NumCandidates; // No. of pmkid candidates + PMKID_CANDIDATE CandidateList[1]; +} NDIS_802_11_PMKID_CANDIDATE_LIST, *PNDIS_802_11_PMKID_CANDIDATE_LIST; + +//Flags for PMKID Candidate list structure +#define NDIS_802_11_PMKID_CANDIDATE_PREAUTH_ENABLED 0x01 + +// Add new authentication modes +typedef enum _NDIS_802_11_AUTHENTICATION_MODE +{ + Ndis802_11AuthModeOpen, + Ndis802_11AuthModeShared, + Ndis802_11AuthModeAutoSwitch, + Ndis802_11AuthModeWPA, + Ndis802_11AuthModeWPAPSK, + Ndis802_11AuthModeWPANone, + Ndis802_11AuthModeWPA2, + Ndis802_11AuthModeWPA2PSK, + Ndis802_11AuthModeMax // Not a real mode, defined as upper bound +} NDIS_802_11_AUTHENTICATION_MODE, *PNDIS_802_11_AUTHENTICATION_MODE; + +typedef UCHAR NDIS_802_11_RATES[NDIS_802_11_LENGTH_RATES]; // Set of 8 data rates +typedef UCHAR NDIS_802_11_RATES_EX[NDIS_802_11_LENGTH_RATES_EX]; // Set of 16 data rates + +typedef struct PACKED _NDIS_802_11_SSID +{ + INT SsidLength; // length of SSID field below, in bytes; + // this can be zero. + UCHAR Ssid[NDIS_802_11_LENGTH_SSID]; // SSID information field +} NDIS_802_11_SSID, *PNDIS_802_11_SSID; + + +typedef struct PACKED _NDIS_WLAN_BSSID +{ + ULONG Length; // Length of this structure + NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; // SSID + ULONG Privacy; // WEP encryption requirement + NDIS_802_11_RSSI Rssi; // receive signal + // strength in dBm + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES SupportedRates; +} NDIS_WLAN_BSSID, *PNDIS_WLAN_BSSID; + +typedef struct PACKED _NDIS_802_11_BSSID_LIST +{ + UINT NumberOfItems; // in list below, at least 1 + NDIS_WLAN_BSSID Bssid[1]; +} NDIS_802_11_BSSID_LIST, *PNDIS_802_11_BSSID_LIST; + +// Added Capabilities, IELength and IEs for each BSSID +typedef struct PACKED _NDIS_WLAN_BSSID_EX +{ + ULONG Length; // Length of this structure + NDIS_802_11_MAC_ADDRESS MacAddress; // BSSID + UCHAR Reserved[2]; + NDIS_802_11_SSID Ssid; // SSID + UINT Privacy; // WEP encryption requirement + NDIS_802_11_RSSI Rssi; // receive signal + // strength in dBm + NDIS_802_11_NETWORK_TYPE NetworkTypeInUse; + NDIS_802_11_CONFIGURATION Configuration; + NDIS_802_11_NETWORK_INFRASTRUCTURE InfrastructureMode; + NDIS_802_11_RATES_EX SupportedRates; + ULONG IELength; + UCHAR IEs[1]; +} NDIS_WLAN_BSSID_EX, *PNDIS_WLAN_BSSID_EX; + +typedef struct PACKED _NDIS_802_11_BSSID_LIST_EX +{ + UINT NumberOfItems; // in list below, at least 1 + NDIS_WLAN_BSSID_EX Bssid[1]; +} NDIS_802_11_BSSID_LIST_EX, *PNDIS_802_11_BSSID_LIST_EX; + +typedef struct PACKED _NDIS_802_11_FIXED_IEs +{ + UCHAR Timestamp[8]; + USHORT BeaconInterval; + USHORT Capabilities; +} NDIS_802_11_FIXED_IEs, *PNDIS_802_11_FIXED_IEs; + +// Added new encryption types +// Also aliased typedef to new name +typedef enum _NDIS_802_11_WEP_STATUS +{ + Ndis802_11WEPEnabled, + Ndis802_11Encryption1Enabled = Ndis802_11WEPEnabled, + Ndis802_11WEPDisabled, + Ndis802_11EncryptionDisabled = Ndis802_11WEPDisabled, + Ndis802_11WEPKeyAbsent, + Ndis802_11Encryption1KeyAbsent = Ndis802_11WEPKeyAbsent, + Ndis802_11WEPNotSupported, + Ndis802_11EncryptionNotSupported = Ndis802_11WEPNotSupported, + Ndis802_11Encryption2Enabled, + Ndis802_11Encryption2KeyAbsent, + Ndis802_11Encryption3Enabled, + Ndis802_11Encryption3KeyAbsent +} NDIS_802_11_WEP_STATUS, *PNDIS_802_11_WEP_STATUS, + NDIS_802_11_ENCRYPTION_STATUS, *PNDIS_802_11_ENCRYPTION_STATUS; + +typedef enum _NDIS_802_11_RELOAD_DEFAULTS +{ + Ndis802_11ReloadWEPKeys +} NDIS_802_11_RELOAD_DEFAULTS, *PNDIS_802_11_RELOAD_DEFAULTS; + +#define NDIS_802_11_AI_REQFI_CAPABILITIES 1 +#define NDIS_802_11_AI_REQFI_LISTENINTERVAL 2 +#define NDIS_802_11_AI_REQFI_CURRENTAPADDRESS 4 + +#define NDIS_802_11_AI_RESFI_CAPABILITIES 1 +#define NDIS_802_11_AI_RESFI_STATUSCODE 2 +#define NDIS_802_11_AI_RESFI_ASSOCIATIONID 4 + +typedef struct _NDIS_802_11_AI_REQFI +{ + USHORT Capabilities; + USHORT ListenInterval; + NDIS_802_11_MAC_ADDRESS CurrentAPAddress; +} NDIS_802_11_AI_REQFI, *PNDIS_802_11_AI_REQFI; + +typedef struct _NDIS_802_11_AI_RESFI +{ + USHORT Capabilities; + USHORT StatusCode; + USHORT AssociationId; +} NDIS_802_11_AI_RESFI, *PNDIS_802_11_AI_RESFI; + +typedef struct _NDIS_802_11_ASSOCIATION_INFORMATION +{ + ULONG Length; + USHORT AvailableRequestFixedIEs; + NDIS_802_11_AI_REQFI RequestFixedIEs; + ULONG RequestIELength; + ULONG OffsetRequestIEs; + USHORT AvailableResponseFixedIEs; + NDIS_802_11_AI_RESFI ResponseFixedIEs; + ULONG ResponseIELength; + ULONG OffsetResponseIEs; +} NDIS_802_11_ASSOCIATION_INFORMATION, *PNDIS_802_11_ASSOCIATION_INFORMATION; + +struct ndis_pmkid_entry { + struct ndis_pmkid_entry *next; + u8 bssid[ETH_ALEN]; + u8 pmkid[16]; +}; + +typedef struct _MLME_DEAUTH_REQ_STRUCT { + UCHAR Addr[MAC_ADDR_LEN]; + USHORT Reason; +} MLME_DEAUTH_REQ_STRUCT, *PMLME_DEAUTH_REQ_STRUCT; diff --git a/contrib/wpa/src/drivers/driver_roboswitch.c b/contrib/wpa/src/drivers/driver_roboswitch.c new file mode 100644 index 0000000..bc11a48 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_roboswitch.c @@ -0,0 +1,476 @@ +/* + * WPA Supplicant - roboswitch driver interface + * Copyright (c) 2008-2009 Jouke Witteveen + * + * 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 <linux/if.h> +#include <linux/sockios.h> +#include <linux/if_ether.h> +#include <linux/mii.h> + +#include "common.h" +#include "driver.h" +#include "l2_packet/l2_packet.h" + +#ifndef ETH_P_EAPOL +#define ETH_P_EAPOL 0x888e +#endif + +#define ROBO_PHY_ADDR 0x1e /* RoboSwitch PHY address */ + +/* MII access registers */ +#define ROBO_MII_PAGE 0x10 /* MII page register */ +#define ROBO_MII_ADDR 0x11 /* MII address register */ +#define ROBO_MII_DATA_OFFSET 0x18 /* Start of MII data registers */ + +#define ROBO_MII_PAGE_ENABLE 0x01 /* MII page op code */ +#define ROBO_MII_ADDR_WRITE 0x01 /* MII address write op code */ +#define ROBO_MII_ADDR_READ 0x02 /* MII address read op code */ +#define ROBO_MII_DATA_MAX 4 /* Consecutive MII data registers */ +#define ROBO_MII_RETRY_MAX 10 /* Read attempts before giving up */ + +/* Page numbers */ +#define ROBO_ARLCTRL_PAGE 0x04 /* ARL control page */ +#define ROBO_VLAN_PAGE 0x34 /* VLAN page */ + +/* ARL control page registers */ +#define ROBO_ARLCTRL_CONF 0x00 /* ARL configuration register */ +#define ROBO_ARLCTRL_ADDR_1 0x10 /* Multiport address 1 */ +#define ROBO_ARLCTRL_VEC_1 0x16 /* Multiport vector 1 */ +#define ROBO_ARLCTRL_ADDR_2 0x20 /* Multiport address 2 */ +#define ROBO_ARLCTRL_VEC_2 0x26 /* Multiport vector 2 */ + +/* VLAN page registers */ +#define ROBO_VLAN_ACCESS 0x08 /* VLAN table access register */ +#define ROBO_VLAN_ACCESS_5350 0x06 /* VLAN table access register (5350) */ +#define ROBO_VLAN_READ 0x0c /* VLAN read register */ +#define ROBO_VLAN_MAX 0xff /* Maximum number of VLANs */ + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + +struct wpa_driver_roboswitch_data { + void *ctx; + struct l2_packet_data *l2; + char ifname[IFNAMSIZ + 1]; + u8 own_addr[ETH_ALEN]; + struct ifreq ifr; + int fd, is_5350; + u16 ports; +}; + + +/* Copied from the kernel-only part of mii.h. */ +static inline struct mii_ioctl_data *if_mii(struct ifreq *rq) +{ + return (struct mii_ioctl_data *) &rq->ifr_ifru; +} + + +/* + * RoboSwitch uses 16-bit Big Endian addresses. + * The ordering of the words is reversed in the MII registers. + */ +static void wpa_driver_roboswitch_addr_be16(const u8 addr[ETH_ALEN], u16 *be) +{ + int i; + for (i = 0; i < ETH_ALEN; i += 2) + be[(ETH_ALEN - i) / 2 - 1] = WPA_GET_BE16(addr + i); +} + + +static u16 wpa_driver_roboswitch_mdio_read( + struct wpa_driver_roboswitch_data *drv, u8 reg) +{ + struct mii_ioctl_data *mii = if_mii(&drv->ifr); + + mii->phy_id = ROBO_PHY_ADDR; + mii->reg_num = reg; + + if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) { + perror("ioctl[SIOCGMIIREG]"); + return 0x00; + } + return mii->val_out; +} + + +static void wpa_driver_roboswitch_mdio_write( + struct wpa_driver_roboswitch_data *drv, u8 reg, u16 val) +{ + struct mii_ioctl_data *mii = if_mii(&drv->ifr); + + mii->phy_id = ROBO_PHY_ADDR; + mii->reg_num = reg; + mii->val_in = val; + + if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) { + perror("ioctl[SIOCSMIIREG"); + } +} + + +static int wpa_driver_roboswitch_reg(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u8 op) +{ + int i; + + /* set page number */ + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_PAGE, + (page << 8) | ROBO_MII_PAGE_ENABLE); + /* set register address */ + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_ADDR, (reg << 8) | op); + + /* check if operation completed */ + for (i = 0; i < ROBO_MII_RETRY_MAX; ++i) { + if ((wpa_driver_roboswitch_mdio_read(drv, ROBO_MII_ADDR) & 3) + == 0) + return 0; + } + /* timeout */ + return -1; +} + + +static int wpa_driver_roboswitch_read(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u16 *val, int len) +{ + int i; + + if (len > ROBO_MII_DATA_MAX || + wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_READ) < 0) + return -1; + + for (i = 0; i < len; ++i) { + val[i] = wpa_driver_roboswitch_mdio_read( + drv, ROBO_MII_DATA_OFFSET + i); + } + + return 0; +} + + +static int wpa_driver_roboswitch_write(struct wpa_driver_roboswitch_data *drv, + u8 page, u8 reg, u16 *val, int len) +{ + int i; + + if (len > ROBO_MII_DATA_MAX) return -1; + for (i = 0; i < len; ++i) { + wpa_driver_roboswitch_mdio_write(drv, ROBO_MII_DATA_OFFSET + i, + val[i]); + } + return wpa_driver_roboswitch_reg(drv, page, reg, ROBO_MII_ADDR_WRITE); +} + + +static void wpa_driver_roboswitch_receive(void *priv, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct wpa_driver_roboswitch_data *drv = priv; + + if (len > 14 && WPA_GET_BE16(buf + 12) == ETH_P_EAPOL && + os_memcmp(buf, drv->own_addr, ETH_ALEN) == 0) { + wpa_supplicant_rx_eapol(drv->ctx, src_addr, buf + 14, + len - 14); + } +} + + +static int wpa_driver_roboswitch_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int wpa_driver_roboswitch_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int wpa_driver_roboswitch_set_param(void *priv, const char *param) +{ + struct wpa_driver_roboswitch_data *drv = priv; + char *sep; + + if (param == NULL || os_strstr(param, "multicast_only=1") == NULL) { + sep = drv->ifname + os_strlen(drv->ifname); + *sep = '.'; + drv->l2 = l2_packet_init(drv->ifname, NULL, ETH_P_ALL, + wpa_driver_roboswitch_receive, drv, + 1); + if (drv->l2 == NULL) { + wpa_printf(MSG_INFO, "%s: Unable to listen on %s", + __func__, drv->ifname); + return -1; + } + *sep = '\0'; + l2_packet_get_own_addr(drv->l2, drv->own_addr); + } else { + wpa_printf(MSG_DEBUG, "%s: Ignoring unicast frames", __func__); + drv->l2 = NULL; + } + return 0; +} + + +static const char * wpa_driver_roboswitch_get_ifname(void *priv) +{ + struct wpa_driver_roboswitch_data *drv = priv; + return drv->ifname; +} + + +static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv, + u16 ports, const u8 *addr) +{ + u16 read1[3], read2[3], addr_be16[3]; + + wpa_driver_roboswitch_addr_be16(addr, addr_be16); + + if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, read1, 1) < 0) + return -1; + if (!(read1[0] & (1 << 4))) { + /* multiport addresses are not yet enabled */ + read1[0] |= 1 << 4; + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, read1, 1); + } else { + /* if both multiport addresses are the same we can add */ + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, read1, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, read2, 3); + if (os_memcmp(read1, read2, 6) != 0) + return -1; + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, read1, 1); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, read2, 1); + if (read1[0] != read2[0]) + return -1; + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_be16, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, &ports, 1); + } + return 0; +} + + +static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv, + u16 ports, const u8 *addr) +{ + u16 _read, addr_be16[3], addr_read[3], ports_read; + + wpa_driver_roboswitch_addr_be16(addr, addr_be16); + + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF, + &_read, 1); + /* If ARL control is disabled, there is nothing to leave. */ + if (!(_read & (1 << 4))) return -1; + + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + /* check if we occupy multiport address 1 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, 1); + /* and multiport address 2 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && + ports_read == ports) { + _read &= ~(1 << 4); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_CONF, &_read, + 1); + } else { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, + addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, + addr_read, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, + &ports_read, 1); + } + } else { + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_2, addr_read, 3); + wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_2, &ports_read, 1); + /* or multiport address 2 */ + if (os_memcmp(addr_read, addr_be16, 6) == 0 && + ports_read == ports) { + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_ADDR_1, + addr_read, 3); + wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE, + ROBO_ARLCTRL_VEC_1, + &ports_read, 1); + } else return -1; + } + return 0; +} + + +static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname) +{ + struct wpa_driver_roboswitch_data *drv; + char *sep; + u16 vlan = 0, _read[2]; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) return NULL; + drv->ctx = ctx; + drv->own_addr[0] = '\0'; + + /* copy ifname and take a pointer to the second to last character */ + sep = drv->ifname + + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)) - 2; + /* find the '.' seperating <interface> and <vlan> */ + while (sep > drv->ifname && *sep != '.') sep--; + if (sep <= drv->ifname) { + wpa_printf(MSG_INFO, "%s: No <interface>.<vlan> pair in " + "interface name %s", __func__, drv->ifname); + os_free(drv); + return NULL; + } + *sep = '\0'; + while (*++sep) { + if (*sep < '0' || *sep > '9') { + wpa_printf(MSG_INFO, "%s: Invalid vlan specification " + "in interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + vlan *= 10; + vlan += *sep - '0'; + if (vlan > ROBO_VLAN_MAX) { + wpa_printf(MSG_INFO, "%s: VLAN out of range in " + "interface name %s", __func__, ifname); + os_free(drv); + return NULL; + } + } + + drv->fd = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->fd < 0) { + wpa_printf(MSG_INFO, "%s: Unable to create socket", __func__); + os_free(drv); + return NULL; + } + + os_memset(&drv->ifr, 0, sizeof(drv->ifr)); + os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ); + if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) { + perror("ioctl[SIOCGMIIPHY]"); + os_free(drv); + return NULL; + } + if (if_mii(&drv->ifr)->phy_id != ROBO_PHY_ADDR) { + wpa_printf(MSG_INFO, "%s: Invalid phy address (not a " + "RoboSwitch?)", __func__); + os_free(drv); + return NULL; + } + + /* set and read back to see if the register can be used */ + _read[0] = ROBO_VLAN_MAX; + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, + _read, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_ACCESS_5350, + _read + 1, 1); + drv->is_5350 = _read[0] == _read[1]; + + /* set the read bit */ + vlan |= 1 << 13; + wpa_driver_roboswitch_write(drv, ROBO_VLAN_PAGE, + drv->is_5350 ? ROBO_VLAN_ACCESS_5350 + : ROBO_VLAN_ACCESS, + &vlan, 1); + wpa_driver_roboswitch_read(drv, ROBO_VLAN_PAGE, ROBO_VLAN_READ, _read, + drv->is_5350 ? 2 : 1); + if (!(drv->is_5350 ? _read[1] & (1 << 4) : _read[0] & (1 << 14))) { + wpa_printf(MSG_INFO, "%s: Could not get port information for " + "VLAN %d", __func__, vlan & ~(1 << 13)); + os_free(drv); + return NULL; + } + drv->ports = _read[0] & 0x001F; + /* add the MII port */ + drv->ports |= 1 << 8; + if (wpa_driver_roboswitch_join(drv, drv->ports, pae_group_addr) < 0) { + wpa_printf(MSG_INFO, "%s: Unable to join PAE group", __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Added PAE group address to " + "RoboSwitch ARL", __func__); + } + + return drv; +} + + +static void wpa_driver_roboswitch_deinit(void *priv) +{ + struct wpa_driver_roboswitch_data *drv = priv; + + if (drv->l2) { + l2_packet_deinit(drv->l2); + drv->l2 = NULL; + } + if (wpa_driver_roboswitch_leave(drv, drv->ports, pae_group_addr) < 0) { + wpa_printf(MSG_DEBUG, "%s: Unable to leave PAE group", + __func__); + } + + close(drv->fd); + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_roboswitch_ops = { + .name = "roboswitch", + .desc = "wpa_supplicant roboswitch driver", + .get_ssid = wpa_driver_roboswitch_get_ssid, + .get_bssid = wpa_driver_roboswitch_get_bssid, + .init = wpa_driver_roboswitch_init, + .deinit = wpa_driver_roboswitch_deinit, + .set_param = wpa_driver_roboswitch_set_param, + .get_ifname = wpa_driver_roboswitch_get_ifname, +}; diff --git a/contrib/wpa/src/drivers/driver_test.c b/contrib/wpa/src/drivers/driver_test.c new file mode 100644 index 0000000..2a41cf2 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_test.c @@ -0,0 +1,1230 @@ +/* + * WPA Supplicant - testing driver interface + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * 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. + */ + +/* Make dure we get winsock2.h for Windows build to get sockaddr_storage */ +#include "build_config.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock2.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/un.h> +#include <dirent.h> +#include <sys/stat.h> +#define DRIVER_TEST_UNIX +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "common.h" +#include "driver.h" +#include "l2_packet/l2_packet.h" +#include "eloop.h" +#include "sha1.h" +#include "ieee802_11_defs.h" + + +struct wpa_driver_test_global { + int dummy; +}; + +struct wpa_driver_test_data { + struct wpa_driver_test_global *global; + void *ctx; + u8 own_addr[ETH_ALEN]; + int test_socket; +#ifdef DRIVER_TEST_UNIX + struct sockaddr_un hostapd_addr; +#endif /* DRIVER_TEST_UNIX */ + int hostapd_addr_set; + struct sockaddr_in hostapd_addr_udp; + int hostapd_addr_udp_set; + char *own_socket_path; + char *test_dir; + u8 bssid[ETH_ALEN]; + u8 ssid[32]; + size_t ssid_len; +#define MAX_SCAN_RESULTS 30 + struct wpa_scan_res *scanres[MAX_SCAN_RESULTS]; + size_t num_scanres; + int use_associnfo; + u8 assoc_wpa_ie[80]; + size_t assoc_wpa_ie_len; + int use_mlme; + int associated; + u8 *probe_req_ie; + size_t probe_req_ie_len; +}; + + +static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + +#ifdef DRIVER_TEST_UNIX + if (drv->associated && drv->hostapd_addr_set) { + struct stat st; + if (stat(drv->hostapd_addr.sun_path, &st) < 0) { + wpa_printf(MSG_DEBUG, "%s: lost connection to AP: %s", + __func__, strerror(errno)); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + } + } +#endif /* DRIVER_TEST_UNIX */ + + eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); +} + + +static int wpa_driver_test_set_wpa(void *priv, int enabled) +{ + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + return 0; +} + + +static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +#ifdef DRIVER_TEST_UNIX +static void wpa_driver_scan_dir(struct wpa_driver_test_data *drv, + const char *path) +{ + struct dirent *dent; + DIR *dir; + struct sockaddr_un addr; + char cmd[512], *pos, *end; + int ret; + + dir = opendir(path); + if (dir == NULL) + return; + + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "SCAN " MACSTR, + MAC2STR(drv->own_addr)); + if (ret >= 0 && ret < end - pos) + pos += ret; + if (drv->probe_req_ie) { + ret = os_snprintf(pos, end - pos, " "); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, drv->probe_req_ie, + drv->probe_req_ie_len); + } + end[-1] = '\0'; + + while ((dent = readdir(dir))) { + if (os_strncmp(dent->d_name, "AP-", 3) != 0) + continue; + wpa_printf(MSG_DEBUG, "%s: SCAN %s", __func__, dent->d_name); + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/%s", + path, dent->d_name); + + if (sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("sendto(test_socket)"); + } + } + closedir(dir); +} +#endif /* DRIVER_TEST_UNIX */ + + +static int wpa_driver_test_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p", __func__, priv); + + drv->num_scanres = 0; + +#ifdef DRIVER_TEST_UNIX + if (drv->test_socket >= 0 && drv->test_dir) + wpa_driver_scan_dir(drv, drv->test_dir); + + if (drv->test_socket >= 0 && drv->hostapd_addr_set && + sendto(drv->test_socket, "SCAN", 4, 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + } +#endif /* DRIVER_TEST_UNIX */ + + if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && + sendto(drv->test_socket, "SCAN", 4, 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + } + + eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); + eloop_register_timeout(1, 0, wpa_driver_test_scan_timeout, drv, + drv->ctx); + return 0; +} + + +static struct wpa_scan_results * wpa_driver_test_get_scan_results2(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + struct wpa_scan_results *res; + size_t i; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) + return NULL; + + res->res = os_zalloc(drv->num_scanres * sizeof(struct wpa_scan_res *)); + if (res->res == NULL) { + os_free(res); + return NULL; + } + + for (i = 0; i < drv->num_scanres; i++) { + struct wpa_scan_res *r; + if (drv->scanres[i] == NULL) + continue; + r = os_malloc(sizeof(*r) + drv->scanres[i]->ie_len); + if (r == NULL) + break; + os_memcpy(r, drv->scanres[i], + sizeof(*r) + drv->scanres[i]->ie_len); + res->res[res->num++] = r; + } + + return res; +} + + +static int wpa_driver_test_set_key(void *priv, wpa_alg alg, const u8 *addr, + int key_idx, int set_tx, + const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + wpa_printf(MSG_DEBUG, "%s: priv=%p alg=%d key_idx=%d set_tx=%d", + __func__, priv, alg, key_idx, set_tx); + if (addr) { + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + } + if (seq) { + wpa_hexdump(MSG_DEBUG, " seq", seq, seq_len); + } + if (key) { + wpa_hexdump(MSG_DEBUG, " key", key, key_len); + } + return 0; +} + + +static int wpa_driver_test_associate( + void *priv, struct wpa_driver_associate_params *params) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d " + "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d", + __func__, priv, params->freq, params->pairwise_suite, + params->group_suite, params->key_mgmt_suite, + params->auth_alg, params->mode); + if (params->bssid) { + wpa_printf(MSG_DEBUG, " bssid=" MACSTR, + MAC2STR(params->bssid)); + } + if (params->ssid) { + wpa_hexdump_ascii(MSG_DEBUG, " ssid", + params->ssid, params->ssid_len); + } + if (params->wpa_ie) { + wpa_hexdump(MSG_DEBUG, " wpa_ie", + params->wpa_ie, params->wpa_ie_len); + drv->assoc_wpa_ie_len = params->wpa_ie_len; + if (drv->assoc_wpa_ie_len > sizeof(drv->assoc_wpa_ie)) + drv->assoc_wpa_ie_len = sizeof(drv->assoc_wpa_ie); + os_memcpy(drv->assoc_wpa_ie, params->wpa_ie, + drv->assoc_wpa_ie_len); + } else + drv->assoc_wpa_ie_len = 0; + +#ifdef DRIVER_TEST_UNIX + if (drv->test_dir && params->bssid) { + os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); + drv->hostapd_addr.sun_family = AF_UNIX; + os_snprintf(drv->hostapd_addr.sun_path, + sizeof(drv->hostapd_addr.sun_path), + "%s/AP-" MACSTR, + drv->test_dir, MAC2STR(params->bssid)); + drv->hostapd_addr_set = 1; + } +#endif /* DRIVER_TEST_UNIX */ + + if (drv->test_socket >= 0 && + (drv->hostapd_addr_set || drv->hostapd_addr_udp_set)) { + char cmd[200], *pos, *end; + int ret; + end = cmd + sizeof(cmd); + pos = cmd; + ret = os_snprintf(pos, end - pos, "ASSOC " MACSTR " ", + MAC2STR(drv->own_addr)); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, params->ssid, + params->ssid_len); + ret = os_snprintf(pos, end - pos, " "); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, params->wpa_ie, + params->wpa_ie_len); + end[-1] = '\0'; +#ifdef DRIVER_TEST_UNIX + if (drv->hostapd_addr_set && + sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + return -1; + } +#endif /* DRIVER_TEST_UNIX */ + if (drv->hostapd_addr_udp_set && + sendto(drv->test_socket, cmd, os_strlen(cmd), 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + return -1; + } + + os_memcpy(drv->ssid, params->ssid, params->ssid_len); + drv->ssid_len = params->ssid_len; + } else { + drv->associated = 1; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); + } + + return 0; +} + + +static int wpa_driver_test_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_test_data *drv = priv; + os_memcpy(bssid, drv->bssid, ETH_ALEN); + return 0; +} + + +static int wpa_driver_test_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_test_data *drv = priv; + os_memcpy(ssid, drv->ssid, 32); + return drv->ssid_len; +} + + +static int wpa_driver_test_send_disassoc(struct wpa_driver_test_data *drv) +{ +#ifdef DRIVER_TEST_UNIX + if (drv->test_socket >= 0 && + sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &drv->hostapd_addr, + sizeof(drv->hostapd_addr)) < 0) { + perror("sendto(test_socket)"); + return -1; + } +#endif /* DRIVER_TEST_UNIX */ + if (drv->test_socket >= 0 && drv->hostapd_addr_udp_set && + sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &drv->hostapd_addr_udp, + sizeof(drv->hostapd_addr_udp)) < 0) { + perror("sendto(test_socket)"); + return -1; + } + return 0; +} + + +static int wpa_driver_test_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + os_memset(drv->bssid, 0, ETH_ALEN); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + return wpa_driver_test_send_disassoc(drv); +} + + +static int wpa_driver_test_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s addr=" MACSTR " reason_code=%d", + __func__, MAC2STR(addr), reason_code); + os_memset(drv->bssid, 0, ETH_ALEN); + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); + return wpa_driver_test_send_disassoc(drv); +} + + +static void wpa_driver_test_scanresp(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const char *data) +{ + struct wpa_scan_res *res; + const char *pos, *pos2; + size_t len; + u8 *ie_pos, *ie_start, *ie_end; +#define MAX_IE_LEN 1000 + + wpa_printf(MSG_DEBUG, "test_driver: SCANRESP %s", data); + if (drv->num_scanres >= MAX_SCAN_RESULTS) { + wpa_printf(MSG_DEBUG, "test_driver: No room for the new scan " + "result"); + return; + } + + /* SCANRESP BSSID SSID IEs */ + + res = os_zalloc(sizeof(*res) + MAX_IE_LEN); + if (res == NULL) + return; + ie_start = ie_pos = (u8 *) (res + 1); + ie_end = ie_pos + MAX_IE_LEN; + + if (hwaddr_aton(data, res->bssid)) { + wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in scanres"); + os_free(res); + return; + } + + pos = data + 17; + while (*pos == ' ') + pos++; + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) { + wpa_printf(MSG_DEBUG, "test_driver: invalid SSID termination " + "in scanres"); + os_free(res); + return; + } + len = (pos2 - pos) / 2; + if (len > 32) + len = 32; + /* + * Generate SSID IE from the SSID field since this IE is not included + * in the main IE field. + */ + *ie_pos++ = WLAN_EID_SSID; + *ie_pos++ = len; + if (hexstr2bin(pos, ie_pos, len) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: invalid SSID in scanres"); + os_free(res); + return; + } + ie_pos += len; + + pos = pos2 + 1; + pos2 = os_strchr(pos, ' '); + if (pos2 == NULL) + len = os_strlen(pos) / 2; + else + len = (pos2 - pos) / 2; + if ((int) len > ie_end - ie_pos) + len = ie_end - ie_pos; + if (hexstr2bin(pos, ie_pos, len) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: invalid IEs in scanres"); + os_free(res); + return; + } + ie_pos += len; + res->ie_len = ie_pos - ie_start; + + if (pos2) { + pos = pos2 + 1; + while (*pos == ' ') + pos++; + if (os_strncmp(pos, "PRIVACY", 7) == 0) + res->caps |= IEEE80211_CAP_PRIVACY; + } + + os_free(drv->scanres[drv->num_scanres]); + drv->scanres[drv->num_scanres++] = res; +} + + +static void wpa_driver_test_assocresp(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const char *data) +{ + /* ASSOCRESP BSSID <res> */ + if (hwaddr_aton(data, drv->bssid)) { + wpa_printf(MSG_DEBUG, "test_driver: invalid BSSID in " + "assocresp"); + } + if (drv->use_associnfo) { + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.assoc_info.req_ies = drv->assoc_wpa_ie; + event.assoc_info.req_ies_len = drv->assoc_wpa_ie_len; + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &event); + } + drv->associated = 1; + wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL); +} + + +static void wpa_driver_test_disassoc(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen) +{ + drv->associated = 0; + wpa_supplicant_event(drv->ctx, EVENT_DISASSOC, NULL); +} + + +static void wpa_driver_test_eapol(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ + const u8 *src = drv->bssid; + + if (data_len > 14) { + /* Skip Ethernet header */ + src = data + ETH_ALEN; + data += 14; + data_len -= 14; + } + wpa_supplicant_rx_eapol(drv->ctx, src, data, data_len); +} + + +static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv, + struct sockaddr *from, + socklen_t fromlen, + const u8 *data, size_t data_len) +{ +#ifdef CONFIG_CLIENT_MLME + struct ieee80211_rx_status rx_status; + os_memset(&rx_status, 0, sizeof(rx_status)); + wpa_supplicant_sta_rx(drv->ctx, data, data_len, &rx_status); +#endif /* CONFIG_CLIENT_MLME */ +} + + +static void wpa_driver_test_receive_unix(int sock, void *eloop_ctx, + void *sock_ctx) +{ + struct wpa_driver_test_data *drv = eloop_ctx; + char *buf; + int res; + struct sockaddr_storage from; + socklen_t fromlen = sizeof(from); + const size_t buflen = 2000; + + buf = os_malloc(buflen); + if (buf == NULL) + return; + res = recvfrom(sock, buf, buflen - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(test_socket)"); + os_free(buf); + return; + } + buf[res] = '\0'; + + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); + + if (os_strncmp(buf, "SCANRESP ", 9) == 0) { + wpa_driver_test_scanresp(drv, (struct sockaddr *) &from, + fromlen, buf + 9); + } else if (os_strncmp(buf, "ASSOCRESP ", 10) == 0) { + wpa_driver_test_assocresp(drv, (struct sockaddr *) &from, + fromlen, buf + 10); + } else if (os_strcmp(buf, "DISASSOC") == 0) { + wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, + fromlen); + } else if (os_strcmp(buf, "DEAUTH") == 0) { + wpa_driver_test_disassoc(drv, (struct sockaddr *) &from, + fromlen); + } else if (os_strncmp(buf, "EAPOL ", 6) == 0) { + wpa_driver_test_eapol(drv, (struct sockaddr *) &from, fromlen, + (const u8 *) buf + 6, res - 6); + } else if (os_strncmp(buf, "MLME ", 5) == 0) { + wpa_driver_test_mlme(drv, (struct sockaddr *) &from, fromlen, + (const u8 *) buf + 5, res - 5); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", + (u8 *) buf, res); + } + os_free(buf); +} + + +static void * wpa_driver_test_init2(void *ctx, const char *ifname, + void *global_priv) +{ + struct wpa_driver_test_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->global = global_priv; + drv->ctx = ctx; + drv->test_socket = -1; + + /* Set dummy BSSID and SSID for testing. */ + drv->bssid[0] = 0x02; + drv->bssid[1] = 0x00; + drv->bssid[2] = 0x00; + drv->bssid[3] = 0x00; + drv->bssid[4] = 0x00; + drv->bssid[5] = 0x01; + os_memcpy(drv->ssid, "test", 5); + drv->ssid_len = 4; + + /* Generate a MAC address to help testing with multiple STAs */ + drv->own_addr[0] = 0x02; /* locally administered */ + sha1_prf((const u8 *) ifname, os_strlen(ifname), + "wpa_supplicant test mac addr generation", + NULL, 0, drv->own_addr + 1, ETH_ALEN - 1); + eloop_register_timeout(1, 0, wpa_driver_test_poll, drv, NULL); + + return drv; +} + + +static void wpa_driver_test_close_test_socket(struct wpa_driver_test_data *drv) +{ + if (drv->test_socket >= 0) { + eloop_unregister_read_sock(drv->test_socket); + close(drv->test_socket); + drv->test_socket = -1; + } + + if (drv->own_socket_path) { + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + } +} + + +static void wpa_driver_test_deinit(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + int i; + wpa_driver_test_close_test_socket(drv); + eloop_cancel_timeout(wpa_driver_test_scan_timeout, drv, drv->ctx); + eloop_cancel_timeout(wpa_driver_test_poll, drv, NULL); + os_free(drv->test_dir); + for (i = 0; i < MAX_SCAN_RESULTS; i++) + os_free(drv->scanres[i]); + os_free(drv->probe_req_ie); + os_free(drv); +} + + +static int wpa_driver_test_attach(struct wpa_driver_test_data *drv, + const char *dir) +{ +#ifdef DRIVER_TEST_UNIX + static unsigned int counter = 0; + struct sockaddr_un addr; + size_t len; + + os_free(drv->own_socket_path); + if (dir) { + len = os_strlen(dir) + 30; + drv->own_socket_path = os_malloc(len); + if (drv->own_socket_path == NULL) + return -1; + os_snprintf(drv->own_socket_path, len, "%s/STA-" MACSTR, + dir, MAC2STR(drv->own_addr)); + } else { + drv->own_socket_path = os_malloc(100); + if (drv->own_socket_path == NULL) + return -1; + os_snprintf(drv->own_socket_path, 100, + "/tmp/wpa_supplicant_test-%d-%d", + getpid(), counter++); + } + + drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_UNIX)"); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path)); + if (bind(drv->test_socket, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + close(drv->test_socket); + unlink(drv->own_socket_path); + os_free(drv->own_socket_path); + drv->own_socket_path = NULL; + return -1; + } + + eloop_register_read_sock(drv->test_socket, + wpa_driver_test_receive_unix, drv, NULL); + + return 0; +#else /* DRIVER_TEST_UNIX */ + return -1; +#endif /* DRIVER_TEST_UNIX */ +} + + +static int wpa_driver_test_attach_udp(struct wpa_driver_test_data *drv, + char *dst) +{ + char *pos; + + pos = os_strchr(dst, ':'); + if (pos == NULL) + return -1; + *pos++ = '\0'; + wpa_printf(MSG_DEBUG, "%s: addr=%s port=%s", __func__, dst, pos); + + drv->test_socket = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_INET)"); + return -1; + } + + os_memset(&drv->hostapd_addr_udp, 0, sizeof(drv->hostapd_addr_udp)); + drv->hostapd_addr_udp.sin_family = AF_INET; +#if defined(CONFIG_NATIVE_WINDOWS) || defined(CONFIG_ANSI_C_EXTRA) + { + int a[4]; + u8 *pos; + sscanf(dst, "%d.%d.%d.%d", &a[0], &a[1], &a[2], &a[3]); + pos = (u8 *) &drv->hostapd_addr_udp.sin_addr; + *pos++ = a[0]; + *pos++ = a[1]; + *pos++ = a[2]; + *pos++ = a[3]; + } +#else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + inet_aton(dst, &drv->hostapd_addr_udp.sin_addr); +#endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */ + drv->hostapd_addr_udp.sin_port = htons(atoi(pos)); + + drv->hostapd_addr_udp_set = 1; + + eloop_register_read_sock(drv->test_socket, + wpa_driver_test_receive_unix, drv, NULL); + + return 0; +} + + +static int wpa_driver_test_set_param(void *priv, const char *param) +{ + struct wpa_driver_test_data *drv = priv; + const char *pos; + + wpa_printf(MSG_DEBUG, "%s: param='%s'", __func__, param); + if (param == NULL) + return 0; + + wpa_driver_test_close_test_socket(drv); + +#ifdef DRIVER_TEST_UNIX + pos = os_strstr(param, "test_socket="); + if (pos) { + const char *pos2; + size_t len; + + pos += 12; + pos2 = os_strchr(pos, ' '); + if (pos2) + len = pos2 - pos; + else + len = os_strlen(pos); + if (len > sizeof(drv->hostapd_addr.sun_path)) + return -1; + os_memset(&drv->hostapd_addr, 0, sizeof(drv->hostapd_addr)); + drv->hostapd_addr.sun_family = AF_UNIX; + os_memcpy(drv->hostapd_addr.sun_path, pos, len); + drv->hostapd_addr_set = 1; + } +#endif /* DRIVER_TEST_UNIX */ + + pos = os_strstr(param, "test_dir="); + if (pos) { + char *end; + os_free(drv->test_dir); + drv->test_dir = os_strdup(pos + 9); + if (drv->test_dir == NULL) + return -1; + end = os_strchr(drv->test_dir, ' '); + if (end) + *end = '\0'; + if (wpa_driver_test_attach(drv, drv->test_dir)) + return -1; + } else { + pos = os_strstr(param, "test_udp="); + if (pos) { + char *dst, *epos; + dst = os_strdup(pos + 9); + if (dst == NULL) + return -1; + epos = os_strchr(dst, ' '); + if (epos) + *epos = '\0'; + if (wpa_driver_test_attach_udp(drv, dst)) + return -1; + os_free(dst); + } else if (wpa_driver_test_attach(drv, NULL)) + return -1; + } + + if (os_strstr(param, "use_associnfo=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use AssocInfo events"); + drv->use_associnfo = 1; + } + +#ifdef CONFIG_CLIENT_MLME + if (os_strstr(param, "use_mlme=1")) { + wpa_printf(MSG_DEBUG, "test_driver: Use internal MLME"); + drv->use_mlme = 1; + } +#endif /* CONFIG_CLIENT_MLME */ + + return 0; +} + + +static const u8 * wpa_driver_test_get_mac_addr(void *priv) +{ + struct wpa_driver_test_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __func__); + return drv->own_addr; +} + + +static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto, + const u8 *data, size_t data_len) +{ + struct wpa_driver_test_data *drv = priv; + char *msg; + size_t msg_len; + struct l2_ethhdr eth; + struct sockaddr *addr; + socklen_t alen; +#ifdef DRIVER_TEST_UNIX + struct sockaddr_un addr_un; +#endif /* DRIVER_TEST_UNIX */ + + wpa_hexdump(MSG_MSGDUMP, "test_send_eapol TX frame", data, data_len); + + os_memset(ð, 0, sizeof(eth)); + os_memcpy(eth.h_dest, dest, ETH_ALEN); + os_memcpy(eth.h_source, drv->own_addr, ETH_ALEN); + eth.h_proto = host_to_be16(proto); + + msg_len = 6 + sizeof(eth) + data_len; + msg = os_malloc(msg_len); + if (msg == NULL) + return -1; + os_memcpy(msg, "EAPOL ", 6); + os_memcpy(msg + 6, ð, sizeof(eth)); + os_memcpy(msg + 6 + sizeof(eth), data, data_len); + + if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || + drv->test_dir == NULL) { + if (drv->hostapd_addr_udp_set) { + addr = (struct sockaddr *) &drv->hostapd_addr_udp; + alen = sizeof(drv->hostapd_addr_udp); + } else { +#ifdef DRIVER_TEST_UNIX + addr = (struct sockaddr *) &drv->hostapd_addr; + alen = sizeof(drv->hostapd_addr); +#else /* DRIVER_TEST_UNIX */ + os_free(msg); + return -1; +#endif /* DRIVER_TEST_UNIX */ + } + } else { +#ifdef DRIVER_TEST_UNIX + struct stat st; + os_memset(&addr_un, 0, sizeof(addr_un)); + addr_un.sun_family = AF_UNIX; + os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), + "%s/STA-" MACSTR, drv->test_dir, MAC2STR(dest)); + if (stat(addr_un.sun_path, &st) < 0) { + os_snprintf(addr_un.sun_path, sizeof(addr_un.sun_path), + "%s/AP-" MACSTR, + drv->test_dir, MAC2STR(dest)); + } + addr = (struct sockaddr *) &addr_un; + alen = sizeof(addr_un); +#else /* DRIVER_TEST_UNIX */ + os_free(msg); + return -1; +#endif /* DRIVER_TEST_UNIX */ + } + + if (sendto(drv->test_socket, msg, msg_len, 0, addr, alen) < 0) { + perror("sendmsg(test_socket)"); + os_free(msg); + return -1; + } + + os_free(msg); + return 0; +} + + +static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_test_data *drv = priv; + os_memset(capa, 0, sizeof(*capa)); + capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE | + WPA_DRIVER_CAPA_KEY_MGMT_FT | + WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK; + capa->enc = WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104 | + WPA_DRIVER_CAPA_ENC_TKIP | + WPA_DRIVER_CAPA_ENC_CCMP; + capa->auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + if (drv->use_mlme) + capa->flags |= WPA_DRIVER_FLAGS_USER_SPACE_MLME; + + return 0; +} + + +static int wpa_driver_test_mlme_setprotection(void *priv, const u8 *addr, + int protect_type, + int key_type) +{ + wpa_printf(MSG_DEBUG, "%s: protect_type=%d key_type=%d", + __func__, protect_type, key_type); + + if (addr) { + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, + __func__, MAC2STR(addr)); + } + + return 0; +} + + +#ifdef CONFIG_CLIENT_MLME +static struct wpa_hw_modes * +wpa_driver_test_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct wpa_hw_modes *modes; + + *num_modes = 1; + *flags = 0; + modes = os_zalloc(*num_modes * sizeof(struct wpa_hw_modes)); + if (modes == NULL) + return NULL; + modes[0].mode = WPA_MODE_IEEE80211G; + modes[0].num_channels = 1; + modes[0].num_rates = 1; + modes[0].channels = os_zalloc(sizeof(struct wpa_channel_data)); + modes[0].rates = os_zalloc(sizeof(struct wpa_rate_data)); + if (modes[0].channels == NULL || modes[0].rates == NULL) { + wpa_supplicant_sta_free_hw_features(modes, *num_modes); + return NULL; + } + modes[0].channels[0].chan = 1; + modes[0].channels[0].freq = 2412; + modes[0].channels[0].flag = WPA_CHAN_W_SCAN | WPA_CHAN_W_ACTIVE_SCAN; + modes[0].rates[0].rate = 10; + modes[0].rates[0].flags = WPA_RATE_BASIC | WPA_RATE_SUPPORTED | + WPA_RATE_CCK | WPA_RATE_MANDATORY; + + return modes; +} + + +static int wpa_driver_test_set_channel(void *priv, wpa_hw_mode phymode, + int chan, int freq) +{ + wpa_printf(MSG_DEBUG, "%s: phymode=%d chan=%d freq=%d", + __func__, phymode, chan, freq); + return 0; +} + + +static int wpa_driver_test_send_mlme(void *priv, const u8 *data, + size_t data_len) +{ + struct wpa_driver_test_data *drv = priv; + struct msghdr msg; + struct iovec io[2]; + struct sockaddr_un addr; + const u8 *dest; + struct dirent *dent; + DIR *dir; + + wpa_hexdump(MSG_MSGDUMP, "test_send_mlme", data, data_len); + if (data_len < 10) + return -1; + dest = data + 4; + + io[0].iov_base = "MLME "; + io[0].iov_len = 5; + io[1].iov_base = (u8 *) data; + io[1].iov_len = data_len; + + os_memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + if (os_memcmp(dest, drv->bssid, ETH_ALEN) == 0 || + drv->test_dir == NULL) { + if (drv->hostapd_addr_udp_set) { + msg.msg_name = &drv->hostapd_addr_udp; + msg.msg_namelen = sizeof(drv->hostapd_addr_udp); + } else { +#ifdef DRIVER_TEST_UNIX + msg.msg_name = &drv->hostapd_addr; + msg.msg_namelen = sizeof(drv->hostapd_addr); +#endif /* DRIVER_TEST_UNIX */ + } + } else if (os_memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + { + dir = opendir(drv->test_dir); + if (dir == NULL) + return -1; + while ((dent = readdir(dir))) { +#ifdef _DIRENT_HAVE_D_TYPE + /* Skip the file if it is not a socket. + * Also accept DT_UNKNOWN (0) in case + * the C library or underlying file + * system does not support d_type. */ + if (dent->d_type != DT_SOCK && + dent->d_type != DT_UNKNOWN) + continue; +#endif /* _DIRENT_HAVE_D_TYPE */ + if (os_strcmp(dent->d_name, ".") == 0 || + os_strcmp(dent->d_name, "..") == 0) + continue; + wpa_printf(MSG_DEBUG, "%s: Send broadcast MLME to %s", + __func__, dent->d_name); + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/%s", drv->test_dir, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + + if (sendmsg(drv->test_socket, &msg, 0) < 0) + perror("sendmsg(test_socket)"); + } + closedir(dir); + return 0; + } else { + struct stat st; + os_memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/AP-" MACSTR, drv->test_dir, MAC2STR(dest)); + if (stat(addr.sun_path, &st) < 0) { + os_snprintf(addr.sun_path, sizeof(addr.sun_path), + "%s/STA-" MACSTR, + drv->test_dir, MAC2STR(dest)); + } + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + } + + if (sendmsg(drv->test_socket, &msg, 0) < 0) { + perror("sendmsg(test_socket)"); + return -1; + } + + return 0; +} + + +static int wpa_driver_test_mlme_add_sta(void *priv, const u8 *addr, + const u8 *supp_rates, + size_t supp_rates_len) +{ + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); + return 0; +} + + +static int wpa_driver_test_mlme_remove_sta(void *priv, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s: addr=" MACSTR, __func__, MAC2STR(addr)); + return 0; +} + + +static int wpa_driver_test_set_ssid(void *priv, const u8 *ssid, + size_t ssid_len) +{ + wpa_printf(MSG_DEBUG, "%s", __func__); + return 0; +} + + +static int wpa_driver_test_set_bssid(void *priv, const u8 *bssid) +{ + wpa_printf(MSG_DEBUG, "%s: bssid=" MACSTR, __func__, MAC2STR(bssid)); + return 0; +} +#endif /* CONFIG_CLIENT_MLME */ + + +static int wpa_driver_test_set_probe_req_ie(void *priv, const u8 *ies, + size_t ies_len) +{ + struct wpa_driver_test_data *drv = priv; + + os_free(drv->probe_req_ie); + if (ies) { + drv->probe_req_ie = os_malloc(ies_len); + if (drv->probe_req_ie == NULL) { + drv->probe_req_ie_len = 0; + return -1; + } + os_memcpy(drv->probe_req_ie, ies, ies_len); + drv->probe_req_ie_len = ies_len; + } else { + drv->probe_req_ie = NULL; + drv->probe_req_ie_len = 0; + } + return 0; +} + + +static void * wpa_driver_test_global_init(void) +{ + struct wpa_driver_test_global *global; + + global = os_zalloc(sizeof(*global)); + return global; +} + + +static void wpa_driver_test_global_deinit(void *priv) +{ + struct wpa_driver_test_global *global = priv; + os_free(global); +} + + +static struct wpa_interface_info * +wpa_driver_test_get_interfaces(void *global_priv) +{ + /* struct wpa_driver_test_global *global = priv; */ + struct wpa_interface_info *iface; + + iface = os_zalloc(sizeof(*iface)); + if (iface == NULL) + return iface; + iface->ifname = os_strdup("sta0"); + iface->desc = os_strdup("test interface 0"); + iface->drv_name = "test"; + iface->next = os_zalloc(sizeof(*iface)); + if (iface->next) { + iface->next->ifname = os_strdup("sta1"); + iface->next->desc = os_strdup("test interface 1"); + iface->next->drv_name = "test"; + } + + return iface; +} + + +const struct wpa_driver_ops wpa_driver_test_ops = { + "test", + "wpa_supplicant test driver", + wpa_driver_test_get_bssid, + wpa_driver_test_get_ssid, + wpa_driver_test_set_wpa, + wpa_driver_test_set_key, + NULL /* init */, + wpa_driver_test_deinit, + wpa_driver_test_set_param, + NULL /* set_countermeasures */, + NULL /* set_drop_unencrypted */, + wpa_driver_test_scan, + NULL /* get_scan_results */, + wpa_driver_test_deauthenticate, + wpa_driver_test_disassociate, + wpa_driver_test_associate, + NULL /* set_auth_alg */, + NULL /* add_pmkid */, + NULL /* remove_pmkid */, + NULL /* flush_pmkid */, + wpa_driver_test_get_capa, + NULL /* poll */, + NULL /* get_ifname */, + wpa_driver_test_get_mac_addr, + wpa_driver_test_send_eapol, + NULL /* set_operstate */, + wpa_driver_test_mlme_setprotection, +#ifdef CONFIG_CLIENT_MLME + wpa_driver_test_get_hw_feature_data, + wpa_driver_test_set_channel, + wpa_driver_test_set_ssid, + wpa_driver_test_set_bssid, + wpa_driver_test_send_mlme, + wpa_driver_test_mlme_add_sta, + wpa_driver_test_mlme_remove_sta, +#else /* CONFIG_CLIENT_MLME */ + NULL /* get_hw_feature_data */, + NULL /* set_channel */, + NULL /* set_ssid */, + NULL /* set_bssid */, + NULL /* send_mlme */, + NULL /* mlme_add_sta */, + NULL /* mlme_remove_sta */, +#endif /* CONFIG_CLIENT_MLME */ + NULL /* update_ft_ies */, + NULL /* send_ft_action */, + wpa_driver_test_get_scan_results2, + wpa_driver_test_set_probe_req_ie, + NULL /* set_mode */, + NULL /* set_country */, + wpa_driver_test_global_init, + wpa_driver_test_global_deinit, + wpa_driver_test_init2, + wpa_driver_test_get_interfaces +}; diff --git a/contrib/wpa/src/drivers/driver_wext.c b/contrib/wpa/src/drivers/driver_wext.c new file mode 100644 index 0000000..e771d37 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_wext.c @@ -0,0 +1,2375 @@ +/* + * WPA Supplicant - driver interaction with generic Linux Wireless Extensions + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * 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. + * + * This file implements a driver interface for the Linux Wireless Extensions. + * When used with WE-18 or newer, this interface can be used as-is with number + * of drivers. In addition to this, some of the common functions in this file + * can be used by other driver interface implementations that use generic WE + * ioctls, but require private ioctls for some of the functionality. + */ + +#include "includes.h" +#include <sys/ioctl.h> +#include <net/if_arp.h> + +#include "wireless_copy.h" +#include "common.h" +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "driver_wext.h" +#include "ieee802_11_defs.h" +#include "wpa_common.h" + + +static int wpa_driver_wext_flush_pmkid(void *priv); +static int wpa_driver_wext_get_range(void *priv); +static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv); +static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv); + + +static int wpa_driver_wext_send_oper_ifla(struct wpa_driver_wext_data *drv, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = drv->ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = (struct rtattr *) + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "WEXT: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(drv->event_sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "WEXT: Sending operstate IFLA failed: " + "%s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} + + +int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, + int idx, u32 value) +{ + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.param.flags = idx & IW_AUTH_INDEX; + iwr.u.param.value = value; + + if (ioctl(drv->ioctl_sock, SIOCSIWAUTH, &iwr) < 0) { + if (errno != EOPNOTSUPP) { + wpa_printf(MSG_DEBUG, "WEXT: SIOCSIWAUTH(param %d " + "value 0x%x) failed: %s)", + idx, value, strerror(errno)); + } + ret = errno == EOPNOTSUPP ? -2 : -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_get_bssid - Get BSSID, SIOCGIWAP + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @bssid: Buffer for BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_get_bssid(void *priv, u8 *bssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) { + perror("ioctl[SIOCGIWAP]"); + ret = -1; + } + os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN); + + return ret; +} + + +/** + * wpa_driver_wext_set_bssid - Set BSSID, SIOCSIWAP + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @bssid: BSSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.ap_addr.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(iwr.u.ap_addr.sa_data, bssid, ETH_ALEN); + else + os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN); + + if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) { + perror("ioctl[SIOCSIWAP]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_get_ssid - Get SSID, SIOCGIWESSID + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: Buffer for the SSID; must be at least 32 bytes long + * Returns: SSID length on success, -1 on failure + */ +int wpa_driver_wext_get_ssid(void *priv, u8 *ssid) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) ssid; + iwr.u.essid.length = 32; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else { + ret = iwr.u.essid.length; + if (ret > 32) + ret = 32; + /* Some drivers include nul termination in the SSID, so let's + * remove it here before further processing. WE-21 changes this + * to explicitly require the length _not_ to include nul + * termination. */ + if (ret > 0 && ssid[ret - 1] == '\0' && + drv->we_version_compiled < 21) + ret--; + } + + return ret; +} + + +/** + * wpa_driver_wext_set_ssid - Set SSID, SIOCSIWESSID + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: SSID + * @ssid_len: Length of SSID (0..32) + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + char buf[33]; + + if (ssid_len > 32) + return -1; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* flags: 1 = ESSID is active, 0 = not (promiscuous) */ + iwr.u.essid.flags = (ssid_len != 0); + os_memset(buf, 0, sizeof(buf)); + os_memcpy(buf, ssid, ssid_len); + iwr.u.essid.pointer = (caddr_t) buf; + if (drv->we_version_compiled < 21) { + /* For historic reasons, set SSID length to include one extra + * character, C string nul termination, even though SSID is + * really an octet string that should not be presented as a C + * string. Some Linux drivers decrement the length by one and + * can thus end up missing the last octet of the SSID if the + * length is not incremented here. WE-21 changes this to + * explicitly require the length _not_ to include nul + * termination. */ + if (ssid_len) + ssid_len++; + } + iwr.u.essid.length = ssid_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + ret = -1; + } + + return ret; +} + + +/** + * wpa_driver_wext_set_freq - Set frequency/channel, SIOCSIWFREQ + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @freq: Frequency in MHz + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_freq(void *priv, int freq) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.freq.m = freq * 100000; + iwr.u.freq.e = 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) { + perror("ioctl[SIOCSIWFREQ]"); + ret = -1; + } + + return ret; +} + + +static void +wpa_driver_wext_event_wireless_custom(void *ctx, char *custom) +{ + union wpa_event_data data; + + wpa_printf(MSG_MSGDUMP, "WEXT: Custom wireless event: '%s'", + custom); + + os_memset(&data, 0, sizeof(data)); + /* Host AP driver */ + if (os_strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + data.michael_mic_failure.unicast = + os_strstr(custom, " unicast ") != NULL; + /* TODO: parse parameters(?) */ + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + } else if (os_strncmp(custom, "ASSOCINFO(ReqIEs=", 17) == 0) { + char *spos; + int bytes; + + spos = custom + 17; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + return; + bytes /= 2; + + data.assoc_info.req_ies = os_malloc(bytes); + if (data.assoc_info.req_ies == NULL) + return; + + data.assoc_info.req_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.req_ies, bytes); + + spos += bytes * 2; + + data.assoc_info.resp_ies = NULL; + data.assoc_info.resp_ies_len = 0; + + if (os_strncmp(spos, " RespIEs=", 9) == 0) { + spos += 9; + + bytes = strspn(spos, "0123456789abcdefABCDEF"); + if (!bytes || (bytes & 1)) + goto done; + bytes /= 2; + + data.assoc_info.resp_ies = os_malloc(bytes); + if (data.assoc_info.resp_ies == NULL) + goto done; + + data.assoc_info.resp_ies_len = bytes; + hexstr2bin(spos, data.assoc_info.resp_ies, bytes); + } + + wpa_supplicant_event(ctx, EVENT_ASSOCINFO, &data); + + done: + os_free(data.assoc_info.resp_ies); + os_free(data.assoc_info.req_ies); +#ifdef CONFIG_PEERKEY + } else if (os_strncmp(custom, "STKSTART.request=", 17) == 0) { + if (hwaddr_aton(custom + 17, data.stkstart.peer)) { + wpa_printf(MSG_DEBUG, "WEXT: unrecognized " + "STKSTART.request '%s'", custom + 17); + return; + } + wpa_supplicant_event(ctx, EVENT_STKSTART, &data); +#endif /* CONFIG_PEERKEY */ + } +} + + +static int wpa_driver_wext_event_wireless_michaelmicfailure( + void *ctx, const char *ev, size_t len) +{ + const struct iw_michaelmicfailure *mic; + union wpa_event_data data; + + if (len < sizeof(*mic)) + return -1; + + mic = (const struct iw_michaelmicfailure *) ev; + + wpa_printf(MSG_DEBUG, "Michael MIC failure wireless event: " + "flags=0x%x src_addr=" MACSTR, mic->flags, + MAC2STR(mic->src_addr.sa_data)); + + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = !(mic->flags & IW_MICFAILURE_GROUP); + wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE, &data); + + return 0; +} + + +static int wpa_driver_wext_event_wireless_pmkidcand( + struct wpa_driver_wext_data *drv, const char *ev, size_t len) +{ + const struct iw_pmkid_cand *cand; + union wpa_event_data data; + const u8 *addr; + + if (len < sizeof(*cand)) + return -1; + + cand = (const struct iw_pmkid_cand *) ev; + addr = (const u8 *) cand->bssid.sa_data; + + wpa_printf(MSG_DEBUG, "PMKID candidate wireless event: " + "flags=0x%x index=%d bssid=" MACSTR, cand->flags, + cand->index, MAC2STR(addr)); + + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.pmkid_candidate.bssid, addr, ETH_ALEN); + data.pmkid_candidate.index = cand->index; + data.pmkid_candidate.preauth = cand->flags & IW_PMKID_CAND_PREAUTH; + wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data); + + return 0; +} + + +static int wpa_driver_wext_event_wireless_assocreqie( + struct wpa_driver_wext_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocReq IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = os_malloc(len); + if (drv->assoc_req_ies == NULL) { + drv->assoc_req_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_req_ies, ev, len); + drv->assoc_req_ies_len = len; + + return 0; +} + + +static int wpa_driver_wext_event_wireless_assocrespie( + struct wpa_driver_wext_data *drv, const char *ev, int len) +{ + if (len < 0) + return -1; + + wpa_hexdump(MSG_DEBUG, "AssocResp IE wireless event", (const u8 *) ev, + len); + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = os_malloc(len); + if (drv->assoc_resp_ies == NULL) { + drv->assoc_resp_ies_len = 0; + return -1; + } + os_memcpy(drv->assoc_resp_ies, ev, len); + drv->assoc_resp_ies_len = len; + + return 0; +} + + +static void wpa_driver_wext_event_assoc_ies(struct wpa_driver_wext_data *drv) +{ + union wpa_event_data data; + + if (drv->assoc_req_ies == NULL && drv->assoc_resp_ies == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + if (drv->assoc_req_ies) { + data.assoc_info.req_ies = drv->assoc_req_ies; + drv->assoc_req_ies = NULL; + data.assoc_info.req_ies_len = drv->assoc_req_ies_len; + } + if (drv->assoc_resp_ies) { + data.assoc_info.resp_ies = drv->assoc_resp_ies; + drv->assoc_resp_ies = NULL; + data.assoc_info.resp_ies_len = drv->assoc_resp_ies_len; + } + + wpa_supplicant_event(drv->ctx, EVENT_ASSOCINFO, &data); + + os_free(data.assoc_info.req_ies); + os_free(data.assoc_info.resp_ies); +} + + +static void wpa_driver_wext_event_wireless(struct wpa_driver_wext_data *drv, + void *ctx, 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. */ + os_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_compiled > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVCUSTOM || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVASSOCRESPIE || + iwe->cmd == IWEVPMKIDCAND)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + wpa_printf(MSG_DEBUG, "Wireless event: new AP: " + MACSTR, + MAC2STR((u8 *) iwe->u.ap_addr.sa_data)); + if (is_zero_ether_addr( + (const u8 *) iwe->u.ap_addr.sa_data) || + os_memcmp(iwe->u.ap_addr.sa_data, + "\x44\x44\x44\x44\x44\x44", ETH_ALEN) == + 0) { + os_free(drv->assoc_req_ies); + drv->assoc_req_ies = NULL; + os_free(drv->assoc_resp_ies); + drv->assoc_resp_ies = NULL; + wpa_supplicant_event(ctx, EVENT_DISASSOC, + NULL); + + } else { + wpa_driver_wext_event_assoc_ies(drv); + wpa_supplicant_event(ctx, EVENT_ASSOC, NULL); + } + break; + case IWEVMICHAELMICFAILURE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVMICHAELMICFAILURE length"); + return; + } + wpa_driver_wext_event_wireless_michaelmicfailure( + ctx, custom, iwe->u.data.length); + break; + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVCUSTOM length"); + return; + } + buf = os_malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; + os_memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + wpa_driver_wext_event_wireless_custom(ctx, buf); + os_free(buf); + break; + case SIOCGIWSCAN: + drv->scan_complete_events = 1; + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, + drv, ctx); + wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL); + break; + case IWEVASSOCREQIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVASSOCREQIE length"); + return; + } + wpa_driver_wext_event_wireless_assocreqie( + drv, custom, iwe->u.data.length); + break; + case IWEVASSOCRESPIE: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVASSOCRESPIE length"); + return; + } + wpa_driver_wext_event_wireless_assocrespie( + drv, custom, iwe->u.data.length); + break; + case IWEVPMKIDCAND: + if (custom + iwe->u.data.length > end) { + wpa_printf(MSG_DEBUG, "WEXT: Invalid " + "IWEVPMKIDCAND length"); + return; + } + wpa_driver_wext_event_wireless_pmkidcand( + drv, custom, iwe->u.data.length); + break; + } + + pos += iwe->len; + } +} + + +static void wpa_driver_wext_event_link(struct wpa_driver_wext_data *drv, + void *ctx, char *buf, size_t len, + int del) +{ + union wpa_event_data event; + + os_memset(&event, 0, sizeof(event)); + if (len > sizeof(event.interface_status.ifname)) + len = sizeof(event.interface_status.ifname) - 1; + os_memcpy(event.interface_status.ifname, buf, len); + event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED : + EVENT_INTERFACE_ADDED; + + wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s", + del ? "DEL" : "NEW", + event.interface_status.ifname, + del ? "removed" : "added"); + + if (os_strcmp(drv->ifname, event.interface_status.ifname) == 0) { + if (del) + drv->if_removed = 1; + else + drv->if_removed = 0; + } + + wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event); +} + + +static int wpa_driver_wext_own_ifname(struct wpa_driver_wext_data *drv, + struct nlmsghdr *h) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return 0; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_IFNAME) { + if (os_strcmp(((char *) attr) + rta_len, drv->ifname) + == 0) + return 1; + else + break; + } + attr = RTA_NEXT(attr, attrlen); + } + + return 0; +} + + +static int wpa_driver_wext_own_ifindex(struct wpa_driver_wext_data *drv, + int ifindex, struct nlmsghdr *h) +{ + if (drv->ifindex == ifindex || drv->ifindex2 == ifindex) + return 1; + + if (drv->if_removed && wpa_driver_wext_own_ifname(drv, h)) { + drv->ifindex = if_nametoindex(drv->ifname); + wpa_printf(MSG_DEBUG, "WEXT: Update ifindex for a removed " + "interface"); + wpa_driver_wext_finish_drv_init(drv); + return 1; + } + + return 0; +} + + +static void wpa_driver_wext_event_rtm_newlink(struct wpa_driver_wext_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + if (!wpa_driver_wext_own_ifindex(drv, ifi->ifi_index, h)) { + wpa_printf(MSG_DEBUG, "Ignore event for foreign ifindex %d", + ifi->ifi_index); + return; + } + + wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x " + "(%s%s%s%s)", + drv->operstate, ifi->ifi_flags, + (ifi->ifi_flags & IFF_UP) ? "[UP]" : "", + (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "", + (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "", + (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : ""); + /* + * Some drivers send the association event before the operup event--in + * this case, lifting operstate in wpa_driver_wext_set_operstate() + * fails. This will hit us when wpa_supplicant does not need to do + * IEEE 802.1X authentication + */ + if (drv->operstate == 1 && + (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP && + !(ifi->ifi_flags & IFF_RUNNING)) + wpa_driver_wext_send_oper_ifla(drv, -1, IF_OPER_UP); + + 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) { + wpa_driver_wext_event_wireless( + drv, ctx, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } else if (attr->rta_type == IFLA_IFNAME) { + wpa_driver_wext_event_link(drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 0); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_wext_event_rtm_dellink(struct wpa_driver_wext_data *drv, + void *ctx, struct nlmsghdr *h, + size_t len) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr * attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + 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_IFNAME) { + wpa_driver_wext_event_link(drv, ctx, + ((char *) attr) + rta_len, + attr->rta_len - rta_len, 1); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void wpa_driver_wext_event_receive(int sock, void *eloop_ctx, + void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + 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) { + wpa_printf(MSG_DEBUG, "Malformed netlink message: " + "len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + wpa_driver_wext_event_rtm_newlink(eloop_ctx, sock_ctx, + h, plen); + break; + case RTM_DELLINK: + wpa_driver_wext_event_rtm_dellink(eloop_ctx, sock_ctx, + h, plen); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "%d extra bytes in the end of netlink " + "message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + + +static int wpa_driver_wext_get_ifflags_ifname(struct wpa_driver_wext_data *drv, + const char *ifname, int *flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + return -1; + } + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +/** + * wpa_driver_wext_get_ifflags - Get interface flags (SIOCGIFFLAGS) + * @drv: driver_wext private data + * @flags: Pointer to returned flags value + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags) +{ + return wpa_driver_wext_get_ifflags_ifname(drv, drv->ifname, flags); +} + + +static int wpa_driver_wext_set_ifflags_ifname(struct wpa_driver_wext_data *drv, + const char *ifname, int flags) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("SIOCSIFFLAGS"); + return -1; + } + return 0; +} + + +/** + * wpa_driver_wext_set_ifflags - Set interface flags (SIOCSIFFLAGS) + * @drv: driver_wext private data + * @flags: New value for flags + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags) +{ + return wpa_driver_wext_set_ifflags_ifname(drv, drv->ifname, flags); +} + + +/** + * wpa_driver_wext_init - Initialize WE driver interface + * @ctx: context to be used when calling wpa_supplicant functions, + * e.g., wpa_supplicant_event() + * @ifname: interface name, e.g., wlan0 + * Returns: Pointer to private data, %NULL on failure + */ +void * wpa_driver_wext_init(void *ctx, const char *ifname) +{ + int s; + struct sockaddr_nl local; + struct wpa_driver_wext_data *drv; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + drv->ctx = ctx; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket(PF_INET,SOCK_DGRAM)"); + os_free(drv); + return NULL; + } + + s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (s < 0) { + perror("socket(PF_NETLINK,SOCK_RAW,NETLINK_ROUTE)"); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + os_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); + close(drv->ioctl_sock); + os_free(drv); + return NULL; + } + + eloop_register_read_sock(s, wpa_driver_wext_event_receive, drv, ctx); + drv->event_sock = s; + + drv->mlme_sock = -1; + + wpa_driver_wext_finish_drv_init(drv); + + return drv; +} + + +static void wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv) +{ + int flags; + + if (wpa_driver_wext_get_ifflags(drv, &flags) != 0) + printf("Could not get interface '%s' flags\n", drv->ifname); + else if (!(flags & IFF_UP)) { + if (wpa_driver_wext_set_ifflags(drv, flags | IFF_UP) != 0) { + printf("Could not set interface '%s' UP\n", + drv->ifname); + } else { + /* + * Wait some time to allow driver to initialize before + * starting configuring the driver. This seems to be + * needed at least some drivers that load firmware etc. + * when the interface is set up. + */ + wpa_printf(MSG_DEBUG, "Interface %s set UP - waiting " + "a second for the driver to complete " + "initialization", drv->ifname); + sleep(1); + } + } + + /* + * Make sure that the driver does not have any obsolete PMKID entries. + */ + wpa_driver_wext_flush_pmkid(drv); + + if (wpa_driver_wext_set_mode(drv, 0) < 0) { + printf("Could not configure driver to use managed mode\n"); + } + + wpa_driver_wext_get_range(drv); + + /* + * Unlock the driver's BSSID and force to a random SSID to clear any + * previous association the driver might have when the supplicant + * starts up. + */ + wpa_driver_wext_disconnect(drv); + + drv->ifindex = if_nametoindex(drv->ifname); + + if (os_strncmp(drv->ifname, "wlan", 4) == 0) { + /* + * Host AP driver may use both wlan# and wifi# interface in + * wireless events. Since some of the versions included WE-18 + * support, let's add the alternative ifindex also from + * driver_wext.c for the time being. This may be removed at + * some point once it is believed that old versions of the + * driver are not in use anymore. + */ + char ifname2[IFNAMSIZ + 1]; + os_strlcpy(ifname2, drv->ifname, sizeof(ifname2)); + os_memcpy(ifname2, "wifi", 4); + wpa_driver_wext_alternative_ifindex(drv, ifname2); + } + + wpa_driver_wext_send_oper_ifla(drv, 1, IF_OPER_DORMANT); +} + + +/** + * wpa_driver_wext_deinit - Deinitialize WE driver interface + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * + * Shut down driver interface and processing of driver events. Free + * private data buffer if one was allocated in wpa_driver_wext_init(). + */ +void wpa_driver_wext_deinit(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + int flags; + + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + + /* + * Clear possibly configured driver parameters in order to make it + * easier to use the driver after wpa_supplicant has been terminated. + */ + wpa_driver_wext_disconnect(drv); + + wpa_driver_wext_send_oper_ifla(priv, 0, IF_OPER_UP); + + eloop_unregister_read_sock(drv->event_sock); + if (drv->mlme_sock >= 0) + eloop_unregister_read_sock(drv->mlme_sock); + + if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) + (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); + + close(drv->event_sock); + close(drv->ioctl_sock); + if (drv->mlme_sock >= 0) + close(drv->mlme_sock); + os_free(drv->assoc_req_ies); + os_free(drv->assoc_resp_ies); + os_free(drv); +} + + +/** + * wpa_driver_wext_scan_timeout - Scan timeout to report scan completion + * @eloop_ctx: Unused + * @timeout_ctx: ctx argument given to wpa_driver_wext_init() + * + * This function can be used as registered timeout when starting a scan to + * generate a scan completed event if the driver does not report this. + */ +void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx) +{ + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); +} + + +/** + * wpa_driver_wext_scan - Request the driver to initiate scan + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @ssid: Specific SSID to scan for (ProbeReq) or %NULL to scan for + * all SSIDs (either active scan with broadcast SSID or passive + * scan + * @ssid_len: Length of the SSID + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0, timeout; + struct iw_scan_req req; + + if (ssid_len > IW_ESSID_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: too long SSID (%lu)", + __FUNCTION__, (unsigned long) ssid_len); + return -1; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + if (ssid && ssid_len) { + os_memset(&req, 0, sizeof(req)); + req.essid_len = ssid_len; + req.bssid.sa_family = ARPHRD_ETHER; + os_memset(req.bssid.sa_data, 0xff, ETH_ALEN); + os_memcpy(req.essid, ssid, ssid_len); + iwr.u.data.pointer = (caddr_t) &req; + iwr.u.data.length = sizeof(req); + iwr.u.data.flags = IW_SCAN_THIS_ESSID; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) { + perror("ioctl[SIOCSIWSCAN]"); + ret = -1; + } + + /* Not all drivers generate "scan completed" wireless event, so try to + * read results after a timeout. */ + timeout = 5; + if (drv->scan_complete_events) { + /* + * The driver seems to deliver SIOCGIWSCAN events to notify + * when scan is complete, so use longer timeout to avoid race + * conditions with scanning and following association request. + */ + timeout = 30; + } + wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d " + "seconds", ret, timeout); + eloop_cancel_timeout(wpa_driver_wext_scan_timeout, drv, drv->ctx); + eloop_register_timeout(timeout, 0, wpa_driver_wext_scan_timeout, drv, + drv->ctx); + + return ret; +} + + +static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv, + size_t *len) +{ + struct iwreq iwr; + u8 *res_buf; + size_t res_buf_len; + + res_buf_len = IW_SCAN_MAX_DATA; + for (;;) { + res_buf = os_malloc(res_buf_len); + if (res_buf == NULL) + return NULL; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = res_buf; + iwr.u.data.length = res_buf_len; + + if (ioctl(drv->ioctl_sock, SIOCGIWSCAN, &iwr) == 0) + break; + + if (errno == E2BIG && res_buf_len < 65535) { + os_free(res_buf); + res_buf = NULL; + res_buf_len *= 2; + if (res_buf_len > 65535) + res_buf_len = 65535; /* 16-bit length field */ + wpa_printf(MSG_DEBUG, "Scan results did not fit - " + "trying larger buffer (%lu bytes)", + (unsigned long) res_buf_len); + } else { + perror("ioctl[SIOCGIWSCAN]"); + os_free(res_buf); + return NULL; + } + } + + if (iwr.u.data.length > res_buf_len) { + os_free(res_buf); + return NULL; + } + *len = iwr.u.data.length; + + return res_buf; +} + + +/* + * Data structure for collecting WEXT scan results. This is needed to allow + * the various methods of reporting IEs to be combined into a single IE buffer. + */ +struct wext_scan_data { + struct wpa_scan_res res; + u8 *ie; + size_t ie_len; + u8 ssid[32]; + size_t ssid_len; + int maxrate; +}; + + +static void wext_get_scan_mode(struct iw_event *iwe, + struct wext_scan_data *res) +{ + if (iwe->u.mode == IW_MODE_ADHOC) + res->res.caps |= IEEE80211_CAP_IBSS; + else if (iwe->u.mode == IW_MODE_MASTER || iwe->u.mode == IW_MODE_INFRA) + res->res.caps |= IEEE80211_CAP_ESS; +} + + +static void wext_get_scan_ssid(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + int ssid_len = iwe->u.essid.length; + if (custom + ssid_len > end) + return; + if (iwe->u.essid.flags && + ssid_len > 0 && + ssid_len <= IW_ESSID_MAX_SIZE) { + os_memcpy(res->ssid, custom, ssid_len); + res->ssid_len = ssid_len; + } +} + + +static void wext_get_scan_freq(struct iw_event *iwe, + struct wext_scan_data *res) +{ + int divi = 1000000, i; + + if (iwe->u.freq.e == 0) { + /* + * Some drivers do not report frequency, but a channel. + * Try to map this to frequency by assuming they are using + * IEEE 802.11b/g. But don't overwrite a previously parsed + * frequency if the driver sends both frequency and channel, + * since the driver may be sending an A-band channel that we + * don't handle here. + */ + + if (res->res.freq) + return; + + if (iwe->u.freq.m >= 1 && iwe->u.freq.m <= 13) { + res->res.freq = 2407 + 5 * iwe->u.freq.m; + return; + } else if (iwe->u.freq.m == 14) { + res->res.freq = 2484; + return; + } + } + + if (iwe->u.freq.e > 6) { + wpa_printf(MSG_DEBUG, "Invalid freq in scan results (BSSID=" + MACSTR " m=%d e=%d)", + MAC2STR(res->res.bssid), iwe->u.freq.m, + iwe->u.freq.e); + return; + } + + for (i = 0; i < iwe->u.freq.e; i++) + divi /= 10; + res->res.freq = iwe->u.freq.m / divi; +} + + +static void wext_get_scan_qual(struct iw_event *iwe, + struct wext_scan_data *res) +{ + res->res.qual = iwe->u.qual.qual; + res->res.noise = iwe->u.qual.noise; + res->res.level = iwe->u.qual.level; +} + + +static void wext_get_scan_encode(struct iw_event *iwe, + struct wext_scan_data *res) +{ + if (!(iwe->u.data.flags & IW_ENCODE_DISABLED)) + res->res.caps |= IEEE80211_CAP_PRIVACY; +} + + +static void wext_get_scan_rate(struct iw_event *iwe, + struct wext_scan_data *res, char *pos, + char *end) +{ + int maxrate; + char *custom = pos + IW_EV_LCP_LEN; + struct iw_param p; + size_t clen; + + clen = iwe->len; + if (custom + clen > end) + return; + maxrate = 0; + while (((ssize_t) clen) >= (ssize_t) sizeof(struct iw_param)) { + /* Note: may be misaligned, make a local, aligned copy */ + os_memcpy(&p, custom, sizeof(struct iw_param)); + if (p.value > maxrate) + maxrate = p.value; + clen -= sizeof(struct iw_param); + custom += sizeof(struct iw_param); + } + + /* Convert the maxrate from WE-style (b/s units) to + * 802.11 rates (500000 b/s units). + */ + res->maxrate = maxrate / 500000; +} + + +static void wext_get_scan_iwevgenie(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + char *genie, *gpos, *gend; + u8 *tmp; + + if (iwe->u.data.length == 0) + return; + + gpos = genie = custom; + gend = genie + iwe->u.data.length; + if (gend > end) { + wpa_printf(MSG_INFO, "IWEVGENIE overflow"); + return; + } + + tmp = os_realloc(res->ie, res->ie_len + gend - gpos); + if (tmp == NULL) + return; + os_memcpy(tmp + res->ie_len, gpos, gend - gpos); + res->ie = tmp; + res->ie_len += gend - gpos; +} + + +static void wext_get_scan_custom(struct iw_event *iwe, + struct wext_scan_data *res, char *custom, + char *end) +{ + size_t clen; + u8 *tmp; + + clen = iwe->u.data.length; + if (custom + clen > end) + return; + + if (clen > 7 && os_strncmp(custom, "wpa_ie=", 7) == 0) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1 || bytes == 0) + return; + bytes /= 2; + tmp = os_realloc(res->ie, res->ie_len + bytes); + if (tmp == NULL) + return; + hexstr2bin(spos, tmp + res->ie_len, bytes); + res->ie = tmp; + res->ie_len += bytes; + } else if (clen > 7 && os_strncmp(custom, "rsn_ie=", 7) == 0) { + char *spos; + int bytes; + spos = custom + 7; + bytes = custom + clen - spos; + if (bytes & 1 || bytes == 0) + return; + bytes /= 2; + tmp = os_realloc(res->ie, res->ie_len + bytes); + if (tmp == NULL) + return; + hexstr2bin(spos, tmp + res->ie_len, bytes); + res->ie = tmp; + res->ie_len += bytes; + } else if (clen > 4 && os_strncmp(custom, "tsf=", 4) == 0) { + char *spos; + int bytes; + u8 bin[8]; + spos = custom + 4; + bytes = custom + clen - spos; + if (bytes != 16) { + wpa_printf(MSG_INFO, "Invalid TSF length (%d)", bytes); + return; + } + bytes /= 2; + hexstr2bin(spos, bin, bytes); + res->res.tsf += WPA_GET_BE64(bin); + } +} + + +static int wext_19_iw_point(struct wpa_driver_wext_data *drv, u16 cmd) +{ + return drv->we_version_compiled > 18 && + (cmd == SIOCGIWESSID || cmd == SIOCGIWENCODE || + cmd == IWEVGENIE || cmd == IWEVCUSTOM); +} + + +static void wpa_driver_wext_add_scan_entry(struct wpa_scan_results *res, + struct wext_scan_data *data) +{ + struct wpa_scan_res **tmp; + struct wpa_scan_res *r; + size_t extra_len; + u8 *pos, *end, *ssid_ie = NULL, *rate_ie = NULL; + + /* Figure out whether we need to fake any IEs */ + pos = data->ie; + end = pos + data->ie_len; + while (pos && pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_SSID) + ssid_ie = pos; + else if (pos[0] == WLAN_EID_SUPP_RATES) + rate_ie = pos; + else if (pos[0] == WLAN_EID_EXT_SUPP_RATES) + rate_ie = pos; + pos += 2 + pos[1]; + } + + extra_len = 0; + if (ssid_ie == NULL) + extra_len += 2 + data->ssid_len; + if (rate_ie == NULL && data->maxrate) + extra_len += 3; + + r = os_zalloc(sizeof(*r) + extra_len + data->ie_len); + if (r == NULL) + return; + os_memcpy(r, &data->res, sizeof(*r)); + r->ie_len = extra_len + data->ie_len; + pos = (u8 *) (r + 1); + if (ssid_ie == NULL) { + /* + * Generate a fake SSID IE since the driver did not report + * a full IE list. + */ + *pos++ = WLAN_EID_SSID; + *pos++ = data->ssid_len; + os_memcpy(pos, data->ssid, data->ssid_len); + pos += data->ssid_len; + } + if (rate_ie == NULL && data->maxrate) { + /* + * Generate a fake Supported Rates IE since the driver did not + * report a full IE list. + */ + *pos++ = WLAN_EID_SUPP_RATES; + *pos++ = 1; + *pos++ = data->maxrate; + } + if (data->ie) + os_memcpy(pos, data->ie, data->ie_len); + + tmp = os_realloc(res->res, + (res->num + 1) * sizeof(struct wpa_scan_res *)); + if (tmp == NULL) { + os_free(r); + return; + } + tmp[res->num++] = r; + res->res = tmp; +} + + +/** + * wpa_driver_wext_get_scan_results - Fetch the latest scan results + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * Returns: Scan results on success, -1 on failure + */ +struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + size_t ap_num = 0, len; + int first; + u8 *res_buf; + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom; + struct wpa_scan_results *res; + struct wext_scan_data data; + + res_buf = wpa_driver_wext_giwscan(drv, &len); + if (res_buf == NULL) + return NULL; + + ap_num = 0; + first = 1; + + res = os_zalloc(sizeof(*res)); + if (res == NULL) { + os_free(res_buf); + return NULL; + } + + pos = (char *) res_buf; + end = (char *) res_buf + len; + os_memset(&data, 0, sizeof(data)); + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + os_memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + if (iwe->len <= IW_EV_LCP_LEN) + break; + + custom = pos + IW_EV_POINT_LEN; + if (wext_19_iw_point(drv, iwe->cmd)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + os_memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + os_memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case SIOCGIWAP: + if (!first) + wpa_driver_wext_add_scan_entry(res, &data); + first = 0; + os_free(data.ie); + os_memset(&data, 0, sizeof(data)); + os_memcpy(data.res.bssid, + iwe->u.ap_addr.sa_data, ETH_ALEN); + break; + case SIOCGIWMODE: + wext_get_scan_mode(iwe, &data); + break; + case SIOCGIWESSID: + wext_get_scan_ssid(iwe, &data, custom, end); + break; + case SIOCGIWFREQ: + wext_get_scan_freq(iwe, &data); + break; + case IWEVQUAL: + wext_get_scan_qual(iwe, &data); + break; + case SIOCGIWENCODE: + wext_get_scan_encode(iwe, &data); + break; + case SIOCGIWRATE: + wext_get_scan_rate(iwe, &data, pos, end); + break; + case IWEVGENIE: + wext_get_scan_iwevgenie(iwe, &data, custom, end); + break; + case IWEVCUSTOM: + wext_get_scan_custom(iwe, &data, custom, end); + break; + } + + pos += iwe->len; + } + os_free(res_buf); + res_buf = NULL; + if (!first) + wpa_driver_wext_add_scan_entry(res, &data); + os_free(data.ie); + + wpa_printf(MSG_DEBUG, "Received %lu bytes of scan results (%lu BSSes)", + (unsigned long) len, (unsigned long) res->num); + + return res; +} + + +static int wpa_driver_wext_get_range(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + /* + * 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; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, 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]"); + os_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->has_capability = 1; + drv->we_version_compiled = range->we_version_compiled; + if (range->enc_capa & IW_ENC_CAPA_WPA) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA | + WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK; + } + if (range->enc_capa & IW_ENC_CAPA_WPA2) { + drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA2 | + WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK; + } + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_WEP40 | + WPA_DRIVER_CAPA_ENC_WEP104; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_TKIP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_TKIP; + if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP) + drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP; + if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE) + drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE; + drv->capa.auth = WPA_DRIVER_AUTH_OPEN | + WPA_DRIVER_AUTH_SHARED | + WPA_DRIVER_AUTH_LEAP; + + wpa_printf(MSG_DEBUG, " capabilities: key_mgmt 0x%x enc 0x%x " + "flags 0x%x", + drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags); + } else { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - " + "assuming WPA is not supported"); + } + + os_free(range); + return 0; +} + + +static int wpa_driver_wext_set_wpa(void *priv, int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + return wpa_driver_wext_set_auth_param(drv, IW_AUTH_WPA_ENABLED, + enabled); +} + + +static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv, + const u8 *psk) +{ + struct iw_encode_ext *ext; + struct iwreq iwr; + int ret; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) + return 0; + + if (!psk) + return 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + + ext = os_zalloc(sizeof(*ext) + PMK_LEN); + if (ext == NULL) + return -1; + + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + PMK_LEN; + ext->key_len = PMK_LEN; + os_memcpy(&ext->key, psk, ext->key_len); + ext->alg = IW_ENCODE_ALG_PMK; + + ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr); + if (ret < 0) + perror("ioctl[SIOCSIWENCODEEXT] PMK"); + os_free(ext); + + return ret; +} + + +static int wpa_driver_wext_set_key_ext(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, + size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + struct iw_encode_ext *ext; + + if (seq_len > IW_ENCODE_SEQ_MAX_SIZE) { + wpa_printf(MSG_DEBUG, "%s: Invalid seq_len %lu", + __FUNCTION__, (unsigned long) seq_len); + return -1; + } + + ext = os_zalloc(sizeof(*ext) + key_len); + if (ext == NULL) + return -1; + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) ext; + iwr.u.encoding.length = sizeof(*ext) + key_len; + + if (addr == NULL || + os_memcmp(addr, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0) + ext->ext_flags |= IW_ENCODE_EXT_GROUP_KEY; + if (set_tx) + ext->ext_flags |= IW_ENCODE_EXT_SET_TX_KEY; + + ext->addr.sa_family = ARPHRD_ETHER; + if (addr) + os_memcpy(ext->addr.sa_data, addr, ETH_ALEN); + else + os_memset(ext->addr.sa_data, 0xff, ETH_ALEN); + if (key && key_len) { + os_memcpy(ext + 1, key, key_len); + ext->key_len = key_len; + } + switch (alg) { + case WPA_ALG_NONE: + ext->alg = IW_ENCODE_ALG_NONE; + break; + case WPA_ALG_WEP: + ext->alg = IW_ENCODE_ALG_WEP; + break; + case WPA_ALG_TKIP: + ext->alg = IW_ENCODE_ALG_TKIP; + break; + case WPA_ALG_CCMP: + ext->alg = IW_ENCODE_ALG_CCMP; + break; + case WPA_ALG_PMK: + ext->alg = IW_ENCODE_ALG_PMK; + break; +#ifdef CONFIG_IEEE80211W + case WPA_ALG_IGTK: + ext->alg = IW_ENCODE_ALG_AES_CMAC; + break; +#endif /* CONFIG_IEEE80211W */ + default: + wpa_printf(MSG_DEBUG, "%s: Unknown algorithm %d", + __FUNCTION__, alg); + os_free(ext); + return -1; + } + + if (seq && seq_len) { + ext->ext_flags |= IW_ENCODE_EXT_RX_SEQ_VALID; + os_memcpy(ext->rx_seq, seq, seq_len); + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr) < 0) { + ret = errno == EOPNOTSUPP ? -2 : -1; + if (errno == ENODEV) { + /* + * ndiswrapper seems to be returning incorrect error + * code.. */ + ret = -2; + } + + perror("ioctl[SIOCSIWENCODEEXT]"); + } + + os_free(ext); + return ret; +} + + +/** + * wpa_driver_wext_set_key - Configure encryption key + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @priv: Private driver interface data + * @alg: Encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, + * %WPA_ALG_TKIP, %WPA_ALG_CCMP); %WPA_ALG_NONE clears the key. + * @addr: Address of the peer STA or ff:ff:ff:ff:ff:ff for + * broadcast/default keys + * @key_idx: key index (0..3), usually 0 for unicast keys + * @set_tx: Configure this key as the default Tx key (only used when + * driver does not support separate unicast/individual key + * @seq: Sequence number/packet number, seq_len octets, the next + * packet number to be used for in replay protection; configured + * for Rx keys (in most cases, this is only used with broadcast + * keys and set to zero for unicast keys) + * @seq_len: Length of the seq, depends on the algorithm: + * TKIP: 6 octets, CCMP: 6 octets + * @key: Key buffer; TKIP: 16-byte temporal key, 8-byte Tx Mic key, + * 8-byte Rx Mic Key + * @key_len: Length of the key buffer in octets (WEP: 5 or 13, + * TKIP: 32, CCMP: 16) + * Returns: 0 on success, -1 on failure + * + * This function uses SIOCSIWENCODEEXT by default, but tries to use + * SIOCSIWENCODE if the extended ioctl fails when configuring a WEP key. + */ +int wpa_driver_wext_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "%s: alg=%d key_idx=%d set_tx=%d seq_len=%lu " + "key_len=%lu", + __FUNCTION__, alg, key_idx, set_tx, + (unsigned long) seq_len, (unsigned long) key_len); + + ret = wpa_driver_wext_set_key_ext(drv, alg, addr, key_idx, set_tx, + seq, seq_len, key, key_len); + if (ret == 0) + return 0; + + if (ret == -2 && + (alg == WPA_ALG_NONE || alg == WPA_ALG_WEP)) { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT, trying SIOCSIWENCODE"); + ret = 0; + } else { + wpa_printf(MSG_DEBUG, "Driver did not support " + "SIOCSIWENCODEEXT"); + return ret; + } + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + if (alg == WPA_ALG_NONE) + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + iwr.u.encoding.pointer = (caddr_t) key; + iwr.u.encoding.length = key_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + if (set_tx && alg != WPA_ALG_NONE) { + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.encoding.flags = key_idx + 1; + iwr.u.encoding.flags |= IW_ENCODE_TEMP; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE] (set_tx)"); + ret = -1; + } + } + + return ret; +} + + +static int wpa_driver_wext_set_countermeasures(void *priv, + int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + return wpa_driver_wext_set_auth_param(drv, + IW_AUTH_TKIP_COUNTERMEASURES, + enabled); +} + + +static int wpa_driver_wext_set_drop_unencrypted(void *priv, + int enabled) +{ + struct wpa_driver_wext_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + drv->use_crypt = enabled; + return wpa_driver_wext_set_auth_param(drv, IW_AUTH_DROP_UNENCRYPTED, + enabled); +} + + +static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv, + const u8 *addr, int cmd, int reason_code) +{ + struct iwreq iwr; + struct iw_mlme mlme; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&mlme, 0, sizeof(mlme)); + mlme.cmd = cmd; + mlme.reason_code = reason_code; + mlme.addr.sa_family = ARPHRD_ETHER; + os_memcpy(mlme.addr.sa_data, addr, ETH_ALEN); + iwr.u.data.pointer = (caddr_t) &mlme; + iwr.u.data.length = sizeof(mlme); + + if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) { + perror("ioctl[SIOCSIWMLME]"); + ret = -1; + } + + return ret; +} + + +static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv) +{ + struct iwreq iwr; + const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 }; + u8 ssid[32]; + int i; + + /* + * Only force-disconnect when the card is in infrastructure mode, + * otherwise the driver might interpret the cleared BSSID and random + * SSID as an attempt to create a new ad-hoc network. + */ + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { + perror("ioctl[SIOCGIWMODE]"); + iwr.u.mode = IW_MODE_INFRA; + } + + if (iwr.u.mode == IW_MODE_INFRA) { + /* + * Clear the BSSID selection and set a random SSID to make sure + * the driver will not be trying to associate with something + * even if it does not understand SIOCSIWMLME commands (or + * tries to associate automatically after deauth/disassoc). + */ + wpa_driver_wext_set_bssid(drv, null_bssid); + + for (i = 0; i < 32; i++) + ssid[i] = rand() & 0xFF; + wpa_driver_wext_set_ssid(drv, ssid, 32); + } +} + + +static int wpa_driver_wext_deauthenticate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_wext_data *drv = priv; + int ret; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DEAUTH, reason_code); + wpa_driver_wext_disconnect(drv); + return ret; +} + + +static int wpa_driver_wext_disassociate(void *priv, const u8 *addr, + int reason_code) +{ + struct wpa_driver_wext_data *drv = priv; + int ret; + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + ret = wpa_driver_wext_mlme(drv, addr, IW_MLME_DISASSOC, reason_code); + wpa_driver_wext_disconnect(drv); + return ret; +} + + +static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie, + size_t ie_len) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) ie; + iwr.u.data.length = ie_len; + + if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) { + perror("ioctl[SIOCSIWGENIE]"); + ret = -1; + } + + return ret; +} + + +int wpa_driver_wext_cipher2wext(int cipher) +{ + switch (cipher) { + case CIPHER_NONE: + return IW_AUTH_CIPHER_NONE; + case CIPHER_WEP40: + return IW_AUTH_CIPHER_WEP40; + case CIPHER_TKIP: + return IW_AUTH_CIPHER_TKIP; + case CIPHER_CCMP: + return IW_AUTH_CIPHER_CCMP; + case CIPHER_WEP104: + return IW_AUTH_CIPHER_WEP104; + default: + return 0; + } +} + + +int wpa_driver_wext_keymgmt2wext(int keymgmt) +{ + switch (keymgmt) { + case KEY_MGMT_802_1X: + case KEY_MGMT_802_1X_NO_WPA: + return IW_AUTH_KEY_MGMT_802_1X; + case KEY_MGMT_PSK: + return IW_AUTH_KEY_MGMT_PSK; + default: + return 0; + } +} + + +static int +wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv, + struct wpa_driver_associate_params *params) +{ + struct iwreq iwr; + int ret = 0; + + wpa_printf(MSG_DEBUG, "WEXT: Driver did not support " + "SIOCSIWAUTH for AUTH_ALG, trying SIOCSIWENCODE"); + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + /* Just changing mode, not actual keys */ + iwr.u.encoding.flags = 0; + iwr.u.encoding.pointer = (caddr_t) NULL; + iwr.u.encoding.length = 0; + + /* + * Note: IW_ENCODE_{OPEN,RESTRICTED} can be interpreted to mean two + * different things. Here they are used to indicate Open System vs. + * Shared Key authentication algorithm. However, some drivers may use + * them to select between open/restricted WEP encrypted (open = allow + * both unencrypted and encrypted frames; restricted = only allow + * encrypted frames). + */ + + if (!drv->use_crypt) { + iwr.u.encoding.flags |= IW_ENCODE_DISABLED; + } else { + if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + iwr.u.encoding.flags |= IW_ENCODE_OPEN; + if (params->auth_alg & AUTH_ALG_SHARED_KEY) + iwr.u.encoding.flags |= IW_ENCODE_RESTRICTED; + } + + if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) { + perror("ioctl[SIOCSIWENCODE]"); + ret = -1; + } + + return ret; +} + + +int wpa_driver_wext_associate(void *priv, + struct wpa_driver_associate_params *params) +{ + struct wpa_driver_wext_data *drv = priv; + int ret = 0; + int allow_unencrypted_eapol; + int value; + + wpa_printf(MSG_DEBUG, "%s", __FUNCTION__); + + /* + * If the driver did not support SIOCSIWAUTH, fallback to + * SIOCSIWENCODE here. + */ + if (drv->auth_alg_fallback && + wpa_driver_wext_auth_alg_fallback(drv, params) < 0) + ret = -1; + + if (!params->bssid && + wpa_driver_wext_set_bssid(drv, NULL) < 0) + ret = -1; + + /* TODO: should consider getting wpa version and cipher/key_mgmt suites + * from configuration, not from here, where only the selected suite is + * available */ + if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len) + < 0) + ret = -1; + if (params->wpa_ie == NULL || params->wpa_ie_len == 0) + value = IW_AUTH_WPA_VERSION_DISABLED; + else if (params->wpa_ie[0] == WLAN_EID_RSN) + value = IW_AUTH_WPA_VERSION_WPA2; + else + value = IW_AUTH_WPA_VERSION_WPA; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_WPA_VERSION, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->pairwise_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_PAIRWISE, value) < 0) + ret = -1; + value = wpa_driver_wext_cipher2wext(params->group_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_CIPHER_GROUP, value) < 0) + ret = -1; + value = wpa_driver_wext_keymgmt2wext(params->key_mgmt_suite); + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_KEY_MGMT, value) < 0) + ret = -1; + value = params->key_mgmt_suite != KEY_MGMT_NONE || + params->pairwise_suite != CIPHER_NONE || + params->group_suite != CIPHER_NONE || + params->wpa_ie_len; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_PRIVACY_INVOKED, value) < 0) + ret = -1; + + /* Allow unencrypted EAPOL messages even if pairwise keys are set when + * not using WPA. IEEE 802.1X specifies that these frames are not + * encrypted, but WPA encrypts them when pairwise keys are in use. */ + if (params->key_mgmt_suite == KEY_MGMT_802_1X || + params->key_mgmt_suite == KEY_MGMT_PSK) + allow_unencrypted_eapol = 0; + else + allow_unencrypted_eapol = 1; + + if (wpa_driver_wext_set_psk(drv, params->psk) < 0) + ret = -1; + if (wpa_driver_wext_set_auth_param(drv, + IW_AUTH_RX_UNENCRYPTED_EAPOL, + allow_unencrypted_eapol) < 0) + ret = -1; +#ifdef CONFIG_IEEE80211W + switch (params->mgmt_frame_protection) { + case NO_MGMT_FRAME_PROTECTION: + value = IW_AUTH_MFP_DISABLED; + break; + case MGMT_FRAME_PROTECTION_OPTIONAL: + value = IW_AUTH_MFP_OPTIONAL; + break; + case MGMT_FRAME_PROTECTION_REQUIRED: + value = IW_AUTH_MFP_REQUIRED; + break; + }; + if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0) + ret = -1; +#endif /* CONFIG_IEEE80211W */ + if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0) + ret = -1; + if (wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0) + ret = -1; + if (params->bssid && + wpa_driver_wext_set_bssid(drv, params->bssid) < 0) + ret = -1; + + return ret; +} + + +static int wpa_driver_wext_set_auth_alg(void *priv, int auth_alg) +{ + struct wpa_driver_wext_data *drv = priv; + int algs = 0, res; + + if (auth_alg & AUTH_ALG_OPEN_SYSTEM) + algs |= IW_AUTH_ALG_OPEN_SYSTEM; + if (auth_alg & AUTH_ALG_SHARED_KEY) + algs |= IW_AUTH_ALG_SHARED_KEY; + if (auth_alg & AUTH_ALG_LEAP) + algs |= IW_AUTH_ALG_LEAP; + if (algs == 0) { + /* at least one algorithm should be set */ + algs = IW_AUTH_ALG_OPEN_SYSTEM; + } + + res = wpa_driver_wext_set_auth_param(drv, IW_AUTH_80211_AUTH_ALG, + algs); + drv->auth_alg_fallback = res == -2; + return res; +} + + +/** + * wpa_driver_wext_set_mode - Set wireless mode (infra/adhoc), SIOCSIWMODE + * @priv: Pointer to private wext data from wpa_driver_wext_init() + * @mode: 0 = infra/BSS (associate with an AP), 1 = adhoc/IBSS + * Returns: 0 on success, -1 on failure + */ +int wpa_driver_wext_set_mode(void *priv, int mode) +{ + struct wpa_driver_wext_data *drv = priv; + struct iwreq iwr; + int ret = -1, flags; + unsigned int new_mode = mode ? IW_MODE_ADHOC : IW_MODE_INFRA; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + iwr.u.mode = new_mode; + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) == 0) { + ret = 0; + goto done; + } + + if (errno != EBUSY) { + perror("ioctl[SIOCSIWMODE]"); + goto done; + } + + /* mac80211 doesn't allow mode changes while the device is up, so if + * the device isn't in the mode we're about to change to, take device + * down, try to set the mode again, and bring it back up. + */ + if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) { + perror("ioctl[SIOCGIWMODE]"); + goto done; + } + + if (iwr.u.mode == new_mode) { + ret = 0; + goto done; + } + + if (wpa_driver_wext_get_ifflags(drv, &flags) == 0) { + (void) wpa_driver_wext_set_ifflags(drv, flags & ~IFF_UP); + + /* Try to set the mode again while the interface is down */ + iwr.u.mode = new_mode; + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) + perror("ioctl[SIOCSIWMODE]"); + else + ret = 0; + + /* Ignore return value of get_ifflags to ensure that the device + * is always up like it was before this function was called. + */ + (void) wpa_driver_wext_get_ifflags(drv, &flags); + (void) wpa_driver_wext_set_ifflags(drv, flags | IFF_UP); + } + +done: + return ret; +} + + +static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv, + u32 cmd, const u8 *bssid, const u8 *pmkid) +{ + struct iwreq iwr; + struct iw_pmksa pmksa; + int ret = 0; + + os_memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ); + os_memset(&pmksa, 0, sizeof(pmksa)); + pmksa.cmd = cmd; + pmksa.bssid.sa_family = ARPHRD_ETHER; + if (bssid) + os_memcpy(pmksa.bssid.sa_data, bssid, ETH_ALEN); + if (pmkid) + os_memcpy(pmksa.pmkid, pmkid, IW_PMKID_LEN); + iwr.u.data.pointer = (caddr_t) &pmksa; + iwr.u.data.length = sizeof(pmksa); + + if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) { + if (errno != EOPNOTSUPP) + perror("ioctl[SIOCSIWPMKSA]"); + ret = -1; + } + + return ret; +} + + +static int wpa_driver_wext_add_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_ADD, bssid, pmkid); +} + + +static int wpa_driver_wext_remove_pmkid(void *priv, const u8 *bssid, + const u8 *pmkid) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_REMOVE, bssid, pmkid); +} + + +static int wpa_driver_wext_flush_pmkid(void *priv) +{ + struct wpa_driver_wext_data *drv = priv; + return wpa_driver_wext_pmksa(drv, IW_PMKSA_FLUSH, NULL, NULL); +} + + +int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + struct wpa_driver_wext_data *drv = priv; + if (!drv->has_capability) + return -1; + os_memcpy(capa, &drv->capa, sizeof(*capa)); + return 0; +} + + +int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, + const char *ifname) +{ + if (ifname == NULL) { + drv->ifindex2 = -1; + return 0; + } + + drv->ifindex2 = if_nametoindex(ifname); + if (drv->ifindex2 <= 0) + return -1; + + wpa_printf(MSG_DEBUG, "Added alternative ifindex %d (%s) for " + "wireless events", drv->ifindex2, ifname); + + return 0; +} + + +int wpa_driver_wext_set_operstate(void *priv, int state) +{ + struct wpa_driver_wext_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)", + __func__, drv->operstate, state, state ? "UP" : "DORMANT"); + drv->operstate = state; + return wpa_driver_wext_send_oper_ifla( + drv, -1, state ? IF_OPER_UP : IF_OPER_DORMANT); +} + + +int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv) +{ + return drv->we_version_compiled; +} + + +const struct wpa_driver_ops wpa_driver_wext_ops = { + .name = "wext", + .desc = "Linux wireless extensions (generic)", + .get_bssid = wpa_driver_wext_get_bssid, + .get_ssid = wpa_driver_wext_get_ssid, + .set_wpa = wpa_driver_wext_set_wpa, + .set_key = wpa_driver_wext_set_key, + .set_countermeasures = wpa_driver_wext_set_countermeasures, + .set_drop_unencrypted = wpa_driver_wext_set_drop_unencrypted, + .scan = wpa_driver_wext_scan, + .get_scan_results2 = wpa_driver_wext_get_scan_results, + .deauthenticate = wpa_driver_wext_deauthenticate, + .disassociate = wpa_driver_wext_disassociate, + .set_mode = wpa_driver_wext_set_mode, + .associate = wpa_driver_wext_associate, + .set_auth_alg = wpa_driver_wext_set_auth_alg, + .init = wpa_driver_wext_init, + .deinit = wpa_driver_wext_deinit, + .add_pmkid = wpa_driver_wext_add_pmkid, + .remove_pmkid = wpa_driver_wext_remove_pmkid, + .flush_pmkid = wpa_driver_wext_flush_pmkid, + .get_capa = wpa_driver_wext_get_capa, + .set_operstate = wpa_driver_wext_set_operstate, +}; diff --git a/contrib/wpa/src/drivers/driver_wext.h b/contrib/wpa/src/drivers/driver_wext.h new file mode 100644 index 0000000..b89c2cb --- /dev/null +++ b/contrib/wpa/src/drivers/driver_wext.h @@ -0,0 +1,82 @@ +/* + * WPA Supplicant - driver_wext exported functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * 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. + */ + +#ifndef DRIVER_WEXT_H +#define DRIVER_WEXT_H + +#include <net/if.h> + +struct wpa_driver_wext_data { + void *ctx; + int event_sock; + int ioctl_sock; + int mlme_sock; + char ifname[IFNAMSIZ + 1]; + int ifindex; + int ifindex2; + int if_removed; + u8 *assoc_req_ies; + size_t assoc_req_ies_len; + u8 *assoc_resp_ies; + size_t assoc_resp_ies_len; + struct wpa_driver_capa capa; + int has_capability; + int we_version_compiled; + + /* for set_auth_alg fallback */ + int use_crypt; + int auth_alg_fallback; + + int operstate; + + char mlmedev[IFNAMSIZ + 1]; + + int scan_complete_events; +}; + +int wpa_driver_wext_get_ifflags(struct wpa_driver_wext_data *drv, int *flags); +int wpa_driver_wext_set_ifflags(struct wpa_driver_wext_data *drv, int flags); +int wpa_driver_wext_get_bssid(void *priv, u8 *bssid); +int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid); +int wpa_driver_wext_get_ssid(void *priv, u8 *ssid); +int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len); +int wpa_driver_wext_set_freq(void *priv, int freq); +int wpa_driver_wext_set_mode(void *priv, int mode); +int wpa_driver_wext_set_key(void *priv, wpa_alg alg, + const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); +int wpa_driver_wext_scan(void *priv, const u8 *ssid, size_t ssid_len); +struct wpa_scan_results * wpa_driver_wext_get_scan_results(void *priv); + +void wpa_driver_wext_scan_timeout(void *eloop_ctx, void *timeout_ctx); + +int wpa_driver_wext_alternative_ifindex(struct wpa_driver_wext_data *drv, + const char *ifname); + +void * wpa_driver_wext_init(void *ctx, const char *ifname); +void wpa_driver_wext_deinit(void *priv); + +int wpa_driver_wext_set_operstate(void *priv, int state); +int wpa_driver_wext_get_version(struct wpa_driver_wext_data *drv); + +int wpa_driver_wext_associate(void *priv, + struct wpa_driver_associate_params *params); +int wpa_driver_wext_get_capa(void *priv, struct wpa_driver_capa *capa); +int wpa_driver_wext_set_auth_param(struct wpa_driver_wext_data *drv, + int idx, u32 value); +int wpa_driver_wext_cipher2wext(int cipher); +int wpa_driver_wext_keymgmt2wext(int keymgmt); + +#endif /* DRIVER_WEXT_H */ diff --git a/contrib/wpa/src/drivers/driver_wired.c b/contrib/wpa/src/drivers/driver_wired.c new file mode 100644 index 0000000..098991a --- /dev/null +++ b/contrib/wpa/src/drivers/driver_wired.c @@ -0,0 +1,286 @@ +/* + * WPA Supplicant - wired Ethernet driver interface + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * + * 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 <net/if.h> +#ifdef __linux__ +#include <netpacket/packet.h> +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) +#include <net/if_dl.h> +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ + +#include "common.h" +#include "driver.h" + + +static const u8 pae_group_addr[ETH_ALEN] = +{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; + + +struct wpa_driver_wired_data { + void *ctx; + int pf_sock; + char ifname[IFNAMSIZ + 1]; + int membership, multi, iff_allmulti, iff_up; +}; + + +static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid) +{ + ssid[0] = 0; + return 0; +} + + +static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) +{ + /* Report PAE group address as the "BSSID" for wired connection. */ + os_memcpy(bssid, pae_group_addr, ETH_ALEN); + return 0; +} + + +static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCGIFFLAGS]"); + close(s); + return -1; + } + close(s); + *flags = ifr.ifr_flags & 0xffff; + return 0; +} + + +static int wpa_driver_wired_set_ifflags(const char *ifname, int flags) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_flags = flags & 0xffff; + if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOCSIFFLAGS]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) +{ + struct ifreq ifr; + int s; + + s = socket(PF_INET, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); +#ifdef __linux__ + ifr.ifr_hwaddr.sa_family = AF_UNSPEC; + os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); +#endif /* __linux__ */ +#if defined(__FreeBSD__) || defined(__DragonFly__) + { + struct sockaddr_dl *dlp; + dlp = (struct sockaddr_dl *) &ifr.ifr_addr; + dlp->sdl_len = sizeof(struct sockaddr_dl); + dlp->sdl_family = AF_LINK; + dlp->sdl_index = 0; + dlp->sdl_nlen = 0; + dlp->sdl_alen = ETH_ALEN; + dlp->sdl_slen = 0; + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + } +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ +#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) + { + struct sockaddr *sap; + sap = (struct sockaddr *) &ifr.ifr_addr; + sap->sa_len = sizeof(struct sockaddr); + sap->sa_family = AF_UNSPEC; + os_memcpy(sap->sa_data, addr, ETH_ALEN); + } +#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */ + + if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) { + perror("ioctl[SIOC{ADD/DEL}MULTI]"); + close(s); + return -1; + } + close(s); + return 0; +} + + +static int wpa_driver_wired_membership(struct wpa_driver_wired_data *drv, + const u8 *addr, int add) +{ +#ifdef __linux__ + struct packet_mreq mreq; + + if (drv->pf_sock == -1) + return -1; + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = if_nametoindex(drv->ifname); + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + os_memcpy(mreq.mr_address, addr, ETH_ALEN); + + if (setsockopt(drv->pf_sock, SOL_PACKET, + add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt"); + return -1; + } + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +static void * wpa_driver_wired_init(void *ctx, const char *ifname) +{ + struct wpa_driver_wired_data *drv; + int flags; + + drv = os_zalloc(sizeof(*drv)); + if (drv == NULL) + return NULL; + os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname)); + drv->ctx = ctx; + +#ifdef __linux__ + drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0); + if (drv->pf_sock < 0) + perror("socket(PF_PACKET)"); +#else /* __linux__ */ + drv->pf_sock = -1; +#endif /* __linux__ */ + + if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && + !(flags & IFF_UP) && + wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { + drv->iff_up = 1; + } + + if (wpa_driver_wired_membership(drv, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "packet socket", __func__); + drv->membership = 1; + } else if (wpa_driver_wired_multi(ifname, pae_group_addr, 1) == 0) { + wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " + "SIOCADDMULTI", __func__); + drv->multi = 1; + } else if (wpa_driver_wired_get_ifflags(ifname, &flags) < 0) { + wpa_printf(MSG_INFO, "%s: Could not get interface " + "flags", __func__); + os_free(drv); + return NULL; + } else if (flags & IFF_ALLMULTI) { + wpa_printf(MSG_DEBUG, "%s: Interface is already configured " + "for multicast", __func__); + } else if (wpa_driver_wired_set_ifflags(ifname, + flags | IFF_ALLMULTI) < 0) { + wpa_printf(MSG_INFO, "%s: Failed to enable allmulti", + __func__); + os_free(drv); + return NULL; + } else { + wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", + __func__); + drv->iff_allmulti = 1; + } + + return drv; +} + + +static void wpa_driver_wired_deinit(void *priv) +{ + struct wpa_driver_wired_data *drv = priv; + int flags; + + if (drv->membership && + wpa_driver_wired_membership(drv, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (PACKET)", __func__); + } + + if (drv->multi && + wpa_driver_wired_multi(drv->ifname, pae_group_addr, 0) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " + "group (SIOCDELMULTI)", __func__); + } + + if (drv->iff_allmulti && + (wpa_driver_wired_get_ifflags(drv->ifname, &flags) < 0 || + wpa_driver_wired_set_ifflags(drv->ifname, + flags & ~IFF_ALLMULTI) < 0)) { + wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode", + __func__); + } + + if (drv->iff_up && + wpa_driver_wired_get_ifflags(drv->ifname, &flags) == 0 && + (flags & IFF_UP) && + wpa_driver_wired_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down", + __func__); + } + + if (drv->pf_sock != -1) + close(drv->pf_sock); + + os_free(drv); +} + + +const struct wpa_driver_ops wpa_driver_wired_ops = { + .name = "wired", + .desc = "wpa_supplicant wired Ethernet driver", + .get_ssid = wpa_driver_wired_get_ssid, + .get_bssid = wpa_driver_wired_get_bssid, + .init = wpa_driver_wired_init, + .deinit = wpa_driver_wired_deinit, +}; diff --git a/contrib/wpa/src/drivers/ndis_events.c b/contrib/wpa/src/drivers/ndis_events.c new file mode 100644 index 0000000..f6eaa7c --- /dev/null +++ b/contrib/wpa/src/drivers/ndis_events.c @@ -0,0 +1,808 @@ +/* + * ndis_events - Receive NdisMIndicateStatus() events using WMI + * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * + * 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. + */ + +#define _WIN32_WINNT 0x0400 + +#include "includes.h" + +#ifndef COBJMACROS +#define COBJMACROS +#endif /* COBJMACROS */ +#include <wbemidl.h> + +#include "common.h" + + +static int wmi_refcnt = 0; +static int wmi_first = 1; + +struct ndis_events_data { + IWbemObjectSink sink; + IWbemObjectSinkVtbl sink_vtbl; + + IWbemServices *pSvc; + IWbemLocator *pLoc; + + HANDLE read_pipe, write_pipe, event_avail; + UINT ref; + int terminating; + char *ifname; /* {GUID..} */ + WCHAR *adapter_desc; +}; + +#define BstrAlloc(x) (x) ? SysAllocString(x) : NULL +#define BstrFree(x) if (x) SysFreeString(x) + +/* WBEM / WMI wrapper functions, to perform in-place conversion of WCHARs to + * BSTRs */ +HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecQuery( + IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, + long lFlags, IWbemContext *pCtx, IEnumWbemClassObject **ppEnum) +{ + BSTR bsQueryLanguage, bsQuery; + HRESULT hr; + + bsQueryLanguage = BstrAlloc(strQueryLanguage); + bsQuery = BstrAlloc(strQuery); + + hr = IWbemServices_ExecQuery(pSvc, bsQueryLanguage, bsQuery, lFlags, + pCtx, ppEnum); + + BstrFree(bsQueryLanguage); + BstrFree(bsQuery); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE call_IWbemServices_ExecNotificationQueryAsync( + IWbemServices *pSvc, LPCWSTR strQueryLanguage, LPCWSTR strQuery, + long lFlags, IWbemContext *pCtx, IWbemObjectSink *pResponseHandler) +{ + BSTR bsQueryLanguage, bsQuery; + HRESULT hr; + + bsQueryLanguage = BstrAlloc(strQueryLanguage); + bsQuery = BstrAlloc(strQuery); + + hr = IWbemServices_ExecNotificationQueryAsync(pSvc, bsQueryLanguage, + bsQuery, lFlags, pCtx, + pResponseHandler); + + BstrFree(bsQueryLanguage); + BstrFree(bsQuery); + + return hr; +} + + +HRESULT STDMETHODCALLTYPE call_IWbemLocator_ConnectServer( + IWbemLocator *pLoc, LPCWSTR strNetworkResource, LPCWSTR strUser, + LPCWSTR strPassword, LPCWSTR strLocale, long lSecurityFlags, + LPCWSTR strAuthority, IWbemContext *pCtx, IWbemServices **ppNamespace) +{ + BSTR bsNetworkResource, bsUser, bsPassword, bsLocale, bsAuthority; + HRESULT hr; + + bsNetworkResource = BstrAlloc(strNetworkResource); + bsUser = BstrAlloc(strUser); + bsPassword = BstrAlloc(strPassword); + bsLocale = BstrAlloc(strLocale); + bsAuthority = BstrAlloc(strAuthority); + + hr = IWbemLocator_ConnectServer(pLoc, bsNetworkResource, bsUser, + bsPassword, bsLocale, lSecurityFlags, + bsAuthority, pCtx, ppNamespace); + + BstrFree(bsNetworkResource); + BstrFree(bsUser); + BstrFree(bsPassword); + BstrFree(bsLocale); + BstrFree(bsAuthority); + + return hr; +} + + +enum event_types { EVENT_CONNECT, EVENT_DISCONNECT, EVENT_MEDIA_SPECIFIC, + EVENT_ADAPTER_ARRIVAL, EVENT_ADAPTER_REMOVAL }; + +static int ndis_events_get_adapter(struct ndis_events_data *events, + const char *ifname, const char *desc); + + +static int ndis_events_constructor(struct ndis_events_data *events) +{ + events->ref = 1; + + if (!CreatePipe(&events->read_pipe, &events->write_pipe, NULL, 512)) { + wpa_printf(MSG_ERROR, "CreatePipe() failed: %d", + (int) GetLastError()); + return -1; + } + events->event_avail = CreateEvent(NULL, TRUE, FALSE, NULL); + if (events->event_avail == NULL) { + wpa_printf(MSG_ERROR, "CreateEvent() failed: %d", + (int) GetLastError()); + CloseHandle(events->read_pipe); + CloseHandle(events->write_pipe); + return -1; + } + + return 0; +} + + +static void ndis_events_destructor(struct ndis_events_data *events) +{ + CloseHandle(events->read_pipe); + CloseHandle(events->write_pipe); + CloseHandle(events->event_avail); + IWbemServices_Release(events->pSvc); + IWbemLocator_Release(events->pLoc); + if (--wmi_refcnt == 0) + CoUninitialize(); +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_query_interface(IWbemObjectSink *this, REFIID riid, void **obj) +{ + *obj = NULL; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IWbemObjectSink)) { + *obj = this; + IWbemObjectSink_AddRef(this); + return NOERROR; + } + + return E_NOINTERFACE; +} + + +static ULONG STDMETHODCALLTYPE ndis_events_add_ref(IWbemObjectSink *this) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + return ++events->ref; +} + + +static ULONG STDMETHODCALLTYPE ndis_events_release(IWbemObjectSink *this) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + + if (--events->ref != 0) + return events->ref; + + ndis_events_destructor(events); + wpa_printf(MSG_DEBUG, "ndis_events: terminated"); + os_free(events->adapter_desc); + os_free(events->ifname); + os_free(events); + return 0; +} + + +static int ndis_events_send_event(struct ndis_events_data *events, + enum event_types type, + char *data, size_t data_len) +{ + char buf[512], *pos, *end; + int _type; + DWORD written; + + end = buf + sizeof(buf); + _type = (int) type; + os_memcpy(buf, &_type, sizeof(_type)); + pos = buf + sizeof(_type); + + if (data) { + if (2 + data_len > (size_t) (end - pos)) { + wpa_printf(MSG_DEBUG, "Not enough room for send_event " + "data (%d)", data_len); + return -1; + } + *pos++ = data_len >> 8; + *pos++ = data_len & 0xff; + os_memcpy(pos, data, data_len); + pos += data_len; + } + + if (WriteFile(events->write_pipe, buf, pos - buf, &written, NULL)) { + SetEvent(events->event_avail); + return 0; + } + wpa_printf(MSG_INFO, "WriteFile() failed: %d", (int) GetLastError()); + return -1; +} + + +static void ndis_events_media_connect(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaConnect"); + ndis_events_send_event(events, EVENT_CONNECT, NULL, 0); +} + + +static void ndis_events_media_disconnect(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaDisconnect"); + ndis_events_send_event(events, EVENT_DISCONNECT, NULL, 0); +} + + +static void ndis_events_media_specific(struct ndis_events_data *events, + IWbemClassObject *pObj) +{ + VARIANT vt; + HRESULT hr; + LONG lower, upper, k; + UCHAR ch; + char *data, *pos; + size_t data_len; + + wpa_printf(MSG_DEBUG, "MSNdis_StatusMediaSpecificIndication"); + + /* This is the StatusBuffer from NdisMIndicateStatus() call */ + hr = IWbemClassObject_Get(pObj, L"NdisStatusMediaSpecificIndication", + 0, &vt, NULL, NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Could not get " + "NdisStatusMediaSpecificIndication from " + "the object?!"); + return; + } + + SafeArrayGetLBound(V_ARRAY(&vt), 1, &lower); + SafeArrayGetUBound(V_ARRAY(&vt), 1, &upper); + data_len = upper - lower + 1; + data = os_malloc(data_len); + if (data == NULL) { + wpa_printf(MSG_DEBUG, "Failed to allocate buffer for event " + "data"); + VariantClear(&vt); + return; + } + + pos = data; + for (k = lower; k <= upper; k++) { + SafeArrayGetElement(V_ARRAY(&vt), &k, &ch); + *pos++ = ch; + } + wpa_hexdump(MSG_DEBUG, "MediaSpecificEvent", (u8 *) data, data_len); + + VariantClear(&vt); + + ndis_events_send_event(events, EVENT_MEDIA_SPECIFIC, data, data_len); + + os_free(data); +} + + +static void ndis_events_adapter_arrival(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterArrival"); + ndis_events_send_event(events, EVENT_ADAPTER_ARRIVAL, NULL, 0); +} + + +static void ndis_events_adapter_removal(struct ndis_events_data *events) +{ + wpa_printf(MSG_DEBUG, "MSNdis_NotifyAdapterRemoval"); + ndis_events_send_event(events, EVENT_ADAPTER_REMOVAL, NULL, 0); +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_indicate(IWbemObjectSink *this, long lObjectCount, + IWbemClassObject __RPC_FAR *__RPC_FAR *ppObjArray) +{ + struct ndis_events_data *events = (struct ndis_events_data *) this; + long i; + + if (events->terminating) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " + "indication - terminating"); + return WBEM_NO_ERROR; + } + /* wpa_printf(MSG_DEBUG, "Notification received - %d object(s)", + lObjectCount); */ + + for (i = 0; i < lObjectCount; i++) { + IWbemClassObject *pObj = ppObjArray[i]; + HRESULT hr; + VARIANT vtClass, vt; + + hr = IWbemClassObject_Get(pObj, L"__CLASS", 0, &vtClass, NULL, + NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Failed to get __CLASS from " + "event."); + break; + } + /* wpa_printf(MSG_DEBUG, "CLASS: '%S'", vtClass.bstrVal); */ + + hr = IWbemClassObject_Get(pObj, L"InstanceName", 0, &vt, NULL, + NULL); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "Failed to get InstanceName " + "from event."); + VariantClear(&vtClass); + break; + } + + if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterArrival") == 0) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Try to " + "update adapter description since it may " + "have changed with new adapter instance"); + ndis_events_get_adapter(events, events->ifname, NULL); + } + + if (wcscmp(events->adapter_desc, vt.bstrVal) != 0) { + wpa_printf(MSG_DEBUG, "ndis_events_indicate: Ignore " + "indication for foreign adapter: " + "InstanceName: '%S' __CLASS: '%S'", + vt.bstrVal, vtClass.bstrVal); + VariantClear(&vtClass); + VariantClear(&vt); + continue; + } + VariantClear(&vt); + + if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaSpecificIndication") == 0) { + ndis_events_media_specific(events, pObj); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaConnect") == 0) { + ndis_events_media_connect(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_StatusMediaDisconnect") == 0) { + ndis_events_media_disconnect(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterArrival") == 0) { + ndis_events_adapter_arrival(events); + } else if (wcscmp(vtClass.bstrVal, + L"MSNdis_NotifyAdapterRemoval") == 0) { + ndis_events_adapter_removal(events); + } else { + wpa_printf(MSG_DEBUG, "Unepected event - __CLASS: " + "'%S'", vtClass.bstrVal); + } + + VariantClear(&vtClass); + } + + return WBEM_NO_ERROR; +} + + +static HRESULT STDMETHODCALLTYPE +ndis_events_set_status(IWbemObjectSink *this, long lFlags, HRESULT hResult, + BSTR strParam, IWbemClassObject __RPC_FAR *pObjParam) +{ + return WBEM_NO_ERROR; +} + + +static int notification_query(IWbemObjectSink *pDestSink, + IWbemServices *pSvc, const char *class_name) +{ + HRESULT hr; + WCHAR query[256]; + + _snwprintf(query, 256, + L"SELECT * FROM %S", class_name); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + hr = call_IWbemServices_ExecNotificationQueryAsync( + pSvc, L"WQL", query, 0, 0, pDestSink); + if (FAILED(hr)) { + wpa_printf(MSG_DEBUG, "ExecNotificationQueryAsync for %s " + "failed with hresult of 0x%x", + class_name, (int) hr); + return -1; + } + + return 0; +} + + +static int register_async_notification(IWbemObjectSink *pDestSink, + IWbemServices *pSvc) +{ + int i; + const char *class_list[] = { + "MSNdis_StatusMediaConnect", + "MSNdis_StatusMediaDisconnect", + "MSNdis_StatusMediaSpecificIndication", + "MSNdis_NotifyAdapterArrival", + "MSNdis_NotifyAdapterRemoval", + NULL + }; + + for (i = 0; class_list[i]; i++) { + if (notification_query(pDestSink, pSvc, class_list[i]) < 0) + return -1; + } + + return 0; +} + + +void ndis_events_deinit(struct ndis_events_data *events) +{ + events->terminating = 1; + IWbemServices_CancelAsyncCall(events->pSvc, &events->sink); + IWbemObjectSink_Release(&events->sink); + /* + * Rest of deinitialization is done in ndis_events_destructor() once + * all reference count drops to zero. + */ +} + + +static int ndis_events_use_desc(struct ndis_events_data *events, + const char *desc) +{ + char *tmp, *pos; + size_t len; + + if (desc == NULL) { + if (events->adapter_desc == NULL) + return -1; + /* Continue using old description */ + return 0; + } + + tmp = os_strdup(desc); + if (tmp == NULL) + return -1; + + pos = os_strstr(tmp, " (Microsoft's Packet Scheduler)"); + if (pos) + *pos = '\0'; + + len = os_strlen(tmp); + events->adapter_desc = os_malloc((len + 1) * sizeof(WCHAR)); + if (events->adapter_desc == NULL) { + os_free(tmp); + return -1; + } + _snwprintf(events->adapter_desc, len + 1, L"%S", tmp); + os_free(tmp); + return 0; +} + + +static int ndis_events_get_adapter(struct ndis_events_data *events, + const char *ifname, const char *desc) +{ + HRESULT hr; + IWbemServices *pSvc; +#define MAX_QUERY_LEN 256 + WCHAR query[MAX_QUERY_LEN]; + IEnumWbemClassObject *pEnumerator; + IWbemClassObject *pObj; + ULONG uReturned; + VARIANT vt; + int len, pos; + + /* + * Try to get adapter descriptor through WMI CIMv2 Win32_NetworkAdapter + * to have better probability of matching with InstanceName from + * MSNdis events. If this fails, use the provided description. + */ + + os_free(events->adapter_desc); + events->adapter_desc = NULL; + + hr = call_IWbemLocator_ConnectServer( + events->pLoc, L"ROOT\\CIMV2", NULL, NULL, 0, 0, 0, 0, &pSvc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "ndis_events: Could not connect to WMI " + "server (ROOT\\CIMV2) - error 0x%x", (int) hr); + return ndis_events_use_desc(events, desc); + } + wpa_printf(MSG_DEBUG, "ndis_events: Connected to ROOT\\CIMV2."); + + _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Index FROM Win32_NetworkAdapterConfiguration " + L"WHERE SettingID='%S'", ifname); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "GUID from Win32_NetworkAdapterConfiguration: " + "0x%x", (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "GUID from Win32_NetworkAdapterConfiguration: " + "0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + IEnumWbemClassObject_Release(pEnumerator); + + VariantInit(&vt); + hr = IWbemClassObject_Get(pObj, L"Index", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Index from " + "Win32_NetworkAdapterConfiguration: 0x%x", + (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Name,PNPDeviceID FROM Win32_NetworkAdapter WHERE " + L"Index=%d", + vt.uintVal); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + VariantClear(&vt); + IWbemClassObject_Release(pObj); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + IEnumWbemClassObject_Release(pEnumerator); + + hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " + "Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + return ndis_events_use_desc(events, desc); + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::Name='%S'", + vt.bstrVal); + events->adapter_desc = _wcsdup(vt.bstrVal); + VariantClear(&vt); + + /* + * Try to get even better candidate for matching with InstanceName + * from Win32_PnPEntity. This is needed at least for some USB cards + * that can change the InstanceName whenever being unplugged and + * plugged again. + */ + + hr = IWbemClassObject_Get(pObj, L"PNPDeviceID", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get PNPDeviceID " + "from Win32_NetworkAdapter: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_NetworkAdapter::PNPDeviceID=" + "'%S'", vt.bstrVal); + + len = _snwprintf(query, MAX_QUERY_LEN, + L"SELECT Name FROM Win32_PnPEntity WHERE DeviceID='"); + if (len < 0 || len >= MAX_QUERY_LEN - 1) { + VariantClear(&vt); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + /* Escape \ as \\ */ + for (pos = 0; vt.bstrVal[pos] && len < MAX_QUERY_LEN - 2; pos++) { + if (vt.bstrVal[pos] == '\\') { + if (len >= MAX_QUERY_LEN - 3) + break; + query[len++] = '\\'; + } + query[len++] = vt.bstrVal[pos]; + } + query[len++] = L'\''; + query[len] = L'\0'; + VariantClear(&vt); + IWbemClassObject_Release(pObj); + wpa_printf(MSG_DEBUG, "ndis_events: WMI: %S", query); + + hr = call_IWbemServices_ExecQuery( + pSvc, L"WQL", query, + WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, + NULL, &pEnumerator); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to query interface " + "Name from Win32_PnPEntity: 0x%x", (int) hr); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + uReturned = 0; + hr = IEnumWbemClassObject_Next(pEnumerator, WBEM_INFINITE, 1, + &pObj, &uReturned); + if (!SUCCEEDED(hr) || uReturned == 0) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to find interface " + "from Win32_PnPEntity: 0x%x", (int) hr); + IEnumWbemClassObject_Release(pEnumerator); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + IEnumWbemClassObject_Release(pEnumerator); + + hr = IWbemClassObject_Get(pObj, L"Name", 0, &vt, NULL, NULL); + if (!SUCCEEDED(hr)) { + wpa_printf(MSG_DEBUG, "ndis_events: Failed to get Name from " + "Win32_PnPEntity: 0x%x", (int) hr); + IWbemClassObject_Release(pObj); + IWbemServices_Release(pSvc); + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + return 0; /* use Win32_NetworkAdapter::Name */ + } + + wpa_printf(MSG_DEBUG, "ndis_events: Win32_PnPEntity::Name='%S'", + vt.bstrVal); + os_free(events->adapter_desc); + events->adapter_desc = _wcsdup(vt.bstrVal); + VariantClear(&vt); + + IWbemClassObject_Release(pObj); + + IWbemServices_Release(pSvc); + + if (events->adapter_desc == NULL) + return ndis_events_use_desc(events, desc); + + return 0; +} + + +struct ndis_events_data * +ndis_events_init(HANDLE *read_pipe, HANDLE *event_avail, + const char *ifname, const char *desc) +{ + HRESULT hr; + IWbemObjectSink *pSink; + struct ndis_events_data *events; + + events = os_zalloc(sizeof(*events)); + if (events == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate sink for events."); + return NULL; + } + events->ifname = os_strdup(ifname); + if (events->ifname == NULL) { + os_free(events); + return NULL; + } + + if (wmi_refcnt++ == 0) { + hr = CoInitializeEx(0, COINIT_MULTITHREADED); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoInitializeEx() failed - " + "returned 0x%x", (int) hr); + os_free(events); + return NULL; + } + } + + if (wmi_first) { + /* CoInitializeSecurity() must be called once and only once + * per process, so let's use wmi_first flag to protect against + * multiple calls. */ + wmi_first = 0; + + hr = CoInitializeSecurity(NULL, -1, NULL, NULL, + RPC_C_AUTHN_LEVEL_PKT_PRIVACY, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, EOAC_SECURE_REFS, NULL); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoInitializeSecurity() failed " + "- returned 0x%x", (int) hr); + os_free(events); + return NULL; + } + } + + hr = CoCreateInstance(&CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, + &IID_IWbemLocator, + (LPVOID *) (void *) &events->pLoc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "CoCreateInstance() failed - returned " + "0x%x", (int) hr); + CoUninitialize(); + os_free(events); + return NULL; + } + + if (ndis_events_get_adapter(events, ifname, desc) < 0) { + CoUninitialize(); + os_free(events); + return NULL; + } + wpa_printf(MSG_DEBUG, "ndis_events: use adapter descriptor '%S'", + events->adapter_desc); + + hr = call_IWbemLocator_ConnectServer( + events->pLoc, L"ROOT\\WMI", NULL, NULL, + 0, 0, 0, 0, &events->pSvc); + if (FAILED(hr)) { + wpa_printf(MSG_ERROR, "Could not connect to server - error " + "0x%x", (int) hr); + CoUninitialize(); + os_free(events->adapter_desc); + os_free(events); + return NULL; + } + wpa_printf(MSG_DEBUG, "Connected to ROOT\\WMI."); + + ndis_events_constructor(events); + pSink = &events->sink; + pSink->lpVtbl = &events->sink_vtbl; + events->sink_vtbl.QueryInterface = ndis_events_query_interface; + events->sink_vtbl.AddRef = ndis_events_add_ref; + events->sink_vtbl.Release = ndis_events_release; + events->sink_vtbl.Indicate = ndis_events_indicate; + events->sink_vtbl.SetStatus = ndis_events_set_status; + + if (register_async_notification(pSink, events->pSvc) < 0) { + wpa_printf(MSG_DEBUG, "Failed to register async " + "notifications"); + ndis_events_destructor(events); + os_free(events->adapter_desc); + os_free(events); + return NULL; + } + + *read_pipe = events->read_pipe; + *event_avail = events->event_avail; + + return events; +} diff --git a/contrib/wpa/src/drivers/priv_netlink.h b/contrib/wpa/src/drivers/priv_netlink.h new file mode 100644 index 0000000..2a31e25 --- /dev/null +++ b/contrib/wpa/src/drivers/priv_netlink.h @@ -0,0 +1,104 @@ +/* + * wpa_supplicant - Private copy of Linux netlink/rtnetlink definitions. + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * 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. + */ + +#ifndef PRIV_NETLINK_H +#define PRIV_NETLINK_H + +/* + * This should be replaced with user space header once one is available with C + * library, etc.. + */ + +#ifndef IFF_LOWER_UP +#define IFF_LOWER_UP 0x10000 /* driver signals L1 up */ +#endif +#ifndef IFF_DORMANT +#define IFF_DORMANT 0x20000 /* driver signals dormant */ +#endif + +#ifndef IFLA_IFNAME +#define IFLA_IFNAME 3 +#endif +#ifndef IFLA_WIRELESS +#define IFLA_WIRELESS 11 +#endif +#ifndef IFLA_OPERSTATE +#define IFLA_OPERSTATE 16 +#endif +#ifndef IFLA_LINKMODE +#define IFLA_LINKMODE 17 +#define IF_OPER_DORMANT 5 +#define IF_OPER_UP 6 +#endif + +#define NLM_F_REQUEST 1 + +#define NETLINK_ROUTE 0 +#define RTMGRP_LINK 1 +#define RTM_BASE 0x10 +#define RTM_NEWLINK (RTM_BASE + 0) +#define RTM_DELLINK (RTM_BASE + 1) +#define RTM_SETLINK (RTM_BASE + 3) + +#define NLMSG_ALIGNTO 4 +#define NLMSG_ALIGN(len) (((len) + NLMSG_ALIGNTO - 1) & ~(NLMSG_ALIGNTO - 1)) +#define NLMSG_LENGTH(len) ((len) + NLMSG_ALIGN(sizeof(struct nlmsghdr))) +#define NLMSG_DATA(nlh) ((void*) (((char*) nlh) + NLMSG_LENGTH(0))) + +#define RTA_ALIGNTO 4 +#define RTA_ALIGN(len) (((len) + RTA_ALIGNTO - 1) & ~(RTA_ALIGNTO - 1)) +#define RTA_OK(rta,len) \ +((len) > 0 && (rta)->rta_len >= sizeof(struct rtattr) && \ +(rta)->rta_len <= (len)) +#define RTA_NEXT(rta,attrlen) \ +((attrlen) -= RTA_ALIGN((rta)->rta_len), \ +(struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) +#define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len)) +#define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0))) + + +struct sockaddr_nl +{ + sa_family_t nl_family; + unsigned short nl_pad; + u32 nl_pid; + u32 nl_groups; +}; + +struct nlmsghdr +{ + u32 nlmsg_len; + u16 nlmsg_type; + u16 nlmsg_flags; + u32 nlmsg_seq; + u32 nlmsg_pid; +}; + +struct ifinfomsg +{ + unsigned char ifi_family; + unsigned char __ifi_pad; + unsigned short ifi_type; + int ifi_index; + unsigned ifi_flags; + unsigned ifi_change; +}; + +struct rtattr +{ + unsigned short rta_len; + unsigned short rta_type; +}; + +#endif /* PRIV_NETLINK_H */ diff --git a/contrib/wpa/src/drivers/radiotap.c b/contrib/wpa/src/drivers/radiotap.c new file mode 100644 index 0000000..804473f --- /dev/null +++ b/contrib/wpa/src/drivers/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/contrib/wpa/src/drivers/radiotap.h b/contrib/wpa/src/drivers/radiotap.h new file mode 100644 index 0000000..508264c --- /dev/null +++ b/contrib/wpa/src/drivers/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/contrib/wpa/src/drivers/radiotap_iter.h b/contrib/wpa/src/drivers/radiotap_iter.h new file mode 100644 index 0000000..92a798a --- /dev/null +++ b/contrib/wpa/src/drivers/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 */ |