/* * Copyright (C) 2003-2005 Pontus Fuchs, Giridhar Pemmasani * * This program 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include "iw_ndis.h" #include "wrapndis.h" static int freq_chan[] = { 2412, 2417, 2422, 2427, 2432, 2437, 2442, 2447, 2452, 2457, 2462, 2467, 2472, 2484 }; static const char *network_names[] = {"IEEE 802.11FH", "IEEE 802.11b", "IEEE 802.11a", "IEEE 802.11g", "Auto"}; int set_essid(struct ndis_device *wnd, const char *ssid, int ssid_len) { NDIS_STATUS res; struct ndis_essid req; if (ssid_len > NDIS_ESSID_MAX_SIZE) return -EINVAL; memset(&req, 0, sizeof(req)); req.length = ssid_len; if (ssid_len) memcpy(&req.essid, ssid, ssid_len); res = mp_set(wnd, OID_802_11_SSID, &req, sizeof(req)); if (res) { WARNING("setting essid failed (%08X)", res); EXIT2(return -EINVAL); } memcpy(&wnd->essid, &req, sizeof(req)); EXIT2(return 0); } static int set_assoc_params(struct ndis_device *wnd) { TRACE2("wpa_version=0x%x auth_alg=0x%x key_mgmt=0x%x " "cipher_pairwise=0x%x cipher_group=0x%x", wnd->iw_auth_wpa_version, wnd->iw_auth_80211_alg, wnd->iw_auth_key_mgmt, wnd->iw_auth_cipher_pairwise, wnd->iw_auth_cipher_group); set_auth_mode(wnd); set_priv_filter(wnd); set_encr_mode(wnd); return 0; } static int iw_set_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); char ssid[NDIS_ESSID_MAX_SIZE]; int length; ENTER2(""); memset(ssid, 0, sizeof(ssid)); /* there is no way to turn off essid other than to set to * random bytes; instead, we use off to mean any */ if (wrqu->essid.flags) { /* wireless-tools prior to version 20 add extra 1, and * later than 20 don't! Deal with that mess */ length = wrqu->essid.length - 1; if (length > 0) length--; while (length < wrqu->essid.length && extra[length]) length++; TRACE2("%d", length); if (length <= 0 || length > NDIS_ESSID_MAX_SIZE) EXIT2(return -EINVAL); } else length = 0; set_assoc_params(wnd); memcpy(ssid, extra, length); if (set_essid(wnd, ssid, length)) EXIT2(return -EINVAL); EXIT2(return 0); } static int iw_get_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; struct ndis_essid req; ENTER2(""); memset(&req, 0, sizeof(req)); res = mp_query(wnd, OID_802_11_SSID, &req, sizeof(req)); if (res) { WARNING("getting essid failed (%08X)", res); EXIT2(return -EOPNOTSUPP); } memcpy(extra, req.essid, req.length); if (req.length > 0) wrqu->essid.flags = 1; else wrqu->essid.flags = 0; wrqu->essid.length = req.length; EXIT2(return 0); } int set_infra_mode(struct ndis_device *wnd, enum ndis_infrastructure_mode mode) { NDIS_STATUS res; unsigned int i; ENTER2("%d", mode); res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &wnd->infrastructure_mode); if (res != NDIS_STATUS_SUCCESS) { WARNING("getting operating mode to failed (%08X)", res); EXIT2(return -EINVAL); } if (wnd->infrastructure_mode == mode) EXIT2(return 0); res = mp_set_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, mode); if (res) { WARNING("setting operating mode to %d failed (%08X)", mode, res); EXIT2(return -EINVAL); } /* NDIS drivers clear keys when infrastructure mode is * changed. But Linux tools assume otherwise. So set the * keys */ if (wnd->iw_auth_key_mgmt == 0 || wnd->iw_auth_key_mgmt == IW_AUTH_KEY_MGMT_802_1X) { for (i = 0; i < MAX_ENCR_KEYS; i++) { if (wnd->encr_info.keys[i].length > 0) add_wep_key(wnd, wnd->encr_info.keys[i].key, wnd->encr_info.keys[i].length, i); } } wnd->infrastructure_mode = mode; EXIT2(return 0); } static int iw_set_infra_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); enum ndis_infrastructure_mode ndis_mode; ENTER2("%d", wrqu->mode); switch (wrqu->mode) { case IW_MODE_ADHOC: ndis_mode = Ndis802_11IBSS; break; case IW_MODE_INFRA: ndis_mode = Ndis802_11Infrastructure; break; case IW_MODE_AUTO: ndis_mode = Ndis802_11AutoUnknown; break; default: EXIT2(return -EINVAL); } if (set_infra_mode(wnd, ndis_mode)) EXIT2(return -EINVAL); EXIT2(return 0); } static int iw_get_infra_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); int ndis_mode, iw_mode; NDIS_STATUS res; ENTER2(""); res = mp_query_int(wnd, OID_802_11_INFRASTRUCTURE_MODE, &ndis_mode); if (res) { WARNING("getting operating mode failed (%08X)", res); EXIT2(return -EOPNOTSUPP); } switch(ndis_mode) { case Ndis802_11IBSS: iw_mode = IW_MODE_ADHOC; break; case Ndis802_11Infrastructure: iw_mode = IW_MODE_INFRA; break; case Ndis802_11AutoUnknown: iw_mode = IW_MODE_AUTO; break; default: ERROR("invalid operating mode (%u)", ndis_mode); EXIT2(return -EINVAL); } wrqu->mode = iw_mode; EXIT2(return 0); } static const char *network_type_to_name(int net_type) { if (net_type >= 0 && net_type < (sizeof(network_names)/sizeof(network_names[0]))) return network_names[net_type]; else return network_names[sizeof(network_names) / sizeof(network_names[0]) - 1]; } static int iw_get_network_type(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); unsigned int network_type; NDIS_STATUS res; ENTER2(""); res = mp_query_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, &network_type); if (res) { WARNING("getting network type failed: %08X", res); network_type = -1; } strncpy(wrqu->name, network_type_to_name(network_type), sizeof(wrqu->name) - 1); wrqu->name[sizeof(wrqu->name)-1] = 0; return 0; } static int iw_get_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; struct ndis_configuration req; ENTER2(""); memset(&req, 0, sizeof(req)); res = mp_query(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); if (res) { WARNING("getting configuration failed (%08X)", res); EXIT2(return -EOPNOTSUPP); } memset(&(wrqu->freq), 0, sizeof(struct iw_freq)); /* see comment in wireless.h above the "struct iw_freq" definition for an explanation of this if NOTE: 1000000 is due to the kHz */ if (req.ds_config > 1000000) { wrqu->freq.m = req.ds_config / 10; wrqu->freq.e = 1; } else wrqu->freq.m = req.ds_config; /* convert from kHz to Hz */ wrqu->freq.e += 3; return 0; } static int iw_set_freq(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; struct ndis_configuration req; ENTER2(""); /* this OID is valid only when not associated */ if (netif_carrier_ok(wnd->net_dev)) EXIT2(return 0); memset(&req, 0, sizeof(req)); res = mp_query(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); if (res) { WARNING("getting configuration failed (%08X)", res); EXIT2(return 0); } if (wrqu->freq.m < 1000 && wrqu->freq.e == 0) { if (wrqu->freq.m >= 1 && wrqu->freq.m <= (sizeof(freq_chan) / sizeof(freq_chan[0]))) req.ds_config = freq_chan[wrqu->freq.m - 1] * 1000; else return -EINVAL; } else { int i; req.ds_config = wrqu->freq.m; for (i = wrqu->freq.e; i > 0; i--) req.ds_config *= 10; req.ds_config /= 1000; } res = mp_set(wnd, OID_802_11_CONFIGURATION, &req, sizeof(req)); if (res) WARNING("setting configuration failed (%08X)", res); return 0; } static int iw_get_tx_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); ndis_tx_power_level ndis_power; NDIS_STATUS res; ENTER2(""); res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL, &ndis_power, sizeof(ndis_power)); if (res) return -EOPNOTSUPP; wrqu->txpower.flags = IW_TXPOW_MWATT; wrqu->txpower.disabled = 0; wrqu->txpower.fixed = 0; wrqu->txpower.value = ndis_power; return 0; } static int iw_set_tx_power(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); ndis_tx_power_level ndis_power; NDIS_STATUS res; ENTER2(""); if (wrqu->txpower.disabled) ndis_power = 0; else { if (wrqu->txpower.flags == IW_TXPOW_MWATT) ndis_power = wrqu->txpower.value; else { // wrqu->txpower.flags == IW_TXPOW_DBM if (wrqu->txpower.value > 20) ndis_power = 128; else if (wrqu->txpower.value < -43) ndis_power = 127; else { signed char tmp; tmp = wrqu->txpower.value; tmp = -12 - tmp; tmp <<= 2; ndis_power = (unsigned char)tmp; } } } TRACE2("%d", ndis_power); res = mp_set(wnd, OID_802_11_TX_POWER_LEVEL, &ndis_power, sizeof(ndis_power)); if (res) EXIT2(return -EOPNOTSUPP); if (ndis_power == 0) res = disassociate(wnd, 0); EXIT2(return 0); } static int iw_get_bitrate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); ULONG ndis_rate; int res; ENTER2(""); res = mp_query(wnd, OID_GEN_LINK_SPEED, &ndis_rate, sizeof(ndis_rate)); if (res) { WARNING("getting bitrate failed (%08X)", res); ndis_rate = 0; } wrqu->bitrate.value = ndis_rate * 100; return 0; } static int iw_set_bitrate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); int i, n; NDIS_STATUS res; ndis_rates_ex rates; ENTER2(""); if (wrqu->bitrate.fixed == 0) EXIT2(return 0); res = mp_query_info(wnd, OID_802_11_SUPPORTED_RATES, &rates, sizeof(rates), &n, NULL); if (res) { WARNING("getting bit rate failed (%08X)", res); EXIT2(return 0); } for (i = 0; i < n; i++) { if (rates[i] & 0x80) continue; if ((rates[i] & 0x7f) * 500000 > wrqu->bitrate.value) { TRACE2("setting rate %d to 0", (rates[i] & 0x7f) * 500000); rates[i] = 0; } } res = mp_set(wnd, OID_802_11_DESIRED_RATES, &rates, n); if (res) { WARNING("setting bit rate failed (%08X)", res); EXIT2(return 0); } return 0; } static int iw_set_dummy(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* Do nothing. Used for ioctls that are not implemented. */ return 0; } static int iw_get_rts_threshold(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); ndis_rts_threshold threshold; NDIS_STATUS res; ENTER2(""); res = mp_query(wnd, OID_802_11_RTS_THRESHOLD, &threshold, sizeof(threshold)); if (res) return -EOPNOTSUPP; wrqu->rts.value = threshold; return 0; } static int iw_set_rts_threshold(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); ndis_rts_threshold threshold; NDIS_STATUS res; ENTER2(""); threshold = wrqu->rts.value; res = mp_set(wnd, OID_802_11_RTS_THRESHOLD, &threshold, sizeof(threshold)); if (res == NDIS_STATUS_INVALID_DATA) return -EINVAL; if (res) return -EOPNOTSUPP; return 0; } static int iw_get_frag_threshold(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); ndis_fragmentation_threshold frag_threshold; NDIS_STATUS res; ENTER2(""); res = mp_query(wnd, OID_802_11_FRAGMENTATION_THRESHOLD, &frag_threshold, sizeof(frag_threshold)); if (res) return -ENOTSUPP; wrqu->frag.value = frag_threshold; return 0; } static int iw_set_frag_threshold(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); ndis_rts_threshold threshold; NDIS_STATUS res; ENTER2(""); threshold = wrqu->frag.value; res = mp_set(wnd, OID_802_11_FRAGMENTATION_THRESHOLD, &threshold, sizeof(threshold)); if (res == NDIS_STATUS_INVALID_DATA) return -EINVAL; if (res) return -EOPNOTSUPP; return 0; } int get_ap_address(struct ndis_device *wnd, mac_address ap_addr) { NDIS_STATUS res; res = mp_query(wnd, OID_802_11_BSSID, ap_addr, ETH_ALEN); TRACE2(MACSTRSEP, MAC2STR(ap_addr)); if (res) { TRACE2("res: %08X", res); memset(ap_addr, 0x0, ETH_ALEN); EXIT2(return -EOPNOTSUPP); } EXIT2(return 0); } static int iw_get_ap_address(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); mac_address ap_addr; ENTER2(""); get_ap_address(wnd, ap_addr); memcpy(wrqu->ap_addr.sa_data, ap_addr, ETH_ALEN); wrqu->ap_addr.sa_family = ARPHRD_ETHER; EXIT2(return 0); } static int iw_set_ap_address(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; mac_address ap_addr; ENTER2(""); memcpy(ap_addr, wrqu->ap_addr.sa_data, ETH_ALEN); TRACE2(MACSTRSEP, MAC2STR(ap_addr)); res = mp_set(wnd, OID_802_11_BSSID, ap_addr, ETH_ALEN); /* user apps may set ap's mac address, which is not required; * they may fail to work if this function fails, so return * success */ if (res) WARNING("setting AP mac address failed (%08X)", res); EXIT2(return 0); } int set_iw_auth_mode(struct ndis_device *wnd, int wpa_version, int auth_80211_alg) { NDIS_STATUS res; ULONG auth_mode; ENTER2("%d, %d", wpa_version, auth_80211_alg); if (wpa_version & IW_AUTH_WPA_VERSION_WPA2) { if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X) auth_mode = Ndis802_11AuthModeWPA2; else auth_mode = Ndis802_11AuthModeWPA2PSK; } else if (wpa_version & IW_AUTH_WPA_VERSION_WPA) { if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X) auth_mode = Ndis802_11AuthModeWPA; else if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_PSK) auth_mode = Ndis802_11AuthModeWPAPSK; else auth_mode = Ndis802_11AuthModeWPANone; } else if (auth_80211_alg & IW_AUTH_ALG_SHARED_KEY) { if (auth_80211_alg & IW_AUTH_ALG_OPEN_SYSTEM) auth_mode = Ndis802_11AuthModeAutoSwitch; else auth_mode = Ndis802_11AuthModeShared; } else auth_mode = Ndis802_11AuthModeOpen; res = mp_set_int(wnd, OID_802_11_AUTHENTICATION_MODE, auth_mode); if (res) { WARNING("setting auth mode to %u failed (%08X)", auth_mode, res); if (res == NDIS_STATUS_INVALID_DATA) EXIT2(return -EINVAL); return -EOPNOTSUPP; } wnd->iw_auth_wpa_version = wpa_version; wnd->iw_auth_80211_alg = auth_80211_alg; EXIT2(return 0); } int set_ndis_auth_mode(struct ndis_device *wnd, ULONG auth_mode) { NDIS_STATUS res; ENTER2("%d", auth_mode); res = mp_set_int(wnd, OID_802_11_AUTHENTICATION_MODE, auth_mode); if (res) { WARNING("setting auth mode to %u failed (%08X)", auth_mode, res); if (res == NDIS_STATUS_INVALID_DATA) EXIT2(return -EINVAL); return -EOPNOTSUPP; } switch (auth_mode) { case Ndis802_11AuthModeWPA: wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA; wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_802_1X; break; case Ndis802_11AuthModeWPAPSK: wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA; wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; case Ndis802_11AuthModeWPANone: wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; break; case Ndis802_11AuthModeWPA2: wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA2; wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_802_1X; break; case Ndis802_11AuthModeWPA2PSK: wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_WPA2; wnd->iw_auth_key_mgmt = IW_AUTH_KEY_MGMT_PSK; break; case Ndis802_11AuthModeOpen: wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; wnd->iw_auth_80211_alg = IW_AUTH_ALG_OPEN_SYSTEM; break; case Ndis802_11AuthModeShared: wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; wnd->iw_auth_80211_alg = IW_AUTH_ALG_SHARED_KEY; break; case Ndis802_11AuthModeAutoSwitch: wnd->iw_auth_wpa_version = IW_AUTH_WPA_VERSION_DISABLED; wnd->iw_auth_80211_alg = IW_AUTH_ALG_SHARED_KEY; wnd->iw_auth_80211_alg |= IW_AUTH_ALG_OPEN_SYSTEM; break; default: WARNING("invalid authentication algorithm: %d", auth_mode); break; } EXIT2(return 0); } int set_auth_mode(struct ndis_device *wnd) { return set_iw_auth_mode(wnd, wnd->iw_auth_wpa_version, wnd->iw_auth_80211_alg); } int get_ndis_auth_mode(struct ndis_device *wnd) { ULONG mode; NDIS_STATUS res; res = mp_query_int(wnd, OID_802_11_AUTHENTICATION_MODE, &mode); if (res) { WARNING("getting authentication mode failed (%08X)", res); EXIT2(return -EOPNOTSUPP); } TRACE2("%d", mode); return mode; } int set_iw_encr_mode(struct ndis_device *wnd, int cipher_pairwise, int cipher_groupwise) { NDIS_STATUS res; ULONG ndis_mode; ENTER2("%d, %d", cipher_pairwise, cipher_groupwise); if (cipher_pairwise & IW_AUTH_CIPHER_CCMP) ndis_mode = Ndis802_11Encryption3Enabled; else if (cipher_pairwise & IW_AUTH_CIPHER_TKIP) ndis_mode = Ndis802_11Encryption2Enabled; else if (cipher_pairwise & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) ndis_mode = Ndis802_11Encryption1Enabled; else if (cipher_groupwise & IW_AUTH_CIPHER_CCMP) ndis_mode = Ndis802_11Encryption3Enabled; else if (cipher_groupwise & IW_AUTH_CIPHER_TKIP) ndis_mode = Ndis802_11Encryption2Enabled; else ndis_mode = Ndis802_11EncryptionDisabled; res = mp_set_int(wnd, OID_802_11_ENCRYPTION_STATUS, ndis_mode); if (res) { WARNING("setting encryption mode to %u failed (%08X)", ndis_mode, res); if (res == NDIS_STATUS_INVALID_DATA) EXIT2(return -EINVAL); return -EOPNOTSUPP; } wnd->iw_auth_cipher_pairwise = cipher_pairwise; wnd->iw_auth_cipher_group = cipher_groupwise; EXIT2(return 0); } int set_encr_mode(struct ndis_device *wnd) { return set_iw_encr_mode(wnd, wnd->iw_auth_cipher_pairwise, wnd->iw_auth_cipher_group); } int get_ndis_encr_mode(struct ndis_device *wnd) { ULONG mode; NDIS_STATUS res; ENTER2(""); res = mp_query_int(wnd, OID_802_11_ENCRYPTION_STATUS, &mode); if (res) { WARNING("getting encryption status failed (%08X)", res); EXIT2(return -EOPNOTSUPP); } else EXIT2(return mode); } static int iw_get_encr(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); int index, mode; struct encr_info *encr_info = &wnd->encr_info; ENTER2("wnd = %p", wnd); wrqu->data.length = 0; extra[0] = 0; index = (wrqu->encoding.flags & IW_ENCODE_INDEX); TRACE2("index = %u", index); if (index > 0) index--; else index = encr_info->tx_key_index; if (index < 0 || index >= MAX_ENCR_KEYS) { WARNING("encryption index out of range (%u)", index); EXIT2(return -EINVAL); } if (index != encr_info->tx_key_index) { if (encr_info->keys[index].length > 0) { wrqu->data.flags |= IW_ENCODE_ENABLED; wrqu->data.length = encr_info->keys[index].length; memcpy(extra, encr_info->keys[index].key, encr_info->keys[index].length); } else wrqu->data.flags |= IW_ENCODE_DISABLED; EXIT2(return 0); } /* transmit key */ mode = get_ndis_encr_mode(wnd); if (mode < 0) EXIT2(return -EOPNOTSUPP); if (mode == Ndis802_11EncryptionDisabled || mode == Ndis802_11EncryptionNotSupported) wrqu->data.flags |= IW_ENCODE_DISABLED; else { if (mode == Ndis802_11Encryption1KeyAbsent || mode == Ndis802_11Encryption2KeyAbsent || mode == Ndis802_11Encryption3KeyAbsent) wrqu->data.flags |= IW_ENCODE_NOKEY; else { wrqu->data.flags |= IW_ENCODE_ENABLED; wrqu->encoding.flags |= index+1; wrqu->data.length = encr_info->keys[index].length; memcpy(extra, encr_info->keys[index].key, encr_info->keys[index].length); } } mode = get_ndis_auth_mode(wnd); if (mode < 0) EXIT2(return -EOPNOTSUPP); if (mode == Ndis802_11AuthModeOpen) wrqu->data.flags |= IW_ENCODE_OPEN; else if (mode == Ndis802_11AuthModeAutoSwitch) wrqu->data.flags |= IW_ENCODE_RESTRICTED; else // Ndis802_11AuthModeAutoSwitch, Ndis802_11AuthModeWPA etc. wrqu->data.flags |= IW_ENCODE_RESTRICTED; EXIT2(return 0); } /* index must be 0 - N, as per NDIS */ int add_wep_key(struct ndis_device *wnd, char *key, int key_len, int index) { struct ndis_encr_key ndis_key; NDIS_STATUS res; ENTER2("key index: %d, length: %d", index, key_len); if (key_len <= 0 || key_len > NDIS_ENCODING_TOKEN_MAX) { WARNING("invalid key length (%d)", key_len); EXIT2(return -EINVAL); } if (index < 0 || index >= MAX_ENCR_KEYS) { WARNING("invalid key index (%d)", index); EXIT2(return -EINVAL); } ndis_key.struct_size = sizeof(ndis_key); ndis_key.length = key_len; memcpy(&ndis_key.key, key, key_len); ndis_key.index = index; if (index == wnd->encr_info.tx_key_index) { ndis_key.index |= (1 << 31); res = set_iw_encr_mode(wnd, IW_AUTH_CIPHER_WEP104, IW_AUTH_CIPHER_NONE); if (res) WARNING("encryption couldn't be enabled (%08X)", res); } TRACE2("key %d: " MACSTRSEP, index, MAC2STR(key)); res = mp_set(wnd, OID_802_11_ADD_WEP, &ndis_key, sizeof(ndis_key)); if (res) { WARNING("adding encryption key %d failed (%08X)", index+1, res); EXIT2(return -EINVAL); } /* Atheros driver messes up ndis_key during ADD_WEP, so * don't rely on that; instead use info in key and key_len */ wnd->encr_info.keys[index].length = key_len; memcpy(&wnd->encr_info.keys[index].key, key, key_len); EXIT2(return 0); } /* remove_key is for both wep and wpa */ static int remove_key(struct ndis_device *wnd, int index, mac_address bssid) { NDIS_STATUS res; if (wnd->encr_info.keys[index].length == 0) EXIT2(return 0); wnd->encr_info.keys[index].length = 0; memset(&wnd->encr_info.keys[index].key, 0, sizeof(wnd->encr_info.keys[index].length)); if (wnd->iw_auth_cipher_pairwise == IW_AUTH_CIPHER_TKIP || wnd->iw_auth_cipher_pairwise == IW_AUTH_CIPHER_CCMP || wnd->iw_auth_cipher_group == IW_AUTH_CIPHER_TKIP || wnd->iw_auth_cipher_group == IW_AUTH_CIPHER_CCMP) { struct ndis_remove_key remove_key; remove_key.struct_size = sizeof(remove_key); remove_key.index = index; if (bssid) { /* pairwise key */ if (memcmp(bssid, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) != 0) remove_key.index |= (1 << 30); memcpy(remove_key.bssid, bssid, sizeof(remove_key.bssid)); } else memset(remove_key.bssid, 0xff, sizeof(remove_key.bssid)); if (mp_set(wnd, OID_802_11_REMOVE_KEY, &remove_key, sizeof(remove_key))) EXIT2(return -EINVAL); } else { ndis_key_index keyindex = index; res = mp_set_int(wnd, OID_802_11_REMOVE_WEP, keyindex); if (res) { WARNING("removing encryption key %d failed (%08X)", keyindex, res); EXIT2(return -EINVAL); } } /* if it is transmit key, disable encryption */ if (index == wnd->encr_info.tx_key_index) { res = set_iw_encr_mode(wnd, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE); if (res) WARNING("changing encr status failed (%08X)", res); } TRACE2("key %d removed", index); EXIT2(return 0); } static int iw_set_wep(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; unsigned int index, key_len; struct encr_info *encr_info = &wnd->encr_info; unsigned char *key; ENTER2(""); index = (wrqu->encoding.flags & IW_ENCODE_INDEX); TRACE2("index = %u", index); /* iwconfig gives index as 1 - N */ if (index > 0) index--; else index = encr_info->tx_key_index; if (index < 0 || index >= MAX_ENCR_KEYS) { WARNING("encryption index out of range (%u)", index); EXIT2(return -EINVAL); } /* remove key if disabled */ if (wrqu->data.flags & IW_ENCODE_DISABLED) { if (remove_key(wnd, index, NULL)) EXIT2(return -EINVAL); else EXIT2(return 0); } /* global encryption state (for all keys) */ if (wrqu->data.flags & IW_ENCODE_OPEN) res = set_ndis_auth_mode(wnd, Ndis802_11AuthModeOpen); else // if (wrqu->data.flags & IW_ENCODE_RESTRICTED) res = set_ndis_auth_mode(wnd, Ndis802_11AuthModeShared); if (res) { WARNING("setting authentication mode failed (%08X)", res); EXIT2(return -EINVAL); } TRACE2("key length: %d", wrqu->data.length); if (wrqu->data.length > 0) { key_len = wrqu->data.length; key = extra; } else { // must be set as tx key if (encr_info->keys[index].length == 0) { WARNING("key %d is not set", index+1); EXIT2(return -EINVAL); } key_len = encr_info->keys[index].length; key = encr_info->keys[index].key; encr_info->tx_key_index = index; } if (add_wep_key(wnd, key, key_len, index)) EXIT2(return -EINVAL); if (index == encr_info->tx_key_index) { /* if transmit key is at index other than 0, some * drivers, at least Atheros and TI, want another * (global) non-transmit key to be set; don't know why */ if (index != 0) { int i; for (i = 0; i < MAX_ENCR_KEYS; i++) if (i != index && encr_info->keys[i].length != 0) break; if (i == MAX_ENCR_KEYS) { if (index == 0) i = index + 1; else i = index - 1; if (add_wep_key(wnd, key, key_len, i)) WARNING("couldn't add broadcast key" " at %d", i); } } /* ndis drivers want essid to be set after setting encr */ set_essid(wnd, wnd->essid.essid, wnd->essid.length); } EXIT2(return 0); } static int iw_set_nick(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); if (wrqu->data.length >= IW_ESSID_MAX_SIZE || wrqu->data.length <= 0) return -EINVAL; memcpy(wnd->nick, extra, wrqu->data.length); wnd->nick[wrqu->data.length] = 0; return 0; } static int iw_get_nick(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); wrqu->data.length = strlen(wnd->nick); memcpy(extra, wnd->nick, wrqu->data.length); return 0; } #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) && !defined(IW_REQUEST_FLAG_COMPAT) #define iwe_stream_add_event(a, b, c, d, e) iwe_stream_add_event(b, c, d, e) #define iwe_stream_add_point(a, b, c, d, e) iwe_stream_add_point(b, c, d, e) #define iwe_stream_add_value(a, b, c, d, e, f) \ iwe_stream_add_value(b, c, d, e, f) #define iwe_stream_lcp_len(a) IW_EV_LCP_LEN #endif static char *ndis_translate_scan(struct net_device *dev, struct iw_request_info *info, char *event, char *end_buf, void *item) { struct iw_event iwe; char *current_val; int i, nrates; unsigned char buf[MAX_WPA_IE_LEN * 2 + 30]; struct ndis_wlan_bssid *bssid; struct ndis_wlan_bssid_ex *bssid_ex; ENTER2("%p, %p", event, item); bssid = item; bssid_ex = item; /* add mac address */ memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWAP; iwe.u.ap_addr.sa_family = ARPHRD_ETHER; iwe.len = IW_EV_ADDR_LEN; memcpy(iwe.u.ap_addr.sa_data, bssid->mac, ETH_ALEN); event = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_ADDR_LEN); /* add essid */ memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWESSID; iwe.u.data.length = bssid->ssid.length; if (iwe.u.data.length > IW_ESSID_MAX_SIZE) iwe.u.data.length = IW_ESSID_MAX_SIZE; iwe.u.data.flags = 1; iwe.len = IW_EV_POINT_LEN + iwe.u.data.length; event = iwe_stream_add_point(info, event, end_buf, &iwe, bssid->ssid.essid); /* add protocol name */ memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWNAME; strncpy(iwe.u.name, network_type_to_name(bssid->net_type), IFNAMSIZ); event = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_CHAR_LEN); /* add mode */ memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWMODE; if (bssid->mode == Ndis802_11IBSS) iwe.u.mode = IW_MODE_ADHOC; else if (bssid->mode == Ndis802_11Infrastructure) iwe.u.mode = IW_MODE_INFRA; else // if (bssid->mode == Ndis802_11AutoUnknown) iwe.u.mode = IW_MODE_AUTO; event = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_UINT_LEN); /* add freq */ memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWFREQ; iwe.u.freq.m = bssid->config.ds_config; if (bssid->config.ds_config > 1000000) { iwe.u.freq.m = bssid->config.ds_config / 10; iwe.u.freq.e = 1; } else iwe.u.freq.m = bssid->config.ds_config; /* convert from kHz to Hz */ iwe.u.freq.e += 3; iwe.len = IW_EV_FREQ_LEN; event = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_FREQ_LEN); /* add qual */ memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVQUAL; i = 100 * (bssid->rssi - WL_NOISE) / (WL_SIGMAX - WL_NOISE); if (i < 0) i = 0; else if (i > 100) i = 100; iwe.u.qual.level = bssid->rssi; iwe.u.qual.noise = WL_NOISE; iwe.u.qual.qual = i; iwe.len = IW_EV_QUAL_LEN; event = iwe_stream_add_event(info, event, end_buf, &iwe, IW_EV_QUAL_LEN); /* add key info */ memset(&iwe, 0, sizeof(iwe)); iwe.cmd = SIOCGIWENCODE; if (bssid->privacy == Ndis802_11PrivFilterAcceptAll) iwe.u.data.flags = IW_ENCODE_DISABLED; else iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; iwe.u.data.length = 0; iwe.len = IW_EV_POINT_LEN; event = iwe_stream_add_point(info, event, end_buf, &iwe, bssid->ssid.essid); /* add rate */ memset(&iwe, 0, sizeof(iwe)); current_val = event + iwe_stream_lcp_len(info); iwe.cmd = SIOCGIWRATE; if (bssid->length > sizeof(*bssid)) nrates = NDIS_MAX_RATES_EX; else nrates = NDIS_MAX_RATES; for (i = 0 ; i < nrates ; i++) { if (bssid->rates[i] & 0x7f) { iwe.u.bitrate.value = ((bssid->rates[i] & 0x7f) * 500000); current_val = iwe_stream_add_value(info, event, current_val, end_buf, &iwe, IW_EV_PARAM_LEN); } } if ((current_val - event) > iwe_stream_lcp_len(info)) event = current_val; memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; sprintf(buf, "bcn_int=%d", bssid->config.beacon_period); iwe.u.data.length = strlen(buf); event = iwe_stream_add_point(info, event, end_buf, &iwe, buf); memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVCUSTOM; sprintf(buf, "atim=%u", bssid->config.atim_window); iwe.u.data.length = strlen(buf); event = iwe_stream_add_point(info, event, end_buf, &iwe, buf); TRACE2("%d, %u", bssid->length, (unsigned int)sizeof(*bssid)); if (bssid->length > sizeof(*bssid)) { unsigned char *iep = (unsigned char *)bssid_ex->ies + sizeof(struct ndis_fixed_ies); no_warn_unused unsigned char *end = iep + bssid_ex->ie_length; while (iep + 1 < end && iep + 2 + iep[1] <= end) { unsigned char ielen = 2 + iep[1]; if (ielen > SSID_MAX_WPA_IE_LEN) { iep += ielen; continue; } if ((iep[0] == WLAN_EID_GENERIC && iep[1] >= 4 && memcmp(iep + 2, "\x00\x50\xf2\x01", 4) == 0) || iep[0] == RSN_INFO_ELEM) { memset(&iwe, 0, sizeof(iwe)); iwe.cmd = IWEVGENIE; iwe.u.data.length = ielen; event = iwe_stream_add_point(info, event, end_buf, &iwe, iep); } iep += ielen; } } TRACE2("event = %p, current_val = %p", event, current_val); EXIT2(return event); } int set_scan(struct ndis_device *wnd) { NDIS_STATUS res; ENTER2(""); res = mp_set(wnd, OID_802_11_BSSID_LIST_SCAN, NULL, 0); if (res) { WARNING("scanning failed (%08X)", res); EXIT2(return -EOPNOTSUPP); } wnd->scan_timestamp = jiffies; EXIT2(return 0); } static int iw_set_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); return set_scan(wnd); } static int iw_get_scan(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); unsigned int i, list_len, needed; NDIS_STATUS res; struct ndis_bssid_list *bssid_list = NULL; char *event = extra; struct ndis_wlan_bssid *cur_item ; ENTER2(""); if (time_before(jiffies, wnd->scan_timestamp + 3 * HZ)) return -EAGAIN; /* try with space for a few scan items */ list_len = sizeof(ULONG) + sizeof(struct ndis_wlan_bssid_ex) * 8; bssid_list = kmalloc(list_len, GFP_KERNEL); if (!bssid_list) { ERROR("couldn't allocate memory"); return -ENOMEM; } /* some drivers don't set bssid_list->num_items to 0 if OID_802_11_BSSID_LIST returns no items (prism54 driver, e.g.,) */ memset(bssid_list, 0, list_len); needed = 0; res = mp_query_info(wnd, OID_802_11_BSSID_LIST, bssid_list, list_len, NULL, &needed); if (res == NDIS_STATUS_INVALID_LENGTH || res == NDIS_STATUS_BUFFER_TOO_SHORT) { /* now try with required space */ kfree(bssid_list); list_len = needed; bssid_list = kmalloc(list_len, GFP_KERNEL); if (!bssid_list) { ERROR("couldn't allocate memory"); return -ENOMEM; } memset(bssid_list, 0, list_len); res = mp_query(wnd, OID_802_11_BSSID_LIST, bssid_list, list_len); } if (res) { WARNING("getting BSSID list failed (%08X)", res); kfree(bssid_list); EXIT2(return -EOPNOTSUPP); } TRACE2("%d", bssid_list->num_items); cur_item = &bssid_list->bssid[0]; for (i = 0; i < bssid_list->num_items; i++) { event = ndis_translate_scan(dev, info, event, extra + IW_SCAN_MAX_DATA, cur_item); cur_item = (struct ndis_wlan_bssid *)((char *)cur_item + cur_item->length); } wrqu->data.length = event - extra; wrqu->data.flags = 0; kfree(bssid_list); EXIT2(return 0); } static int iw_set_power_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; ULONG power_mode; if (wrqu->power.disabled == 1) power_mode = NDIS_POWER_OFF; else if (wrqu->power.flags & IW_POWER_MIN) power_mode = NDIS_POWER_MIN; else // if (wrqu->power.flags & IW_POWER_MAX) power_mode = NDIS_POWER_MAX; TRACE2("%d", power_mode); res = mp_set(wnd, OID_802_11_POWER_MODE, &power_mode, sizeof(power_mode)); if (res) WARNING("setting power mode failed (%08X)", res); return 0; } static int iw_get_power_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; ULONG power_mode; ENTER2(""); res = mp_query(wnd, OID_802_11_POWER_MODE, &power_mode, sizeof(power_mode)); if (res) return -ENOTSUPP; if (power_mode == NDIS_POWER_OFF) wrqu->power.disabled = 1; else { if (wrqu->power.flags != 0) return 0; wrqu->power.flags |= IW_POWER_ALL_R; wrqu->power.flags |= IW_POWER_TIMEOUT; wrqu->power.value = 0; wrqu->power.disabled = 0; if (power_mode == NDIS_POWER_MIN) wrqu->power.flags |= IW_POWER_MIN; else // if (power_mode == NDIS_POWER_MAX) wrqu->power.flags |= IW_POWER_MAX; } return 0; } static int iw_get_sensitivity(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; ndis_rssi rssi_trigger; ENTER2(""); res = mp_query(wnd, OID_802_11_RSSI_TRIGGER, &rssi_trigger, sizeof(rssi_trigger)); if (res) return -EOPNOTSUPP; wrqu->param.value = rssi_trigger; wrqu->param.disabled = (rssi_trigger == 0); wrqu->param.fixed = 1; return 0; } static int iw_set_sensitivity(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; ndis_rssi rssi_trigger; ENTER2(""); if (wrqu->param.disabled) rssi_trigger = 0; else rssi_trigger = wrqu->param.value; res = mp_set(wnd, OID_802_11_RSSI_TRIGGER, &rssi_trigger, sizeof(rssi_trigger)); if (res == NDIS_STATUS_INVALID_DATA) return -EINVAL; if (res) return -EOPNOTSUPP; return 0; } static int iw_get_ndis_stats(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); struct iw_statistics *stats = &wnd->iw_stats; memcpy(&wrqu->qual, &stats->qual, sizeof(stats->qual)); return 0; } static int iw_get_range(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct iw_range *range = (struct iw_range *)extra; struct iw_point *data = &wrqu->data; struct ndis_device *wnd = netdev_priv(dev); unsigned int i, n; NDIS_STATUS res; ndis_rates_ex rates; ndis_tx_power_level tx_power; ENTER2(""); data->length = sizeof(struct iw_range); memset(range, 0, sizeof(struct iw_range)); range->txpower_capa = IW_TXPOW_MWATT; range->num_txpower = 0; res = mp_query(wnd, OID_802_11_TX_POWER_LEVEL, &tx_power, sizeof(tx_power)); if (!res) { range->num_txpower = 1; range->txpower[0] = tx_power; } range->we_version_compiled = WIRELESS_EXT; range->we_version_source = 18; range->retry_capa = IW_RETRY_LIMIT; range->retry_flags = IW_RETRY_LIMIT; range->min_retry = 0; range->max_retry = 255; range->num_channels = 1; range->max_qual.qual = 100; range->max_qual.level = 154; range->max_qual.noise = 154; range->sensitivity = 3; range->max_encoding_tokens = 4; range->num_encoding_sizes = 2; range->encoding_size[0] = 5; range->encoding_size[1] = 13; range->num_bitrates = 0; memset(&rates, 0, sizeof(rates)); res = mp_query_info(wnd, OID_802_11_SUPPORTED_RATES, &rates, sizeof(rates), &n, NULL); if (res) WARNING("getting bit rates failed: %08X", res); else { for (i = 0; i < n && range->num_bitrates < IW_MAX_BITRATES; i++) if (rates[i] & 0x80) continue; else if (rates[i] & 0x7f) { range->bitrate[range->num_bitrates] = (rates[i] & 0x7f) * 500000; range->num_bitrates++; } } range->num_channels = (sizeof(freq_chan)/sizeof(freq_chan[0])); for (i = 0; i < (sizeof(freq_chan)/sizeof(freq_chan[0])) && i < IW_MAX_FREQUENCIES; i++) { range->freq[i].i = i + 1; range->freq[i].m = freq_chan[i] * 100000; range->freq[i].e = 1; } range->num_frequency = i; range->min_rts = 0; range->max_rts = 2347; range->min_frag = 256; range->max_frag = 2346; /* Event capability (kernel + driver) */ range->event_capa[0] = (IW_EVENT_CAPA_K_0 | IW_EVENT_CAPA_MASK(SIOCGIWTHRSPY) | IW_EVENT_CAPA_MASK(SIOCGIWAP) | IW_EVENT_CAPA_MASK(SIOCGIWSCAN)); range->event_capa[1] = IW_EVENT_CAPA_K_1; range->event_capa[4] = (IW_EVENT_CAPA_MASK(IWEVTXDROP) | IW_EVENT_CAPA_MASK(IWEVCUSTOM) | IW_EVENT_CAPA_MASK(IWEVREGISTERED) | IW_EVENT_CAPA_MASK(IWEVEXPIRED)); range->enc_capa = 0; if (test_bit(Ndis802_11Encryption2Enabled, &wnd->capa.encr)) range->enc_capa |= IW_ENC_CAPA_CIPHER_TKIP; if (test_bit(Ndis802_11Encryption3Enabled, &wnd->capa.encr)) range->enc_capa |= IW_ENC_CAPA_CIPHER_CCMP; if (test_bit(Ndis802_11AuthModeWPA, &wnd->capa.auth) || test_bit(Ndis802_11AuthModeWPAPSK, &wnd->capa.auth)) range->enc_capa |= IW_ENC_CAPA_WPA; if (test_bit(Ndis802_11AuthModeWPA2, &wnd->capa.auth) || test_bit(Ndis802_11AuthModeWPA2PSK, &wnd->capa.auth)) range->enc_capa |= IW_ENC_CAPA_WPA2; return 0; } void set_default_iw_params(struct ndis_device *wnd) { wnd->iw_auth_key_mgmt = 0; wnd->iw_auth_wpa_version = 0; set_infra_mode(wnd, Ndis802_11Infrastructure); set_ndis_auth_mode(wnd, Ndis802_11AuthModeOpen); set_priv_filter(wnd); set_iw_encr_mode(wnd, IW_AUTH_CIPHER_NONE, IW_AUTH_CIPHER_NONE); } static int deauthenticate(struct ndis_device *wnd) { int ret; ENTER2(""); ret = disassociate(wnd, 1); set_default_iw_params(wnd); EXIT2(return ret); } NDIS_STATUS disassociate(struct ndis_device *wnd, int reset_ssid) { NDIS_STATUS res; u8 buf[NDIS_ESSID_MAX_SIZE]; int i; TRACE2(""); res = mp_set(wnd, OID_802_11_DISASSOCIATE, NULL, 0); /* disassociate causes radio to be turned off; if reset_ssid * is given, set ssid to random to enable radio */ if (reset_ssid) { get_random_bytes(buf, sizeof(buf)); for (i = 0; i < sizeof(buf); i++) buf[i] = 'a' + (buf[i] % 26); set_essid(wnd, buf, sizeof(buf)); } return res; } static ULONG ndis_priv_mode(struct ndis_device *wnd) { if (wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2 || wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA) return Ndis802_11PrivFilter8021xWEP; else return Ndis802_11PrivFilterAcceptAll; } int set_priv_filter(struct ndis_device *wnd) { NDIS_STATUS res; ULONG flags; flags = ndis_priv_mode(wnd); ENTER2("filter: %d", flags); res = mp_set_int(wnd, OID_802_11_PRIVACY_FILTER, flags); if (res) TRACE2("setting privacy filter to %d failed (%08X)", flags, res); EXIT2(return 0); } static int iw_set_mlme(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); struct iw_mlme *mlme = (struct iw_mlme *)extra; ENTER2(""); switch (mlme->cmd) { case IW_MLME_DEAUTH: return deauthenticate(wnd); case IW_MLME_DISASSOC: TRACE2("cmd=%d reason_code=%d", mlme->cmd, mlme->reason_code); return disassociate(wnd, 1); default: return -EOPNOTSUPP; } return 0; } static int iw_set_genie(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* * NDIS drivers do not allow IEs to be configured; this is * done by the driver based on other configuration. Return 0 * to avoid causing issues with user space programs that * expect this function to succeed. */ return 0; } static int iw_set_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); TRACE2("index=%d value=%d", wrqu->param.flags & IW_AUTH_INDEX, wrqu->param.value); switch (wrqu->param.flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: wnd->iw_auth_wpa_version = wrqu->param.value; break; case IW_AUTH_CIPHER_PAIRWISE: wnd->iw_auth_cipher_pairwise = wrqu->param.value; break; case IW_AUTH_CIPHER_GROUP: wnd->iw_auth_cipher_group = wrqu->param.value; break; case IW_AUTH_KEY_MGMT: wnd->iw_auth_key_mgmt = wrqu->param.value; break; case IW_AUTH_80211_AUTH_ALG: wnd->iw_auth_80211_alg = wrqu->param.value; break; case IW_AUTH_WPA_ENABLED: if (wrqu->param.value) deauthenticate(wnd); break; case IW_AUTH_TKIP_COUNTERMEASURES: case IW_AUTH_DROP_UNENCRYPTED: case IW_AUTH_RX_UNENCRYPTED_EAPOL: case IW_AUTH_PRIVACY_INVOKED: TRACE2("%d not implemented: %d", wrqu->param.flags & IW_AUTH_INDEX, wrqu->param.value); break; default: WARNING("invalid cmd %d", wrqu->param.flags & IW_AUTH_INDEX); return -EOPNOTSUPP; } return 0; } static int iw_get_auth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); ENTER2("index=%d", wrqu->param.flags & IW_AUTH_INDEX); switch (wrqu->param.flags & IW_AUTH_INDEX) { case IW_AUTH_WPA_VERSION: wrqu->param.value = wnd->iw_auth_wpa_version; break; case IW_AUTH_CIPHER_PAIRWISE: wrqu->param.value = wnd->iw_auth_cipher_pairwise; break; case IW_AUTH_CIPHER_GROUP: wrqu->param.value = wnd->iw_auth_cipher_group; break; case IW_AUTH_KEY_MGMT: wrqu->param.value = wnd->iw_auth_key_mgmt; break; case IW_AUTH_80211_AUTH_ALG: wrqu->param.value = wnd->iw_auth_80211_alg; break; default: WARNING("invalid cmd %d", wrqu->param.flags & IW_AUTH_INDEX); return -EOPNOTSUPP; } return 0; } static int iw_set_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct iw_encode_ext *ext = (struct iw_encode_ext *)extra; struct ndis_device *wnd = netdev_priv(dev); struct ndis_add_key ndis_key; int i, keyidx; NDIS_STATUS res; u8 *addr; keyidx = wrqu->encoding.flags & IW_ENCODE_INDEX; ENTER2("%d", keyidx); if (keyidx) keyidx--; else keyidx = wnd->encr_info.tx_key_index; if (keyidx < 0 || keyidx >= MAX_ENCR_KEYS) return -EINVAL; if (ext->alg == WPA_ALG_WEP) { if (!test_bit(Ndis802_11Encryption1Enabled, &wnd->capa.encr)) EXIT2(return -1); if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) wnd->encr_info.tx_key_index = keyidx; if (add_wep_key(wnd, ext->key, ext->key_len, keyidx)) EXIT2(return -1); else EXIT2(return 0); } if ((wrqu->encoding.flags & IW_ENCODE_DISABLED) || ext->alg == IW_ENCODE_ALG_NONE || ext->key_len == 0) EXIT2(return remove_key(wnd, keyidx, ndis_key.bssid)); if (ext->key_len > sizeof(ndis_key.key)) { TRACE2("incorrect key length (%u)", ext->key_len); EXIT2(return -1); } memset(&ndis_key, 0, sizeof(ndis_key)); ndis_key.struct_size = sizeof(ndis_key) - sizeof(ndis_key.key) + ext->key_len; ndis_key.length = ext->key_len; ndis_key.index = keyidx; if (ext->ext_flags & IW_ENCODE_EXT_RX_SEQ_VALID) { for (i = 0; i < 6 ; i++) ndis_key.rsc |= (((u64)ext->rx_seq[i]) << (i * 8)); TRACE2("0x%Lx", ndis_key.rsc); ndis_key.index |= 1 << 29; } addr = ext->addr.sa_data; if (ext->ext_flags & IW_ENCODE_EXT_GROUP_KEY) { /* group key */ if (wnd->infrastructure_mode == Ndis802_11IBSS) memset(ndis_key.bssid, 0xff, ETH_ALEN); else get_ap_address(wnd, ndis_key.bssid); } else { /* pairwise key */ ndis_key.index |= (1 << 30); memcpy(ndis_key.bssid, addr, ETH_ALEN); } TRACE2(MACSTRSEP, MAC2STR(ndis_key.bssid)); if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) ndis_key.index |= (1 << 31); if (ext->alg == IW_ENCODE_ALG_TKIP && ext->key_len == 32) { /* wpa_supplicant gives us the Michael MIC RX/TX keys in * different order than NDIS spec, so swap the order here. */ memcpy(ndis_key.key, ext->key, 16); memcpy(ndis_key.key + 16, ext->key + 24, 8); memcpy(ndis_key.key + 24, ext->key + 16, 8); } else memcpy(ndis_key.key, ext->key, ext->key_len); res = mp_set(wnd, OID_802_11_ADD_KEY, &ndis_key, ndis_key.struct_size); if (res) { TRACE2("adding key failed (%08X), %u", res, ndis_key.struct_size); EXIT2(return -1); } wnd->encr_info.keys[keyidx].length = ext->key_len; memcpy(&wnd->encr_info.keys[keyidx].key, ndis_key.key, ext->key_len); if (ext->ext_flags & IW_ENCODE_EXT_SET_TX_KEY) wnd->encr_info.tx_key_index = keyidx; TRACE2("key %d added", keyidx); EXIT2(return 0); } static int iw_get_encodeext(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { /* struct iw_encode_ext *ext = (struct iw_encode_ext *) extra; */ /* TODO */ ENTER2(""); return 0; } static int iw_set_pmksa(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct iw_pmksa *pmksa = (struct iw_pmksa *)extra; struct ndis_pmkid pmkid; NDIS_STATUS res; struct ndis_device *wnd = netdev_priv(dev); /* TODO: must keep local list of PMKIDs since NDIS drivers * expect that all PMKID entries are included whenever a new * one is added. */ ENTER2("%d", pmksa->cmd); if ((pmksa->cmd == IW_PMKSA_ADD || pmksa->cmd == IW_PMKSA_REMOVE) && (!(wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2))) EXIT2(return -EOPNOTSUPP); memset(&pmkid, 0, sizeof(pmkid)); if (pmksa->cmd == IW_PMKSA_ADD) { pmkid.bssid_info_count = 1; memcpy(pmkid.bssid_info[0].bssid, pmksa->bssid.sa_data, ETH_ALEN); memcpy(pmkid.bssid_info[0].pmkid, pmksa->pmkid, IW_PMKID_LEN); } pmkid.length = sizeof(pmkid); res = mp_set(wnd, OID_802_11_PMKID, &pmkid, pmkid.length); if (res == NDIS_STATUS_FAILURE) EXIT2(return -EOPNOTSUPP); TRACE2("OID_802_11_PMKID -> %d", res); if (res) return -EINVAL; return 0; } #define WEXT(id) [id - SIOCIWFIRST] static const iw_handler ndis_handler[] = { WEXT(SIOCGIWNAME) = iw_get_network_type, WEXT(SIOCSIWESSID) = iw_set_essid, WEXT(SIOCGIWESSID) = iw_get_essid, WEXT(SIOCSIWMODE) = iw_set_infra_mode, WEXT(SIOCGIWMODE) = iw_get_infra_mode, WEXT(SIOCGIWFREQ) = iw_get_freq, WEXT(SIOCSIWFREQ) = iw_set_freq, WEXT(SIOCGIWTXPOW) = iw_get_tx_power, WEXT(SIOCSIWTXPOW) = iw_set_tx_power, WEXT(SIOCGIWRATE) = iw_get_bitrate, WEXT(SIOCSIWRATE) = iw_set_bitrate, WEXT(SIOCGIWRTS) = iw_get_rts_threshold, WEXT(SIOCSIWRTS) = iw_set_rts_threshold, WEXT(SIOCGIWFRAG) = iw_get_frag_threshold, WEXT(SIOCSIWFRAG) = iw_set_frag_threshold, WEXT(SIOCGIWAP) = iw_get_ap_address, WEXT(SIOCSIWAP) = iw_set_ap_address, WEXT(SIOCSIWENCODE) = iw_set_wep, WEXT(SIOCGIWENCODE) = iw_get_encr, WEXT(SIOCSIWSCAN) = iw_set_scan, WEXT(SIOCGIWSCAN) = iw_get_scan, WEXT(SIOCGIWPOWER) = iw_get_power_mode, WEXT(SIOCSIWPOWER) = iw_set_power_mode, WEXT(SIOCGIWRANGE) = iw_get_range, WEXT(SIOCGIWSTATS) = iw_get_ndis_stats, WEXT(SIOCGIWSENS) = iw_get_sensitivity, WEXT(SIOCSIWSENS) = iw_set_sensitivity, WEXT(SIOCGIWNICKN) = iw_get_nick, WEXT(SIOCSIWNICKN) = iw_set_nick, WEXT(SIOCSIWCOMMIT) = iw_set_dummy, WEXT(SIOCSIWMLME) = iw_set_mlme, WEXT(SIOCSIWGENIE) = iw_set_genie, WEXT(SIOCSIWAUTH) = iw_set_auth, WEXT(SIOCGIWAUTH) = iw_get_auth, WEXT(SIOCSIWENCODEEXT) = iw_set_encodeext, WEXT(SIOCGIWENCODEEXT) = iw_get_encodeext, WEXT(SIOCSIWPMKSA) = iw_set_pmksa, }; /* private ioctl's */ static int priv_reset(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { int res; ENTER2(""); res = mp_reset(netdev_priv(dev)); if (res) { WARNING("reset failed: %08X", res); return -EOPNOTSUPP; } return 0; } static int priv_deauthenticate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { int res; ENTER2(""); res = deauthenticate(netdev_priv(dev)); return res; } static int priv_power_profile(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); struct miniport *mp; ULONG profile_inf; ENTER2(""); mp = &wnd->wd->driver->ndis_driver->mp; if (!mp->pnp_event_notify) EXIT2(return -EOPNOTSUPP); /* 1 for AC and 0 for Battery */ if (wrqu->param.value) profile_inf = NdisPowerProfileAcOnLine; else profile_inf = NdisPowerProfileBattery; LIN2WIN4(mp->pnp_event_notify, wnd->nmb->mp_ctx, NdisDevicePnPEventPowerProfileChanged, &profile_inf, sizeof(profile_inf)); EXIT2(return 0); } static int priv_network_type(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); enum network_type network_type; NDIS_STATUS res; char type; ENTER2(""); type = wrqu->param.value; if (type == 'f') network_type = Ndis802_11FH; else if (type == 'b') network_type = Ndis802_11DS; else if (type == 'a') network_type = Ndis802_11OFDM5; else if (type == 'g' || type == 'n') network_type = Ndis802_11OFDM24; else network_type = Ndis802_11Automode; res = mp_set_int(wnd, OID_802_11_NETWORK_TYPE_IN_USE, network_type); if (res) { WARNING("setting network type to %d failed (%08X)", network_type, res); EXIT2(return -EINVAL); } EXIT2(return 0); } static int priv_media_stream_mode(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; int mode; ENTER2(""); if (wrqu->param.value > 0) mode = Ndis802_11MediaStreamOn; else mode = Ndis802_11MediaStreamOff; res = mp_set_int(wnd, OID_802_11_MEDIA_STREAM_MODE, mode); if (res) { WARNING("oid failed (%08X)", res); EXIT2(return -EINVAL); } EXIT2(return 0); } static int priv_reload_defaults(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) { struct ndis_device *wnd = netdev_priv(dev); int res; ENTER2(""); res = mp_set_int(wnd, OID_802_11_RELOAD_DEFAULTS, Ndis802_11ReloadWEPKeys); if (res) { WARNING("reloading defaults failed: %08X", res); return -EOPNOTSUPP; } return 0; } static const struct iw_priv_args priv_args[] = { {PRIV_RESET, 0, 0, "ndis_reset"}, {PRIV_POWER_PROFILE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "power_profile"}, {PRIV_DEAUTHENTICATE, 0, 0, "deauthenticate"}, {PRIV_NETWORK_TYPE, IW_PRIV_TYPE_CHAR | IW_PRIV_SIZE_FIXED | 1, 0, "network_type"}, {PRIV_MEDIA_STREAM_MODE, IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, 0, "media_stream"}, {PRIV_RELOAD_DEFAULTS, 0, 0, "reload_defaults"}, }; #define WEPRIV(id) [id - SIOCIWFIRSTPRIV] static const iw_handler priv_handler[] = { WEPRIV(PRIV_RESET) = priv_reset, WEPRIV(PRIV_POWER_PROFILE) = priv_power_profile, WEPRIV(PRIV_DEAUTHENTICATE) = priv_deauthenticate, WEPRIV(PRIV_NETWORK_TYPE) = priv_network_type, WEPRIV(PRIV_MEDIA_STREAM_MODE) = priv_media_stream_mode, WEPRIV(PRIV_RELOAD_DEFAULTS) = priv_reload_defaults, }; const struct iw_handler_def ndis_handler_def = { .num_standard = sizeof(ndis_handler) / sizeof(ndis_handler[0]), .num_private = sizeof(priv_handler) / sizeof(priv_handler[0]), .num_private_args = sizeof(priv_args) / sizeof(priv_args[0]), .standard = (iw_handler *)ndis_handler, .private = (iw_handler *)priv_handler, .private_args = (struct iw_priv_args *)priv_args, .get_wireless_stats = get_iw_stats, };