Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-next-2.6

This commit is contained in:
David S. Miller 2010-02-04 08:58:14 -08:00
commit 10be7eb36b
125 changed files with 3770 additions and 2018 deletions

View file

@ -1,7 +1,7 @@
/*
* This is the linux wireless configuration interface.
*
* Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
*/
#include <linux/if.h>
@ -31,15 +31,10 @@ MODULE_AUTHOR("Johannes Berg");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("wireless configuration support");
/* RCU might be appropriate here since we usually
* only read the list, and that can happen quite
* often because we need to do it for each command */
/* RCU-protected (and cfg80211_mutex for writers) */
LIST_HEAD(cfg80211_rdev_list);
int cfg80211_rdev_list_generation;
/*
* This is used to protect the cfg80211_rdev_list
*/
DEFINE_MUTEX(cfg80211_mutex);
/* for debugfs */
@ -418,6 +413,18 @@ int wiphy_register(struct wiphy *wiphy)
int i;
u16 ifmodes = wiphy->interface_modes;
if (WARN_ON(wiphy->addresses && !wiphy->n_addresses))
return -EINVAL;
if (WARN_ON(wiphy->addresses &&
!is_zero_ether_addr(wiphy->perm_addr) &&
memcmp(wiphy->perm_addr, wiphy->addresses[0].addr,
ETH_ALEN)))
return -EINVAL;
if (wiphy->addresses)
memcpy(wiphy->perm_addr, wiphy->addresses[0].addr, ETH_ALEN);
/* sanity check ifmodes */
WARN_ON(!ifmodes);
ifmodes &= ((1 << __NL80211_IFTYPE_AFTER_LAST) - 1) & ~1;
@ -477,7 +484,7 @@ int wiphy_register(struct wiphy *wiphy)
/* set up regulatory info */
wiphy_update_regulatory(wiphy, NL80211_REGDOM_SET_BY_CORE);
list_add(&rdev->list, &cfg80211_rdev_list);
list_add_rcu(&rdev->list, &cfg80211_rdev_list);
cfg80211_rdev_list_generation++;
mutex_unlock(&cfg80211_mutex);
@ -554,7 +561,8 @@ void wiphy_unregister(struct wiphy *wiphy)
* it impossible to find from userspace.
*/
debugfs_remove_recursive(rdev->wiphy.debugfsdir);
list_del(&rdev->list);
list_del_rcu(&rdev->list);
synchronize_rcu();
/*
* Try to grab rdev->mtx. If a command is still in progress,
@ -670,7 +678,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
INIT_LIST_HEAD(&wdev->event_list);
spin_lock_init(&wdev->event_lock);
mutex_lock(&rdev->devlist_mtx);
list_add(&wdev->list, &rdev->netdev_list);
list_add_rcu(&wdev->list, &rdev->netdev_list);
rdev->devlist_generation++;
/* can only change netns with wiphy */
dev->features |= NETIF_F_NETNS_LOCAL;
@ -782,13 +790,21 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
*/
if (!list_empty(&wdev->list)) {
sysfs_remove_link(&dev->dev.kobj, "phy80211");
list_del_init(&wdev->list);
list_del_rcu(&wdev->list);
rdev->devlist_generation++;
#ifdef CONFIG_CFG80211_WEXT
kfree(wdev->wext.keys);
#endif
}
mutex_unlock(&rdev->devlist_mtx);
/*
* synchronise (so that we won't find this netdev
* from other code any more) and then clear the list
* head so that the above code can safely check for
* !list_empty() to avoid double-cleanup.
*/
synchronize_rcu();
INIT_LIST_HEAD(&wdev->list);
break;
case NETDEV_PRE_UP:
if (!(wdev->wiphy->interface_modes & BIT(wdev->iftype)))

View file

@ -1,7 +1,7 @@
/*
* Wireless configuration interface internals.
*
* Copyright 2006-2009 Johannes Berg <johannes@sipsolutions.net>
* Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
*/
#ifndef __NET_WIRELESS_CORE_H
#define __NET_WIRELESS_CORE_H
@ -48,6 +48,7 @@ struct cfg80211_registered_device {
/* associate netdev list */
struct mutex devlist_mtx;
/* protected by devlist_mtx or RCU */
struct list_head netdev_list;
int devlist_generation;
int opencount; /* also protected by devlist_mtx */

View file

@ -237,7 +237,6 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
return -1;
pos = skb->data + hdr_len + CCMP_HDR_LEN;
mic = skb_put(skb, CCMP_MIC_LEN);
hdr = (struct ieee80211_hdr *)skb->data;
ccmp_init_blocks(key->tfm, hdr, key->tx_pn, data_len, b0, b, s0);
@ -257,6 +256,7 @@ static int lib80211_ccmp_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
pos += len;
}
mic = skb_put(skb, CCMP_MIC_LEN);
for (i = 0; i < CCMP_MIC_LEN; i++)
mic[i] = b[i] ^ s0[i];

View file

@ -36,6 +36,8 @@ MODULE_AUTHOR("Jouni Malinen");
MODULE_DESCRIPTION("lib80211 crypt: TKIP");
MODULE_LICENSE("GPL");
#define TKIP_HDR_LEN 8
struct lib80211_tkip_data {
#define TKIP_KEY_LEN 32
u8 key[TKIP_KEY_LEN];
@ -314,13 +316,12 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
u8 * rc4key, int keylen, void *priv)
{
struct lib80211_tkip_data *tkey = priv;
int len;
u8 *pos;
struct ieee80211_hdr *hdr;
hdr = (struct ieee80211_hdr *)skb->data;
if (skb_headroom(skb) < 8 || skb->len < hdr_len)
if (skb_headroom(skb) < TKIP_HDR_LEN || skb->len < hdr_len)
return -1;
if (rc4key == NULL || keylen < 16)
@ -333,9 +334,8 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
}
tkip_mixing_phase2(rc4key, tkey->key, tkey->tx_ttak, tkey->tx_iv16);
len = skb->len - hdr_len;
pos = skb_push(skb, 8);
memmove(pos, pos + 8, hdr_len);
pos = skb_push(skb, TKIP_HDR_LEN);
memmove(pos, pos + TKIP_HDR_LEN, hdr_len);
pos += hdr_len;
*pos++ = *rc4key;
@ -353,7 +353,7 @@ static int lib80211_tkip_hdr(struct sk_buff *skb, int hdr_len,
tkey->tx_iv32++;
}
return 8;
return TKIP_HDR_LEN;
}
static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
@ -384,9 +384,8 @@ static int lib80211_tkip_encrypt(struct sk_buff *skb, int hdr_len, void *priv)
if ((lib80211_tkip_hdr(skb, hdr_len, rc4key, 16, priv)) < 0)
return -1;
icv = skb_put(skb, 4);
crc = ~crc32_le(~0, pos, len);
icv = skb_put(skb, 4);
icv[0] = crc;
icv[1] = crc >> 8;
icv[2] = crc >> 16;
@ -434,7 +433,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
return -1;
}
if (skb->len < hdr_len + 8 + 4)
if (skb->len < hdr_len + TKIP_HDR_LEN + 4)
return -1;
pos = skb->data + hdr_len;
@ -462,7 +461,7 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
}
iv16 = (pos[0] << 8) | pos[2];
iv32 = pos[4] | (pos[5] << 8) | (pos[6] << 16) | (pos[7] << 24);
pos += 8;
pos += TKIP_HDR_LEN;
if (tkip_replay_check(iv32, iv16, tkey->rx_iv32, tkey->rx_iv16)) {
#ifdef CONFIG_LIB80211_DEBUG
@ -523,8 +522,8 @@ static int lib80211_tkip_decrypt(struct sk_buff *skb, int hdr_len, void *priv)
tkey->rx_iv16_new = iv16;
/* Remove IV and ICV */
memmove(skb->data + 8, skb->data, hdr_len);
skb_pull(skb, 8);
memmove(skb->data + TKIP_HDR_LEN, skb->data, hdr_len);
skb_pull(skb, TKIP_HDR_LEN);
skb_trim(skb, skb->len - 4);
return keyidx;

View file

@ -3571,6 +3571,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
{
struct cfg80211_registered_device *rdev;
struct net_device *dev;
struct wireless_dev *wdev;
struct cfg80211_crypto_settings crypto;
struct ieee80211_channel *chan, *fixedchan;
const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
@ -3616,7 +3617,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
}
mutex_lock(&rdev->devlist_mtx);
fixedchan = rdev_fixed_channel(rdev, NULL);
wdev = dev->ieee80211_ptr;
fixedchan = rdev_fixed_channel(rdev, wdev);
if (fixedchan && chan != fixedchan) {
err = -EBUSY;
mutex_unlock(&rdev->devlist_mtx);

View file

@ -134,6 +134,7 @@ static const struct ieee80211_regdomain *cfg80211_world_regdom =
&world_regdom;
static char *ieee80211_regdom = "00";
static char user_alpha2[2];
module_param(ieee80211_regdom, charp, 0444);
MODULE_PARM_DESC(ieee80211_regdom, "IEEE 802.11 regulatory domain code");
@ -252,6 +253,27 @@ static bool regdom_changes(const char *alpha2)
return true;
}
/*
* The NL80211_REGDOM_SET_BY_USER regdom alpha2 is cached, this lets
* you know if a valid regulatory hint with NL80211_REGDOM_SET_BY_USER
* has ever been issued.
*/
static bool is_user_regdom_saved(void)
{
if (user_alpha2[0] == '9' && user_alpha2[1] == '7')
return false;
/* This would indicate a mistake on the design */
if (WARN((!is_world_regdom(user_alpha2) &&
!is_an_alpha2(user_alpha2)),
"Unexpected user alpha2: %c%c\n",
user_alpha2[0],
user_alpha2[1]))
return false;
return true;
}
/**
* country_ie_integrity_changes - tells us if the country IE has changed
* @checksum: checksum of country IE of fields we are interested in
@ -1646,7 +1668,7 @@ static int ignore_request(struct wiphy *wiphy,
switch (pending_request->initiator) {
case NL80211_REGDOM_SET_BY_CORE:
return -EINVAL;
return 0;
case NL80211_REGDOM_SET_BY_COUNTRY_IE:
last_wiphy = wiphy_idx_to_wiphy(last_request->wiphy_idx);
@ -1785,6 +1807,11 @@ new_request:
pending_request = NULL;
if (last_request->initiator == NL80211_REGDOM_SET_BY_USER) {
user_alpha2[0] = last_request->alpha2[0];
user_alpha2[1] = last_request->alpha2[1];
}
/* When r == REG_INTERSECT we do need to call CRDA */
if (r < 0) {
/*
@ -1904,12 +1931,16 @@ static void queue_regulatory_request(struct regulatory_request *request)
schedule_work(&reg_work);
}
/* Core regulatory hint -- happens once during cfg80211_init() */
/*
* Core regulatory hint -- happens during cfg80211_init()
* and when we restore regulatory settings.
*/
static int regulatory_hint_core(const char *alpha2)
{
struct regulatory_request *request;
BUG_ON(last_request);
kfree(last_request);
last_request = NULL;
request = kzalloc(sizeof(struct regulatory_request),
GFP_KERNEL);
@ -1920,14 +1951,12 @@ static int regulatory_hint_core(const char *alpha2)
request->alpha2[1] = alpha2[1];
request->initiator = NL80211_REGDOM_SET_BY_CORE;
queue_regulatory_request(request);
/*
* This ensures last_request is populated once modules
* come swinging in and calling regulatory hints and
* wiphy_apply_custom_regulatory().
*/
flush_scheduled_work();
reg_process_hint(request);
return 0;
}
@ -2109,6 +2138,123 @@ out:
mutex_unlock(&reg_mutex);
}
static void restore_alpha2(char *alpha2, bool reset_user)
{
/* indicates there is no alpha2 to consider for restoration */
alpha2[0] = '9';
alpha2[1] = '7';
/* The user setting has precedence over the module parameter */
if (is_user_regdom_saved()) {
/* Unless we're asked to ignore it and reset it */
if (reset_user) {
REG_DBG_PRINT("cfg80211: Restoring regulatory settings "
"including user preference\n");
user_alpha2[0] = '9';
user_alpha2[1] = '7';
/*
* If we're ignoring user settings, we still need to
* check the module parameter to ensure we put things
* back as they were for a full restore.
*/
if (!is_world_regdom(ieee80211_regdom)) {
REG_DBG_PRINT("cfg80211: Keeping preference on "
"module parameter ieee80211_regdom: %c%c\n",
ieee80211_regdom[0],
ieee80211_regdom[1]);
alpha2[0] = ieee80211_regdom[0];
alpha2[1] = ieee80211_regdom[1];
}
} else {
REG_DBG_PRINT("cfg80211: Restoring regulatory settings "
"while preserving user preference for: %c%c\n",
user_alpha2[0],
user_alpha2[1]);
alpha2[0] = user_alpha2[0];
alpha2[1] = user_alpha2[1];
}
} else if (!is_world_regdom(ieee80211_regdom)) {
REG_DBG_PRINT("cfg80211: Keeping preference on "
"module parameter ieee80211_regdom: %c%c\n",
ieee80211_regdom[0],
ieee80211_regdom[1]);
alpha2[0] = ieee80211_regdom[0];
alpha2[1] = ieee80211_regdom[1];
} else
REG_DBG_PRINT("cfg80211: Restoring regulatory settings\n");
}
/*
* Restoring regulatory settings involves ingoring any
* possibly stale country IE information and user regulatory
* settings if so desired, this includes any beacon hints
* learned as we could have traveled outside to another country
* after disconnection. To restore regulatory settings we do
* exactly what we did at bootup:
*
* - send a core regulatory hint
* - send a user regulatory hint if applicable
*
* Device drivers that send a regulatory hint for a specific country
* keep their own regulatory domain on wiphy->regd so that does does
* not need to be remembered.
*/
static void restore_regulatory_settings(bool reset_user)
{
char alpha2[2];
struct reg_beacon *reg_beacon, *btmp;
mutex_lock(&cfg80211_mutex);
mutex_lock(&reg_mutex);
reset_regdomains();
restore_alpha2(alpha2, reset_user);
/* Clear beacon hints */
spin_lock_bh(&reg_pending_beacons_lock);
if (!list_empty(&reg_pending_beacons)) {
list_for_each_entry_safe(reg_beacon, btmp,
&reg_pending_beacons, list) {
list_del(&reg_beacon->list);
kfree(reg_beacon);
}
}
spin_unlock_bh(&reg_pending_beacons_lock);
if (!list_empty(&reg_beacon_list)) {
list_for_each_entry_safe(reg_beacon, btmp,
&reg_beacon_list, list) {
list_del(&reg_beacon->list);
kfree(reg_beacon);
}
}
/* First restore to the basic regulatory settings */
cfg80211_regdomain = cfg80211_world_regdom;
mutex_unlock(&reg_mutex);
mutex_unlock(&cfg80211_mutex);
regulatory_hint_core(cfg80211_regdomain->alpha2);
/*
* This restores the ieee80211_regdom module parameter
* preference or the last user requested regulatory
* settings, user regulatory settings takes precedence.
*/
if (is_an_alpha2(alpha2))
regulatory_hint_user(user_alpha2);
}
void regulatory_hint_disconnect(void)
{
REG_DBG_PRINT("cfg80211: All devices are disconnected, going to "
"restore regulatory settings\n");
restore_regulatory_settings(false);
}
static bool freq_is_chan_12_13_14(u16 freq)
{
if (freq == ieee80211_channel_to_frequency(12) ||
@ -2498,6 +2644,9 @@ int regulatory_init(void)
cfg80211_regdomain = cfg80211_world_regdom;
user_alpha2[0] = '9';
user_alpha2[1] = '7';
/* We always try to get an update for the static regdomain */
err = regulatory_hint_core(cfg80211_regdomain->alpha2);
if (err) {

View file

@ -63,4 +63,22 @@ void regulatory_hint_11d(struct wiphy *wiphy,
u8 *country_ie,
u8 country_ie_len);
/**
* regulatory_hint_disconnect - informs all devices have been disconneted
*
* Regulotory rules can be enhanced further upon scanning and upon
* connection to an AP. These rules become stale if we disconnect
* and go to another country, whether or not we suspend and resume.
* If we suspend, go to another country and resume we'll automatically
* get disconnected shortly after resuming and things will be reset as well.
* This routine is a helper to restore regulatory settings to how they were
* prior to our first connect attempt. This includes ignoring country IE and
* beacon regulatory hints. The ieee80211_regdom module parameter will always
* be respected but if a user had set the regulatory domain that will take
* precedence.
*
* Must be called from process context.
*/
void regulatory_hint_disconnect(void);
#endif /* __NET_WIRELESS_REG_H */

View file

@ -143,9 +143,9 @@ void cfg80211_bss_expire(struct cfg80211_registered_device *dev)
dev->bss_generation++;
}
static u8 *find_ie(u8 num, u8 *ies, int len)
const u8 *cfg80211_find_ie(u8 eid, const u8 *ies, int len)
{
while (len > 2 && ies[0] != num) {
while (len > 2 && ies[0] != eid) {
len -= ies[1] + 2;
ies += ies[1] + 2;
}
@ -155,11 +155,12 @@ static u8 *find_ie(u8 num, u8 *ies, int len)
return NULL;
return ies;
}
EXPORT_SYMBOL(cfg80211_find_ie);
static int cmp_ies(u8 num, u8 *ies1, size_t len1, u8 *ies2, size_t len2)
{
const u8 *ie1 = find_ie(num, ies1, len1);
const u8 *ie2 = find_ie(num, ies2, len2);
const u8 *ie1 = cfg80211_find_ie(num, ies1, len1);
const u8 *ie2 = cfg80211_find_ie(num, ies2, len2);
int r;
if (!ie1 && !ie2)
@ -185,9 +186,9 @@ static bool is_bss(struct cfg80211_bss *a,
if (!ssid)
return true;
ssidie = find_ie(WLAN_EID_SSID,
a->information_elements,
a->len_information_elements);
ssidie = cfg80211_find_ie(WLAN_EID_SSID,
a->information_elements,
a->len_information_elements);
if (!ssidie)
return false;
if (ssidie[1] != ssid_len)
@ -204,9 +205,9 @@ static bool is_mesh(struct cfg80211_bss *a,
if (!is_zero_ether_addr(a->bssid))
return false;
ie = find_ie(WLAN_EID_MESH_ID,
a->information_elements,
a->len_information_elements);
ie = cfg80211_find_ie(WLAN_EID_MESH_ID,
a->information_elements,
a->len_information_elements);
if (!ie)
return false;
if (ie[1] != meshidlen)
@ -214,9 +215,9 @@ static bool is_mesh(struct cfg80211_bss *a,
if (memcmp(ie + 2, meshid, meshidlen))
return false;
ie = find_ie(WLAN_EID_MESH_CONFIG,
a->information_elements,
a->len_information_elements);
ie = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
a->information_elements,
a->len_information_elements);
if (!ie)
return false;
if (ie[1] != sizeof(struct ieee80211_meshconf_ie))
@ -395,11 +396,12 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev,
if (is_zero_ether_addr(res->pub.bssid)) {
/* must be mesh, verify */
meshid = find_ie(WLAN_EID_MESH_ID, res->pub.information_elements,
res->pub.len_information_elements);
meshcfg = find_ie(WLAN_EID_MESH_CONFIG,
res->pub.information_elements,
res->pub.len_information_elements);
meshid = cfg80211_find_ie(WLAN_EID_MESH_ID,
res->pub.information_elements,
res->pub.len_information_elements);
meshcfg = cfg80211_find_ie(WLAN_EID_MESH_CONFIG,
res->pub.information_elements,
res->pub.len_information_elements);
if (!meshid || !meshcfg ||
meshcfg[1] != sizeof(struct ieee80211_meshconf_ie)) {
/* bogus mesh */

View file

@ -34,6 +34,44 @@ struct cfg80211_conn {
bool auto_auth, prev_bssid_valid;
};
bool cfg80211_is_all_idle(void)
{
struct cfg80211_registered_device *rdev;
struct wireless_dev *wdev;
bool is_all_idle = true;
mutex_lock(&cfg80211_mutex);
/*
* All devices must be idle as otherwise if you are actively
* scanning some new beacon hints could be learned and would
* count as new regulatory hints.
*/
list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
cfg80211_lock_rdev(rdev);
list_for_each_entry(wdev, &rdev->netdev_list, list) {
wdev_lock(wdev);
if (wdev->sme_state != CFG80211_SME_IDLE)
is_all_idle = false;
wdev_unlock(wdev);
}
cfg80211_unlock_rdev(rdev);
}
mutex_unlock(&cfg80211_mutex);
return is_all_idle;
}
static void disconnect_work(struct work_struct *work)
{
if (!cfg80211_is_all_idle())
return;
regulatory_hint_disconnect();
}
static DECLARE_WORK(cfg80211_disconnect_work, disconnect_work);
static int cfg80211_conn_scan(struct wireless_dev *wdev)
{
@ -658,6 +696,8 @@ void __cfg80211_disconnected(struct net_device *dev, const u8 *ie,
wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL);
wdev->wext.connect.ssid_len = 0;
#endif
schedule_work(&cfg80211_disconnect_work);
}
void cfg80211_disconnected(struct net_device *dev, u16 reason,

View file

@ -33,10 +33,30 @@ static ssize_t name ## _show(struct device *dev, \
SHOW_FMT(index, "%d", wiphy_idx);
SHOW_FMT(macaddress, "%pM", wiphy.perm_addr);
SHOW_FMT(address_mask, "%pM", wiphy.addr_mask);
static ssize_t addresses_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct wiphy *wiphy = &dev_to_rdev(dev)->wiphy;
char *start = buf;
int i;
if (!wiphy->addresses)
return sprintf(buf, "%pM\n", wiphy->perm_addr);
for (i = 0; i < wiphy->n_addresses; i++)
buf += sprintf(buf, "%pM\n", &wiphy->addresses[i].addr);
return buf - start;
}
static struct device_attribute ieee80211_dev_attrs[] = {
__ATTR_RO(index),
__ATTR_RO(macaddress),
__ATTR_RO(address_mask),
__ATTR_RO(addresses),
{}
};

View file

@ -227,8 +227,11 @@ unsigned int ieee80211_hdrlen(__le16 fc)
if (ieee80211_is_data(fc)) {
if (ieee80211_has_a4(fc))
hdrlen = 30;
if (ieee80211_is_data_qos(fc))
if (ieee80211_is_data_qos(fc)) {
hdrlen += IEEE80211_QOS_CTL_LEN;
if (ieee80211_has_order(fc))
hdrlen += IEEE80211_HT_CTL_LEN;
}
goto out;
}

View file

@ -1214,7 +1214,7 @@ int cfg80211_wext_siwrate(struct net_device *dev,
memset(&mask, 0, sizeof(mask));
fixed = 0;
maxrate = 0;
maxrate = (u32)-1;
if (rate->value < 0) {
/* nothing */