// SPDX-License-Identifier: GPL-2.0 /* * cfg80211 - wext compat code * * This is temporary code until all wireless functionality is migrated * into cfg80211, when that happens all the exports here go away and * we directly assign the wireless handlers of wireless interfaces. * * Copyright 2008-2009 Johannes Berg <johannes@sipsolutions.net> * Copyright (C) 2019-2023 Intel Corporation
*/
switch (*mode) { case IW_MODE_INFRA:
type = NL80211_IFTYPE_STATION; break; case IW_MODE_ADHOC:
type = NL80211_IFTYPE_ADHOC; break; case IW_MODE_MONITOR:
type = NL80211_IFTYPE_MONITOR; break; default: return -EINVAL;
}
for (i = 0; i < wdev->wiphy->n_cipher_suites; i++) { switch (wdev->wiphy->cipher_suites[i]) { case WLAN_CIPHER_SUITE_TKIP:
range->enc_capa |= (IW_ENC_CAPA_CIPHER_TKIP |
IW_ENC_CAPA_WPA); break;
case WLAN_CIPHER_SUITE_CCMP:
range->enc_capa |= (IW_ENC_CAPA_CIPHER_CCMP |
IW_ENC_CAPA_WPA2); break;
case WLAN_CIPHER_SUITE_WEP40:
range->encoding_size[range->num_encoding_sizes++] =
WLAN_KEY_LEN_WEP40; break;
case WLAN_CIPHER_SUITE_WEP104:
range->encoding_size[range->num_encoding_sizes++] =
WLAN_KEY_LEN_WEP104; break;
}
}
for (band = 0; band < NUM_NL80211_BANDS; band ++) { struct ieee80211_supported_band *sband;
sband = wdev->wiphy->bands[band];
if (!sband) continue;
for (i = 0; i < sband->n_channels && c < IW_MAX_FREQUENCIES; i++) { struct ieee80211_channel *chan = &sband->channels[i];
if (wdev->wiphy->max_scan_ssids > 0)
range->scan_capa |= IW_SCAN_CAPA_ESSID;
return 0;
}
/** * cfg80211_wext_freq - get wext frequency for non-"auto" * @freq: the wext freq encoding * * Returns: a frequency, or a negative error code, or 0 for auto.
*/ int cfg80211_wext_freq(struct iw_freq *freq)
{ /* * Parse frequency - return 0 for auto and * -EINVAL for impossible things.
*/ if (freq->e == 0) { enum nl80211_band band = NL80211_BAND_2GHZ; if (freq->m < 0) return 0; if (freq->m > 14)
band = NL80211_BAND_5GHZ; return ieee80211_channel_to_frequency(freq->m, band);
} else { int i, div = 1000000; for (i = 0; i < freq->e; i++)
div /= 10; if (div <= 0) return -EINVAL; return freq->m / div;
}
}
if (retry->flags == 0 || (retry->flags & IW_RETRY_SHORT)) { /* * First return short value, iwconfig will ask long value * later if needed
*/
retry->flags |= IW_RETRY_LIMIT | IW_RETRY_SHORT;
retry->value = wdev->wiphy->retry_short; if (wdev->wiphy->retry_long == wdev->wiphy->retry_short)
retry->flags |= IW_RETRY_LONG;
/* * In many cases we won't actually need this, but it's better * to do it first in case the allocation fails. Don't use wext.
*/ if (!wdev->wext.keys) {
wdev->wext.keys = kzalloc(sizeof(*wdev->wext.keys),
GFP_KERNEL); if (!wdev->wext.keys) return -ENOMEM; for (i = 0; i < 4; i++)
wdev->wext.keys->params[i].key =
wdev->wext.keys->data[i];
}
if (wdev->iftype != NL80211_IFTYPE_ADHOC &&
wdev->iftype != NL80211_IFTYPE_STATION) return -EOPNOTSUPP;
if (params->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { if (!wdev->connected) return -ENOLINK;
if (!rdev->ops->set_default_mgmt_key) return -EOPNOTSUPP;
if (remove) {
err = 0; if (wdev->connected ||
(wdev->iftype == NL80211_IFTYPE_ADHOC &&
wdev->u.ibss.current_bss)) { /* * If removing the current TX key, we will need to * join a new IBSS without the privacy bit clear.
*/ if (idx == wdev->wext.default_key &&
wdev->iftype == NL80211_IFTYPE_ADHOC) {
cfg80211_leave_ibss(rdev, wdev->netdev, true);
rejoin = true;
}
if (!pairwise && addr &&
!(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
err = -ENOENT; else
err = rdev_del_key(rdev, dev, -1, idx, pairwise,
addr);
}
wdev->wext.connect.privacy = false; /* * Applications using wireless extensions expect to be * able to delete keys that don't exist, so allow that.
*/ if (err == -ENOENT)
err = 0; if (!err) { if (!addr && idx < 4) {
memset(wdev->wext.keys->data[idx], 0, sizeof(wdev->wext.keys->data[idx]));
wdev->wext.keys->params[idx].key_len = 0;
wdev->wext.keys->params[idx].cipher = 0;
} if (idx == wdev->wext.default_key)
wdev->wext.default_key = -1; elseif (idx == wdev->wext.default_mgmt_key)
wdev->wext.default_mgmt_key = -1;
}
if (!err && rejoin)
err = cfg80211_ibss_wext_join(rdev, wdev);
return err;
}
if (addr)
tx_key = false;
if (cfg80211_validate_key_settings(rdev, params, idx, pairwise, addr)) return -EINVAL;
/* * We only need to store WEP keys, since they're the only keys that * can be set before a connection is established and persist after * disconnecting.
*/ if (!addr && (params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
params->cipher == WLAN_CIPHER_SUITE_WEP104)) {
wdev->wext.keys->params[idx] = *params;
memcpy(wdev->wext.keys->data[idx],
params->key, params->key_len);
wdev->wext.keys->params[idx].key =
wdev->wext.keys->data[idx];
}
if ((params->cipher == WLAN_CIPHER_SUITE_WEP40 ||
params->cipher == WLAN_CIPHER_SUITE_WEP104) &&
(tx_key || (!addr && wdev->wext.default_key == -1))) { if (wdev->connected ||
(wdev->iftype == NL80211_IFTYPE_ADHOC &&
wdev->u.ibss.current_bss)) { /* * If we are getting a new TX key from not having * had one before we need to join a new IBSS with * the privacy bit set.
*/ if (wdev->iftype == NL80211_IFTYPE_ADHOC &&
wdev->wext.default_key == -1) {
cfg80211_leave_ibss(rdev, wdev->netdev, true);
rejoin = true;
}
err = rdev_set_default_key(rdev, dev, -1, idx, true, true);
} if (!err) {
wdev->wext.default_key = idx; if (rejoin)
err = cfg80211_ibss_wext_join(rdev, wdev);
} return err;
}
if (wdev->iftype != NL80211_IFTYPE_STATION &&
wdev->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP;
/* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key ||
!rdev->ops->add_key ||
!rdev->ops->set_default_key) return -EOPNOTSUPP;
guard(wiphy)(&rdev->wiphy); if (wdev->valid_links) return -EOPNOTSUPP;
if (wdev->iftype != NL80211_IFTYPE_STATION &&
wdev->iftype != NL80211_IFTYPE_ADHOC) return -EOPNOTSUPP;
/* no use -- only MFP (set_default_mgmt_key) is optional */ if (!rdev->ops->del_key ||
!rdev->ops->add_key ||
!rdev->ops->set_default_key) return -EOPNOTSUPP;
if (wdev->valid_links) return -EOPNOTSUPP;
switch (ext->alg) { case IW_ENCODE_ALG_NONE:
remove = true;
cipher = 0; break; case IW_ENCODE_ALG_WEP: if (ext->key_len == 5)
cipher = WLAN_CIPHER_SUITE_WEP40; elseif (ext->key_len == 13)
cipher = WLAN_CIPHER_SUITE_WEP104; else return -EINVAL; break; case IW_ENCODE_ALG_TKIP:
cipher = WLAN_CIPHER_SUITE_TKIP; break; case IW_ENCODE_ALG_CCMP:
cipher = WLAN_CIPHER_SUITE_CCMP; break; case IW_ENCODE_ALG_AES_CMAC:
cipher = WLAN_CIPHER_SUITE_AES_CMAC; break; default: return -EOPNOTSUPP;
}
if (erq->flags & IW_ENCODE_DISABLED)
remove = true;
if ((data->txpower.flags & IW_TXPOW_TYPE) != IW_TXPOW_DBM) return -EINVAL; if (data->txpower.flags & IW_TXPOW_RANGE) return -EINVAL;
if (!rdev->ops->set_tx_power) return -EOPNOTSUPP;
/* only change when not disabling */ if (!data->txpower.disabled) {
rfkill_set_sw_state(rdev->wiphy.rfkill, false);
if (data->txpower.fixed) { /* * wext doesn't support negative values, see * below where it's for automatic
*/ if (data->txpower.value < 0) return -EINVAL;
dbm = data->txpower.value;
type = NL80211_TX_POWER_FIXED; /* TODO: do regulatory check! */
} else { /* * Automatic power level setting, max being the value * passed in from userland.
*/ if (data->txpower.value < 0) {
type = NL80211_TX_POWER_AUTOMATIC;
} else {
dbm = data->txpower.value;
type = NL80211_TX_POWER_LIMITED;
}
}
} else { if (rfkill_set_sw_state(rdev->wiphy.rfkill, true))
schedule_work(&rdev->rfkill_block); return 0;
}
if (wdev->iftype != NL80211_IFTYPE_STATION) return -EOPNOTSUPP;
switch (data->flags & IW_AUTH_INDEX) { case IW_AUTH_PRIVACY_INVOKED:
wdev->wext.connect.privacy = data->value; return 0; case IW_AUTH_WPA_VERSION: return cfg80211_set_wpa_version(wdev, data->value); case IW_AUTH_CIPHER_GROUP: return cfg80211_set_cipher_group(wdev, data->value); case IW_AUTH_KEY_MGMT: return cfg80211_set_key_mgt(wdev, data->value); case IW_AUTH_CIPHER_PAIRWISE: return cfg80211_set_cipher_pairwise(wdev, data->value); case IW_AUTH_80211_AUTH_ALG: return cfg80211_set_auth_alg(wdev, data->value); case IW_AUTH_WPA_ENABLED: case IW_AUTH_RX_UNENCRYPTED_EAPOL: case IW_AUTH_DROP_UNENCRYPTED: case IW_AUTH_MFP: return 0; default: return -EOPNOTSUPP;
}
}
staticint cfg80211_wext_giwauth(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra)
{ /* XXX: what do we need? */
if (wdev->iftype != NL80211_IFTYPE_STATION) return -EINVAL;
if (!rdev->ops->set_power_mgmt) return -EOPNOTSUPP;
if (wrq->disabled) {
ps = false;
} else { switch (wrq->flags & IW_POWER_MODE) { case IW_POWER_ON: /* If not specified */ case IW_POWER_MODE: /* If set all mask */ case IW_POWER_ALL_R: /* If explicitly state all */
ps = true; break; default: /* Otherwise we ignore */ return -EINVAL;
}
if (wrq->flags & ~(IW_POWER_MODE | IW_POWER_TIMEOUT)) return -EINVAL;
if (wrq->flags & IW_POWER_TIMEOUT)
timeout = wrq->value / 1000;
}
guard(wiphy)(&rdev->wiphy);
err = rdev_set_power_mgmt(rdev, dev, ps, timeout); if (err) return err;
/* Get wireless statistics. Called by /proc/net/wireless and by SIOCGIWSTATS */ staticstruct iw_statistics *cfg80211_wireless_stats(struct net_device *dev)
{ struct wireless_dev *wdev = dev->ieee80211_ptr; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy); /* we are under RTNL - globally locked - so can use static structs */ staticstruct iw_statistics wstats; staticstruct station_info sinfo = {};
u8 bssid[ETH_ALEN]; int ret;
if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) return NULL;
if (!rdev->ops->get_station) return NULL;
/* Grab BSSID of current BSS, if any */
wiphy_lock(&rdev->wiphy); if (wdev->valid_links || !wdev->links[0].client.current_bss) {
wiphy_unlock(&rdev->wiphy); return NULL;
}
memcpy(bssid, wdev->links[0].client.current_bss->pub.bssid, ETH_ALEN);
memset(&sinfo, 0, sizeof(sinfo));
ret = rdev_get_station(rdev, dev, bssid, &sinfo);
wiphy_unlock(&rdev->wiphy);
if (ret) return NULL;
memset(&wstats, 0, sizeof(wstats));
switch (rdev->wiphy.signal_type) { case CFG80211_SIGNAL_TYPE_MBM: if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_SIGNAL)) { int sig = sinfo.signal;
wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;
wstats.qual.updated |= IW_QUAL_DBM;
wstats.qual.level = sig; if (sig < -110)
sig = -110; elseif (sig > -40)
sig = -40;
wstats.qual.qual = sig + 110; break;
}
fallthrough; case CFG80211_SIGNAL_TYPE_UNSPEC: if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_SIGNAL)) {
wstats.qual.updated |= IW_QUAL_LEVEL_UPDATED;
wstats.qual.updated |= IW_QUAL_QUAL_UPDATED;
wstats.qual.level = sinfo.signal;
wstats.qual.qual = sinfo.signal; break;
}
fallthrough; default:
wstats.qual.updated |= IW_QUAL_LEVEL_INVALID;
wstats.qual.updated |= IW_QUAL_QUAL_INVALID;
}
wstats.qual.updated |= IW_QUAL_NOISE_INVALID; if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC))
wstats.discard.misc = sinfo.rx_dropped_misc; if (sinfo.filled & BIT_ULL(NL80211_STA_INFO_TX_FAILED))
wstats.discard.retries = sinfo.tx_failed;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.