📄 iw_ndis.c
字号:
/* * 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 <linux/version.h>#include <linux/wireless.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/if_arp.h>#include <linux/usb.h>#include <linux/random.h>#include <net/iw_handler.h>#include <linux/rtnetlink.h>#include <asm/uaccess.h>#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 wrap_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); DBG_BLOCK(2) { char buf[NDIS_ESSID_MAX_SIZE+1]; memcpy(buf, ssid, ssid_len); buf[ssid_len] = 0; TRACE2("ssid = '%s'", buf); } res = miniport_set_info(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 wrap_ndis_device *wnd){#if WIRELESS_EXT > 17 int auth_mode, encr_mode, priv_mode; priv_mode = Ndis802_11PrivFilterAcceptAll; 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_auth_alg, wnd->iw_auth_key_mgmt, wnd->iw_auth_cipher_pairwise, wnd->iw_auth_cipher_group); if (wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA2) { priv_mode = Ndis802_11PrivFilter8021xWEP; if (wnd->iw_auth_key_mgmt & IW_AUTH_KEY_MGMT_802_1X) auth_mode = Ndis802_11AuthModeWPA2; else auth_mode = Ndis802_11AuthModeWPA2PSK; } else if (wnd->iw_auth_wpa_version & IW_AUTH_WPA_VERSION_WPA) { priv_mode = Ndis802_11PrivFilter8021xWEP; 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 (wnd->iw_auth_80211_auth_alg & IW_AUTH_ALG_SHARED_KEY) { if (wnd->iw_auth_80211_auth_alg & IW_AUTH_ALG_OPEN_SYSTEM) auth_mode = Ndis802_11AuthModeAutoSwitch; else auth_mode = Ndis802_11AuthModeShared; } else auth_mode = Ndis802_11AuthModeOpen; if (wnd->iw_auth_cipher_pairwise & IW_AUTH_CIPHER_CCMP) encr_mode = Ndis802_11Encryption3Enabled; else if (wnd->iw_auth_cipher_pairwise & IW_AUTH_CIPHER_TKIP) encr_mode = Ndis802_11Encryption2Enabled; else if (wnd->iw_auth_cipher_pairwise & (IW_AUTH_CIPHER_WEP40 | IW_AUTH_CIPHER_WEP104)) encr_mode = Ndis802_11Encryption1Enabled; else if (wnd->iw_auth_cipher_group & IW_AUTH_CIPHER_CCMP) encr_mode = Ndis802_11Encryption3Enabled; else if (wnd->iw_auth_cipher_group & IW_AUTH_CIPHER_TKIP) encr_mode = Ndis802_11Encryption2Enabled; else encr_mode = Ndis802_11EncryptionDisabled; TRACE2("priv_mode=%d auth_mode=%d encr_mode=%d", priv_mode, auth_mode, encr_mode); set_priv_filter(wnd, priv_mode); set_auth_mode(wnd, auth_mode); set_encr_mode(wnd, encr_mode);#endif return 0;}static int iw_set_essid(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra){ struct wrap_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; if (wnd->iw_auth_set) { int ret = set_assoc_params(wnd); wnd->iw_auth_set = 0; if (ret < 0) EXIT2(return ret); } 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 wrap_ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; struct ndis_essid req; ENTER2(""); memset(&req, 0, sizeof(req)); res = miniport_query_info(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); extra[req.length] = 0; 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 wrap_ndis_device *wnd, enum network_infrastructure mode){ NDIS_STATUS res; unsigned int i; ENTER2("%d", mode); res = miniport_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 = miniport_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 */ 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 wrap_ndis_device *wnd = netdev_priv(dev); enum network_infrastructure ndis_mode; ENTER2(""); 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 wrap_ndis_device *wnd = netdev_priv(dev); int ndis_mode, iw_mode; NDIS_STATUS res; ENTER2(""); res = miniport_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 wrap_ndis_device *wnd = netdev_priv(dev); unsigned int network_type; NDIS_STATUS res; ENTER2(""); res = miniport_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 wrap_ndis_device *wnd = netdev_priv(dev); NDIS_STATUS res; struct ndis_configuration req; ENTER2(""); memset(&req, 0, sizeof(req)); res = miniport_query_info(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 wrap_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 = miniport_query_info(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 = miniport_set_info(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 wrap_ndis_device *wnd = netdev_priv(dev); ndis_tx_power_level ndis_power; NDIS_STATUS res; ENTER2(""); res = miniport_query_info(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 wrap_ndis_device *wnd = netdev_priv(dev); ndis_tx_power_level ndis_power; NDIS_STATUS res; ENTER2(""); if (wrqu->txpower.disabled) { ndis_power = 0; res = miniport_set_info(wnd, OID_802_11_TX_POWER_LEVEL, &ndis_power, sizeof(ndis_power)); if (res) return -EOPNOTSUPP; res = disassociate(wnd, 1); if (res) return -EOPNOTSUPP; return 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; } } } res = miniport_set_info(wnd, OID_802_11_TX_POWER_LEVEL, &ndis_power, sizeof(ndis_power)); if (res) return -EOPNOTSUPP; return 0;}static int iw_get_bitrate(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra){ struct wrap_ndis_device *wnd = netdev_priv(dev); ULONG ndis_rate; int res; ENTER2(""); res = miniport_query_info(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){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -