diff options
Diffstat (limited to 'net/wireless')
-rw-r--r-- | net/wireless/Makefile | 2 | ||||
-rw-r--r-- | net/wireless/core.c | 33 | ||||
-rw-r--r-- | net/wireless/core.h | 18 | ||||
-rw-r--r-- | net/wireless/lib80211.c | 8 | ||||
-rw-r--r-- | net/wireless/lib80211_crypt_tkip.c | 16 | ||||
-rw-r--r-- | net/wireless/mesh.c | 142 | ||||
-rw-r--r-- | net/wireless/mlme.c | 43 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 612 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 10 | ||||
-rw-r--r-- | net/wireless/reg.c | 262 | ||||
-rw-r--r-- | net/wireless/scan.c | 11 | ||||
-rw-r--r-- | net/wireless/util.c | 15 | ||||
-rw-r--r-- | net/wireless/wext-compat.c | 8 | ||||
-rw-r--r-- | net/wireless/wext-core.c | 10 |
14 files changed, 1005 insertions, 185 deletions
diff --git a/net/wireless/Makefile b/net/wireless/Makefile index e77e508126f..55a28ab21db 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile @@ -10,7 +10,7 @@ obj-$(CONFIG_WEXT_SPY) += wext-spy.o obj-$(CONFIG_WEXT_PRIV) += wext-priv.o cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o -cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o +cfg80211-y += mlme.o ibss.o sme.o chan.o ethtool.o mesh.o cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o cfg80211-$(CONFIG_CFG80211_WEXT) += wext-compat.o wext-sme.o cfg80211-$(CONFIG_CFG80211_INTERNAL_REGDB) += regdb.o diff --git a/net/wireless/core.c b/net/wireless/core.c index 9c21ebf9780..e9a5f8ca4c2 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -4,6 +4,8 @@ * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/if.h> #include <linux/module.h> #include <linux/err.h> @@ -216,8 +218,7 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, rdev->wiphy.debugfsdir, rdev->wiphy.debugfsdir->d_parent, newname)) - printk(KERN_ERR "cfg80211: failed to rename debugfs dir to %s!\n", - newname); + pr_err("failed to rename debugfs dir to %s!\n", newname); nl80211_notify_dev_rename(rdev); @@ -331,6 +332,7 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) WARN_ON(ops->add_virtual_intf && !ops->del_virtual_intf); WARN_ON(ops->add_station && !ops->del_station); WARN_ON(ops->add_mpath && !ops->del_mpath); + WARN_ON(ops->join_mesh && !ops->leave_mesh); alloc_size = sizeof(*rdev) + sizeof_priv; @@ -699,8 +701,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { - printk(KERN_ERR "wireless: failed to add phy80211 " - "symlink to netdev!\n"); + pr_err("failed to add phy80211 symlink to netdev!\n"); } wdev->netdev = dev; wdev->sme_state = CFG80211_SME_IDLE; @@ -752,6 +753,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, cfg80211_mlme_down(rdev, dev); wdev_unlock(wdev); break; + case NL80211_IFTYPE_MESH_POINT: + cfg80211_leave_mesh(rdev, dev); + break; default: break; } @@ -775,20 +779,37 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, } cfg80211_lock_rdev(rdev); mutex_lock(&rdev->devlist_mtx); -#ifdef CONFIG_CFG80211_WEXT wdev_lock(wdev); switch (wdev->iftype) { +#ifdef CONFIG_CFG80211_WEXT case NL80211_IFTYPE_ADHOC: cfg80211_ibss_wext_join(rdev, wdev); break; case NL80211_IFTYPE_STATION: cfg80211_mgd_wext_connect(rdev, wdev); break; +#endif +#ifdef CONFIG_MAC80211_MESH + case NL80211_IFTYPE_MESH_POINT: + { + /* backward compat code... */ + struct mesh_setup setup; + memcpy(&setup, &default_mesh_setup, + sizeof(setup)); + /* back compat only needed for mesh_id */ + setup.mesh_id = wdev->ssid; + setup.mesh_id_len = wdev->mesh_id_up_len; + if (wdev->mesh_id_up_len) + __cfg80211_join_mesh(rdev, dev, + &setup, + &default_mesh_config); + break; + } +#endif default: break; } wdev_unlock(wdev); -#endif rdev->opencount++; mutex_unlock(&rdev->devlist_mtx); cfg80211_unlock_rdev(rdev); diff --git a/net/wireless/core.h b/net/wireless/core.h index 6583cca0e2e..26a0a084e16 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -285,6 +285,20 @@ void __cfg80211_ibss_joined(struct net_device *dev, const u8 *bssid); int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev, struct wireless_dev *wdev); +/* mesh */ +extern const struct mesh_config default_mesh_config; +extern const struct mesh_setup default_mesh_setup; +int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_setup *setup, + const struct mesh_config *conf); +int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_setup *setup, + const struct mesh_config *conf); +int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev); + /* MLME */ int __cfg80211_mlme_auth(struct cfg80211_registered_device *rdev, struct net_device *dev, @@ -341,9 +355,9 @@ void cfg80211_mlme_unregister_socket(struct wireless_dev *wdev, u32 nlpid); void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev); int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct ieee80211_channel *chan, + struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, - bool channel_type_valid, + bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, u64 *cookie); /* SME */ diff --git a/net/wireless/lib80211.c b/net/wireless/lib80211.c index 97d411f7450..3268fac5ab2 100644 --- a/net/wireless/lib80211.c +++ b/net/wireless/lib80211.c @@ -13,6 +13,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/module.h> #include <linux/ctype.h> #include <linux/ieee80211.h> @@ -224,8 +226,8 @@ int lib80211_unregister_crypto_ops(struct lib80211_crypto_ops *ops) return -EINVAL; found: - printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm " - "'%s'\n", ops->name); + printk(KERN_DEBUG "lib80211_crypt: unregistered algorithm '%s'\n", + ops->name); list_del(&alg->list); spin_unlock_irqrestore(&lib80211_crypto_lock, flags); kfree(alg); @@ -270,7 +272,7 @@ static struct lib80211_crypto_ops lib80211_crypt_null = { static int __init lib80211_init(void) { - printk(KERN_INFO DRV_NAME ": " DRV_DESCRIPTION "\n"); + pr_info(DRV_DESCRIPTION "\n"); return lib80211_register_crypto_ops(&lib80211_crypt_null); } diff --git a/net/wireless/lib80211_crypt_tkip.c b/net/wireless/lib80211_crypt_tkip.c index 0fe40510e2c..7ea4f2b0770 100644 --- a/net/wireless/lib80211_crypt_tkip.c +++ b/net/wireless/lib80211_crypt_tkip.c @@ -10,6 +10,8 @@ * more details. */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/err.h> #include <linux/module.h> #include <linux/init.h> @@ -99,8 +101,7 @@ static void *lib80211_tkip_init(int key_idx) priv->tx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_arc4)) { - printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " - "crypto API arc4\n"); + printk(KERN_DEBUG pr_fmt("could not allocate crypto API arc4\n")); priv->tx_tfm_arc4 = NULL; goto fail; } @@ -108,8 +109,7 @@ static void *lib80211_tkip_init(int key_idx) priv->tx_tfm_michael = crypto_alloc_hash("michael_mic", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->tx_tfm_michael)) { - printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " - "crypto API michael_mic\n"); + printk(KERN_DEBUG pr_fmt("could not allocate crypto API michael_mic\n")); priv->tx_tfm_michael = NULL; goto fail; } @@ -117,8 +117,7 @@ static void *lib80211_tkip_init(int key_idx) priv->rx_tfm_arc4 = crypto_alloc_blkcipher("ecb(arc4)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_arc4)) { - printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " - "crypto API arc4\n"); + printk(KERN_DEBUG pr_fmt("could not allocate crypto API arc4\n")); priv->rx_tfm_arc4 = NULL; goto fail; } @@ -126,8 +125,7 @@ static void *lib80211_tkip_init(int key_idx) priv->rx_tfm_michael = crypto_alloc_hash("michael_mic", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(priv->rx_tfm_michael)) { - printk(KERN_DEBUG "lib80211_crypt_tkip: could not allocate " - "crypto API michael_mic\n"); + printk(KERN_DEBUG pr_fmt("could not allocate crypto API michael_mic\n")); priv->rx_tfm_michael = NULL; goto fail; } @@ -536,7 +534,7 @@ static int michael_mic(struct crypto_hash *tfm_michael, u8 * key, u8 * hdr, struct scatterlist sg[2]; if (tfm_michael == NULL) { - printk(KERN_WARNING "michael_mic: tfm_michael == NULL\n"); + pr_warn("%s(): tfm_michael == NULL\n", __func__); return -1; } sg_init_table(sg, 2); diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c new file mode 100644 index 00000000000..73e39c171ff --- /dev/null +++ b/net/wireless/mesh.c @@ -0,0 +1,142 @@ +#include <linux/ieee80211.h> +#include <net/cfg80211.h> +#include "core.h" + +/* Default values, timeouts in ms */ +#define MESH_TTL 31 +#define MESH_DEFAULT_ELEMENT_TTL 31 +#define MESH_MAX_RETR 3 +#define MESH_RET_T 100 +#define MESH_CONF_T 100 +#define MESH_HOLD_T 100 + +#define MESH_PATH_TIMEOUT 5000 + +/* + * Minimum interval between two consecutive PREQs originated by the same + * interface + */ +#define MESH_PREQ_MIN_INT 10 +#define MESH_DIAM_TRAVERSAL_TIME 50 + +/* + * A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds + * before timing out. This way it will remain ACTIVE and no data frames + * will be unnecessarily held in the pending queue. + */ +#define MESH_PATH_REFRESH_TIME 1000 +#define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME) + +/* Default maximum number of established plinks per interface */ +#define MESH_MAX_ESTAB_PLINKS 32 + +#define MESH_MAX_PREQ_RETRIES 4 + + +const struct mesh_config default_mesh_config = { + .dot11MeshRetryTimeout = MESH_RET_T, + .dot11MeshConfirmTimeout = MESH_CONF_T, + .dot11MeshHoldingTimeout = MESH_HOLD_T, + .dot11MeshMaxRetries = MESH_MAX_RETR, + .dot11MeshTTL = MESH_TTL, + .element_ttl = MESH_DEFAULT_ELEMENT_TTL, + .auto_open_plinks = true, + .dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS, + .dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT, + .dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT, + .dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME, + .dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES, + .path_refresh_time = MESH_PATH_REFRESH_TIME, + .min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT, +}; + +const struct mesh_setup default_mesh_setup = { + .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP, + .path_metric = IEEE80211_PATH_METRIC_AIRTIME, + .vendor_ie = NULL, + .vendor_ie_len = 0, +}; + +int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_setup *setup, + const struct mesh_config *conf) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN); + + ASSERT_WDEV_LOCK(wdev); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + if (wdev->mesh_id_len) + return -EALREADY; + + if (!setup->mesh_id_len) + return -EINVAL; + + if (!rdev->ops->join_mesh) + return -EOPNOTSUPP; + + err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup); + if (!err) { + memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len); + wdev->mesh_id_len = setup->mesh_id_len; + } + + return err; +} + +int cfg80211_join_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev, + const struct mesh_setup *setup, + const struct mesh_config *conf) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_join_mesh(rdev, dev, setup, conf); + wdev_unlock(wdev); + + return err; +} + +static int __cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + ASSERT_WDEV_LOCK(wdev); + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + if (!rdev->ops->leave_mesh) + return -EOPNOTSUPP; + + if (!wdev->mesh_id_len) + return -ENOTCONN; + + err = rdev->ops->leave_mesh(&rdev->wiphy, dev); + if (!err) + wdev->mesh_id_len = 0; + return err; +} + +int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev, + struct net_device *dev) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + int err; + + wdev_lock(wdev); + err = __cfg80211_leave_mesh(rdev, dev); + wdev_unlock(wdev); + + return err; +} diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c index 26838d903b9..aa5df8865ff 100644 --- a/net/wireless/mlme.c +++ b/net/wireless/mlme.c @@ -263,6 +263,28 @@ void cfg80211_send_disassoc(struct net_device *dev, const u8 *buf, size_t len) } EXPORT_SYMBOL(cfg80211_send_disassoc); +void cfg80211_send_unprot_deauth(struct net_device *dev, const u8 *buf, + size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_unprot_deauth(rdev, dev, buf, len, GFP_ATOMIC); +} +EXPORT_SYMBOL(cfg80211_send_unprot_deauth); + +void cfg80211_send_unprot_disassoc(struct net_device *dev, const u8 *buf, + size_t len) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + nl80211_send_unprot_disassoc(rdev, dev, buf, len, GFP_ATOMIC); +} +EXPORT_SYMBOL(cfg80211_send_unprot_disassoc); + static void __cfg80211_auth_remove(struct wireless_dev *wdev, const u8 *addr) { int i; @@ -864,9 +886,9 @@ void cfg80211_mlme_purge_registrations(struct wireless_dev *wdev) int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, struct net_device *dev, - struct ieee80211_channel *chan, + struct ieee80211_channel *chan, bool offchan, enum nl80211_channel_type channel_type, - bool channel_type_valid, + bool channel_type_valid, unsigned int wait, const u8 *buf, size_t len, u64 *cookie) { struct wireless_dev *wdev = dev->ieee80211_ptr; @@ -946,8 +968,9 @@ int cfg80211_mlme_mgmt_tx(struct cfg80211_registered_device *rdev, return -EINVAL; /* Transmit the Action frame as requested by user space */ - return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, channel_type, - channel_type_valid, buf, len, cookie); + return rdev->ops->mgmt_tx(&rdev->wiphy, dev, chan, offchan, + channel_type, channel_type_valid, + wait, buf, len, cookie); } bool cfg80211_rx_mgmt(struct net_device *dev, int freq, const u8 *buf, @@ -1028,3 +1051,15 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev, nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp); } EXPORT_SYMBOL(cfg80211_cqm_rssi_notify); + +void cfg80211_cqm_pktloss_notify(struct net_device *dev, + const u8 *peer, u32 num_packets, gfp_t gfp) +{ + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct wiphy *wiphy = wdev->wiphy; + struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); + + /* Indicate roaming trigger event to user space */ + nl80211_send_cqm_pktloss_notify(rdev, dev, peer, num_packets, gfp); +} +EXPORT_SYMBOL(cfg80211_cqm_pktloss_notify); diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 4e78e3f2679..9b62710891a 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -121,8 +121,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, .len = NL80211_MAX_SUPP_RATES }, + [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 }, - [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED }, + [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED }, [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, .len = NL80211_HT_CAPABILITY_LEN }, @@ -163,10 +164,14 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, - [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 }, [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 }, [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 }, + [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 }, + [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 }, + [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 }, + [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG }, + [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, }; /* policy for the key attributes */ @@ -178,6 +183,14 @@ static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = { [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG }, [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG }, [NL80211_KEY_TYPE] = { .type = NLA_U32 }, + [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED }, +}; + +/* policy for the key default flags */ +static const struct nla_policy +nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = { + [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG }, + [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG }, }; /* ifidx get helper */ @@ -310,6 +323,7 @@ struct key_parse { int idx; int type; bool def, defmgmt; + bool def_uni, def_multi; }; static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) @@ -323,6 +337,13 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) k->def = !!tb[NL80211_KEY_DEFAULT]; k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT]; + if (k->def) { + k->def_uni = true; + k->def_multi = true; + } + if (k->defmgmt) + k->def_multi = true; + if (tb[NL80211_KEY_IDX]) k->idx = nla_get_u8(tb[NL80211_KEY_IDX]); @@ -345,6 +366,19 @@ static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k) return -EINVAL; } + if (tb[NL80211_KEY_DEFAULT_TYPES]) { + struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; + int err = nla_parse_nested(kdt, + NUM_NL80211_KEY_DEFAULT_TYPES - 1, + tb[NL80211_KEY_DEFAULT_TYPES], + nl80211_key_default_policy); + if (err) + return err; + + k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; + k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; + } + return 0; } @@ -369,12 +403,32 @@ static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k) k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT]; k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT]; + if (k->def) { + k->def_uni = true; + k->def_multi = true; + } + if (k->defmgmt) + k->def_multi = true; + if (info->attrs[NL80211_ATTR_KEY_TYPE]) { k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]); if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES) return -EINVAL; } + if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) { + struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES]; + int err = nla_parse_nested( + kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1, + info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES], + nl80211_key_default_policy); + if (err) + return err; + + k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST]; + k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST]; + } + return 0; } @@ -397,6 +451,11 @@ static int nl80211_parse_key(struct genl_info *info, struct key_parse *k) if (k->def && k->defmgmt) return -EINVAL; + if (k->defmgmt) { + if (k->def_uni || !k->def_multi) + return -EINVAL; + } + if (k->idx != -1) { if (k->defmgmt) { if (k->idx < 4 || k->idx > 5) @@ -446,6 +505,8 @@ nl80211_parse_connkeys(struct cfg80211_registered_device *rdev, goto error; def = 1; result->def = parse.idx; + if (!parse.def_uni || !parse.def_multi) + goto error; } else if (parse.defmgmt) goto error; err = cfg80211_validate_key_settings(rdev, &parse.p, @@ -526,7 +587,6 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, dev->wiphy.rts_threshold); NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS, dev->wiphy.coverage_class); - NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS, dev->wiphy.max_scan_ssids); NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN, @@ -545,6 +605,22 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX, + dev->wiphy.available_antennas_tx); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX, + dev->wiphy.available_antennas_rx); + + if ((dev->wiphy.available_antennas_tx || + dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) { + u32 tx_ant = 0, rx_ant = 0; + int res; + res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant); + if (!res) { + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant); + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant); + } + } + nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); if (!nl_modes) goto nla_put_failure; @@ -649,19 +725,21 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(add_beacon, NEW_BEACON); CMD(add_station, NEW_STATION); CMD(add_mpath, NEW_MPATH); - CMD(set_mesh_params, SET_MESH_PARAMS); + CMD(update_mesh_config, SET_MESH_CONFIG); CMD(change_bss, SET_BSS); CMD(auth, AUTHENTICATE); CMD(assoc, ASSOCIATE); CMD(deauth, DEAUTHENTICATE); CMD(disassoc, DISASSOCIATE); CMD(join_ibss, JOIN_IBSS); + CMD(join_mesh, JOIN_MESH); CMD(set_pmksa, SET_PMKSA); CMD(del_pmksa, DEL_PMKSA); CMD(flush_pmksa, FLUSH_PMKSA); CMD(remain_on_channel, REMAIN_ON_CHANNEL); CMD(set_bitrate_mask, SET_TX_BITRATE_MASK); CMD(mgmt_tx, FRAME); + CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL); if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { i++; NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); @@ -683,6 +761,14 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, nla_nest_end(msg, nl_cmds); + if (dev->ops->remain_on_channel) + NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION, + dev->wiphy.max_remain_on_channel_duration); + + /* for now at least assume all drivers have it */ + if (dev->ops->mgmt_tx) + NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK); + if (mgmt_stypes) { u16 stypes; struct nlattr *nl_ftypes, *nl_ifs; @@ -1024,6 +1110,35 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) goto bad_res; } + if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] && + info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) { + u32 tx_ant, rx_ant; + if ((!rdev->wiphy.available_antennas_tx && + !rdev->wiphy.available_antennas_rx) || + !rdev->ops->set_antenna) { + result = -EOPNOTSUPP; + goto bad_res; + } + + tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]); + rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]); + + /* reject antenna configurations which don't match the + * available antenna masks, except for the "all" mask */ + if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) || + (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) { + result = -EINVAL; + goto bad_res; + } + + tx_ant = tx_ant & rdev->wiphy.available_antennas_tx; + rx_ant = rx_ant & rdev->wiphy.available_antennas_rx; + + result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant); + if (result) + goto bad_res; + } + changed = 0; if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) { @@ -1291,11 +1406,21 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_MESH_ID]) { + struct wireless_dev *wdev = dev->ieee80211_ptr; + if (ntype != NL80211_IFTYPE_MESH_POINT) return -EINVAL; - params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); - params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); - change = true; + if (netif_running(dev)) + return -EBUSY; + + wdev_lock(wdev); + BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != + IEEE80211_MAX_MESH_ID_LEN); + wdev->mesh_id_up_len = + nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), + wdev->mesh_id_up_len); + wdev_unlock(wdev); } if (info->attrs[NL80211_ATTR_4ADDR]) { @@ -1335,6 +1460,7 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; struct vif_params params; + struct net_device *dev; int err; enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; u32 flags; @@ -1354,12 +1480,6 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) !(rdev->wiphy.interface_modes & (1 << type))) return -EOPNOTSUPP; - if (type == NL80211_IFTYPE_MESH_POINT && - info->attrs[NL80211_ATTR_MESH_ID]) { - params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); - params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); - } - if (info->attrs[NL80211_ATTR_4ADDR]) { params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]); err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type); @@ -1370,11 +1490,27 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, &flags); - err = rdev->ops->add_virtual_intf(&rdev->wiphy, + dev = rdev->ops->add_virtual_intf(&rdev->wiphy, nla_data(info->attrs[NL80211_ATTR_IFNAME]), type, err ? NULL : &flags, ¶ms); + if (IS_ERR(dev)) + return PTR_ERR(dev); - return err; + if (type == NL80211_IFTYPE_MESH_POINT && + info->attrs[NL80211_ATTR_MESH_ID]) { + struct wireless_dev *wdev = dev->ieee80211_ptr; + + wdev_lock(wdev); + BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != + IEEE80211_MAX_MESH_ID_LEN); + wdev->mesh_id_up_len = + nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]), + wdev->mesh_id_up_len); + wdev_unlock(wdev); + } + + return 0; } static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) @@ -1519,8 +1655,6 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) struct key_parse key; int err; struct net_device *dev = info->user_ptr[1]; - int (*func)(struct wiphy *wiphy, struct net_device *netdev, - u8 key_index); err = nl80211_parse_key(info, &key); if (err) @@ -1533,27 +1667,61 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) if (!key.def && !key.defmgmt) return -EINVAL; - if (key.def) - func = rdev->ops->set_default_key; - else - func = rdev->ops->set_default_mgmt_key; + wdev_lock(dev->ieee80211_ptr); - if (!func) - return -EOPNOTSUPP; + if (key.def) { + if (!rdev->ops->set_default_key) { + err = -EOPNOTSUPP; + goto out; + } - wdev_lock(dev->ieee80211_ptr); - err = nl80211_key_allowed(dev->ieee80211_ptr); - if (!err) - err = func(&rdev->wiphy, dev, key.idx); + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (err) + goto out; + + if (!(rdev->wiphy.flags & + WIPHY_FLAG_SUPPORTS_SEPARATE_DEFAULT_KEYS)) { + if (!key.def_uni || !key.def_multi) { + err = -EOPNOTSUPP; + goto out; + } + } + + err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx, + key.def_uni, key.def_multi); + + if (err) + goto out; #ifdef CONFIG_CFG80211_WEXT - if (!err) { - if (func == rdev->ops->set_default_key) - dev->ieee80211_ptr->wext.default_key = key.idx; - else - dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; - } + dev->ieee80211_ptr->wext.default_key = key.idx; +#endif + } else { + if (key.def_uni || !key.def_multi) { + err = -EINVAL; + goto out; + } + + if (!rdev->ops->set_default_mgmt_key) { + err = -EOPNOTSUPP; + goto out; + } + + err = nl80211_key_allowed(dev->ieee80211_ptr); + if (err) + goto out; + + err = rdev->ops->set_default_mgmt_key(&rdev->wiphy, + dev, key.idx); + if (err) + goto out; + +#ifdef CONFIG_CFG80211_WEXT + dev->ieee80211_ptr->wext.default_mgmt_key = key.idx; #endif + } + + out: wdev_unlock(dev->ieee80211_ptr); return err; @@ -1841,6 +2009,9 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, if (sinfo->filled & STATION_INFO_SIGNAL) NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, sinfo->signal); + if (sinfo->filled & STATION_INFO_SIGNAL_AVG) + NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG, + sinfo->signal_avg); if (sinfo->filled & STATION_INFO_TX_BITRATE) { txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE); if (!txrate) @@ -2404,6 +2575,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) params.use_short_preamble = -1; params.use_short_slot_time = -1; params.ap_isolate = -1; + params.ht_opmode = -1; if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) params.use_cts_prot = @@ -2422,6 +2594,9 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) } if (info->attrs[NL80211_ATTR_AP_ISOLATE]) params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); + if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE]) + params.ht_opmode = + nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]); if (!rdev->ops->change_bss) return -EOPNOTSUPP; @@ -2506,22 +2681,33 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) return r; } -static int nl80211_get_mesh_params(struct sk_buff *skb, - struct genl_info *info) +static int nl80211_get_mesh_config(struct sk_buff *skb, + struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct mesh_config cur_params; - int err; struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct mesh_config cur_params; + int err = 0; void *hdr; struct nlattr *pinfoattr; struct sk_buff *msg; - if (!rdev->ops->get_mesh_params) + if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) return -EOPNOTSUPP; - /* Get the mesh params */ - err = rdev->ops->get_mesh_params(&rdev->wiphy, dev, &cur_params); + if (!rdev->ops->get_mesh_config) + return -EOPNOTSUPP; + + wdev_lock(wdev); + /* If not connected, get default parameters */ + if (!wdev->mesh_id_len) + memcpy(&cur_params, &default_mesh_config, sizeof(cur_params)); + else + err = rdev->ops->get_mesh_config(&rdev->wiphy, dev, + &cur_params); + wdev_unlock(wdev); + if (err) return err; @@ -2530,10 +2716,10 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, if (!msg) return -ENOMEM; hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, - NL80211_CMD_GET_MESH_PARAMS); + NL80211_CMD_GET_MESH_CONFIG); if (!hdr) goto nla_put_failure; - pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS); + pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG); if (!pinfoattr) goto nla_put_failure; NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); @@ -2549,6 +2735,8 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, cur_params.dot11MeshMaxRetries); NLA_PUT_U8(msg, NL80211_MESHCONF_TTL, cur_params.dot11MeshTTL); + NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL, + cur_params.element_ttl); NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, cur_params.auto_open_plinks); NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, @@ -2575,14 +2763,6 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, return -ENOBUFS; } -#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ -do {\ - if (table[attr_num]) {\ - cfg.param = nla_fn(table[attr_num]); \ - mask |= (1 << (attr_num - 1)); \ - } \ -} while (0);\ - static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = { [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, @@ -2590,6 +2770,7 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 }, [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 }, [NL80211_MESHCONF_TTL] = { .type = NLA_U8 }, + [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 }, [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, @@ -2600,31 +2781,42 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 }, }; -static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) +static const struct nla_policy + nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = { + [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 }, + [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 }, + [NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE] = { .type = NLA_BINARY, + .len = IEEE80211_MAX_DATA_LEN }, +}; + +static int nl80211_parse_mesh_config(struct genl_info *info, + struct mesh_config *cfg, + u32 *mask_out) { - u32 mask; - struct cfg80211_registered_device *rdev = info->user_ptr[0]; - struct net_device *dev = info->user_ptr[1]; - struct mesh_config cfg; struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; - struct nlattr *parent_attr; + u32 mask = 0; - parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS]; - if (!parent_attr) +#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ +do {\ + if (table[attr_num]) {\ + cfg->param = nla_fn(table[attr_num]); \ + mask |= (1 << (attr_num - 1)); \ + } \ +} while (0);\ + + + if (!info->attrs[NL80211_ATTR_MESH_CONFIG]) return -EINVAL; if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, - parent_attr, nl80211_meshconf_params_policy)) + info->attrs[NL80211_ATTR_MESH_CONFIG], + nl80211_meshconf_params_policy)) return -EINVAL; - if (!rdev->ops->set_mesh_params) - return -EOPNOTSUPP; - /* This makes sure that there aren't more than 32 mesh config * parameters (otherwise our bitfield scheme would not work.) */ BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); /* Fill in the params struct */ - mask = 0; FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, @@ -2637,6 +2829,8 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, mask, NL80211_MESHCONF_TTL, nla_get_u8); + FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl, + mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, @@ -2661,12 +2855,82 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) dot11MeshHWMPRootMode, mask, NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8); + if (mask_out) + *mask_out = mask; - /* Apply changes */ - return rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask); -} + return 0; #undef FILL_IN_MESH_PARAM_IF_SET +} + +static int nl80211_parse_mesh_setup(struct genl_info *info, + struct mesh_setup *setup) +{ + struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1]; + + if (!info->attrs[NL80211_ATTR_MESH_SETUP]) + return -EINVAL; + if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX, + info->attrs[NL80211_ATTR_MESH_SETUP], + nl80211_mesh_setup_params_policy)) + return -EINVAL; + + if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL]) + setup->path_sel_proto = + (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ? + IEEE80211_PATH_PROTOCOL_VENDOR : + IEEE80211_PATH_PROTOCOL_HWMP; + + if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC]) + setup->path_metric = + (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ? + IEEE80211_PATH_METRIC_VENDOR : + IEEE80211_PATH_METRIC_AIRTIME; + + if (tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE]) { + struct nlattr *ieattr = + tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE]; + if (!is_valid_ie_attr(ieattr)) + return -EINVAL; + setup->vendor_ie = nla_data(ieattr); + setup->vendor_ie_len = nla_len(ieattr); + } + + return 0; +} + +static int nl80211_update_mesh_config(struct sk_buff *skb, + struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct wireless_dev *wdev = dev->ieee80211_ptr; + struct mesh_config cfg; + u32 mask; + int err; + + if (wdev->iftype != NL80211_IFTYPE_MESH_POINT) + return -EOPNOTSUPP; + + if (!rdev->ops->update_mesh_config) + return -EOPNOTSUPP; + + err = nl80211_parse_mesh_config(info, &cfg, &mask); + if (err) + return err; + + wdev_lock(wdev); + if (!wdev->mesh_id_len) + err = -ENOLINK; + + if (!err) + err = rdev->ops->update_mesh_config(&rdev->wiphy, dev, + mask, &cfg); + + wdev_unlock(wdev); + + return err; +} static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info) { @@ -3569,6 +3833,34 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) local_state_change); } +static bool +nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev, + int mcast_rate[IEEE80211_NUM_BANDS], + int rateval) +{ + struct wiphy *wiphy = &rdev->wiphy; + bool found = false; + int band, i; + + for (band = 0; band < IEEE80211_NUM_BANDS; band++) { + struct ieee80211_supported_band *sband; + + sband = wiphy->bands[band]; + if (!sband) + continue; + + for (i = 0; i < sband->n_bitrates; i++) { + if (sband->bitrates[i].bitrate == rateval) { + mcast_rate[band] = i + 1; + found = true; + break; + } + } + } + + return found; +} + static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -3653,6 +3945,11 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) } } + if (info->attrs[NL80211_ATTR_MCAST_RATE] && + !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate, + nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE]))) + return -EINVAL; + if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) { connkeys = nl80211_parse_connkeys(rdev, info->attrs[NL80211_ATTR_KEYS]); @@ -3987,7 +4284,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, * We should be on that channel for at least one jiffie, * and more than 5 seconds seems excessive. */ - if (!duration || !msecs_to_jiffies(duration) || duration > 5000) + if (!duration || !msecs_to_jiffies(duration) || + duration > rdev->wiphy.max_remain_on_channel_duration) return -EINVAL; if (!rdev->ops->remain_on_channel) @@ -4155,6 +4453,7 @@ static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; @@ -4180,6 +4479,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) void *hdr; u64 cookie; struct sk_buff *msg; + unsigned int wait = 0; + bool offchan; if (!info->attrs[NL80211_ATTR_FRAME] || !info->attrs[NL80211_ATTR_WIPHY_FREQ]) @@ -4193,9 +4494,16 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT && dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) return -EOPNOTSUPP; + if (info->attrs[NL80211_ATTR_DURATION]) { + if (!rdev->ops->mgmt_tx_cancel_wait) + return -EINVAL; + wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); + } + if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { channel_type = nla_get_u32( info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); @@ -4207,6 +4515,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) channel_type_valid = true; } + offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK]; + freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); chan = rdev_freq_to_chan(rdev, freq, channel_type); if (chan == NULL) @@ -4223,8 +4533,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) err = PTR_ERR(hdr); goto free_msg; } - err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, channel_type, - channel_type_valid, + err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type, + channel_type_valid, wait, nla_data(info->attrs[NL80211_ATTR_FRAME]), nla_len(info->attrs[NL80211_ATTR_FRAME]), &cookie); @@ -4243,6 +4553,31 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info) return err; } +static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + u64 cookie; + + if (!info->attrs[NL80211_ATTR_COOKIE]) + return -EINVAL; + + if (!rdev->ops->mgmt_tx_cancel_wait) + return -EOPNOTSUPP; + + if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && + dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) + return -EOPNOTSUPP; + + cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); + + return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie); +} + static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev = info->user_ptr[0]; @@ -4381,6 +4716,50 @@ out: return err; } +static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + struct mesh_config cfg; + struct mesh_setup setup; + int err; + + /* start with default */ + memcpy(&cfg, &default_mesh_config, sizeof(cfg)); + memcpy(&setup, &default_mesh_setup, sizeof(setup)); + + if (info->attrs[NL80211_ATTR_MESH_CONFIG]) { + /* and parse parameters if given */ + err = nl80211_parse_mesh_config(info, &cfg, NULL); + if (err) + return err; + } + + if (!info->attrs[NL80211_ATTR_MESH_ID] || + !nla_len(info->attrs[NL80211_ATTR_MESH_ID])) + return -EINVAL; + + setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); + setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); + + if (info->attrs[NL80211_ATTR_MESH_SETUP]) { + /* parse additional setup parameters if given */ + err = nl80211_parse_mesh_setup(info, &setup); + if (err) + return err; + } + + return cfg80211_join_mesh(rdev, dev, &setup, &cfg); +} + +static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev = info->user_ptr[0]; + struct net_device *dev = info->user_ptr[1]; + + return cfg80211_leave_mesh(rdev, dev); +} + #define NL80211_FLAG_NEED_WIPHY 0x01 #define NL80211_FLAG_NEED_NETDEV 0x02 #define NL80211_FLAG_NEED_RTNL 0x04 @@ -4636,19 +5015,19 @@ static struct genl_ops nl80211_ops[] = { .flags = GENL_ADMIN_PERM, }, { - .cmd = NL80211_CMD_GET_MESH_PARAMS, - .doit = nl80211_get_mesh_params, + .cmd = NL80211_CMD_GET_MESH_CONFIG, + .doit = nl80211_get_mesh_config, .policy = nl80211_policy, /* can be retrieved by unprivileged users */ .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, { - .cmd = NL80211_CMD_SET_MESH_PARAMS, - .doit = nl80211_set_mesh_params, + .cmd = NL80211_CMD_SET_MESH_CONFIG, + .doit = nl80211_update_mesh_config, .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, - .internal_flags = NL80211_FLAG_NEED_NETDEV | + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | NL80211_FLAG_NEED_RTNL, }, { @@ -4816,6 +5195,14 @@ static struct genl_ops nl80211_ops[] = { NL80211_FLAG_NEED_RTNL, }, { + .cmd = NL80211_CMD_FRAME_WAIT_CANCEL, + .doit = nl80211_tx_mgmt_cancel_wait, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { .cmd = NL80211_CMD_SET_POWER_SAVE, .doit = nl80211_set_power_save, .policy = nl80211_policy, @@ -4855,6 +5242,22 @@ static struct genl_ops nl80211_ops[] = { .internal_flags = NL80211_FLAG_NEED_NETDEV | NL80211_FLAG_NEED_RTNL, }, + { + .cmd = NL80211_CMD_JOIN_MESH, + .doit = nl80211_join_mesh, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, + { + .cmd = NL80211_CMD_LEAVE_MESH, + .doit = nl80211_leave_mesh, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | + NL80211_FLAG_NEED_RTNL, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { @@ -5133,6 +5536,22 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, NL80211_CMD_DISASSOCIATE, gfp); } +void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp); +} + +void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *buf, + size_t len, gfp_t gfp) +{ + nl80211_send_mlme_event(rdev, netdev, buf, len, + NL80211_CMD_UNPROT_DISASSOCIATE, gfp); +} + static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, int cmd, const u8 *addr, gfp_t gfp) @@ -5651,6 +6070,51 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, nlmsg_free(msg); } +void +nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, gfp_t gfp) +{ + struct sk_buff *msg; + struct nlattr *pinfoattr; + void *hdr; + + msg = nlmsg_new(NLMSG_GOODSIZE, gfp); + if (!msg) + return; + + hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); + if (!hdr) { + nlmsg_free(msg); + return; + } + + NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); + NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); + NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer); + + pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); + if (!pinfoattr) + goto nla_put_failure; + + NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets); + + nla_nest_end(msg, pinfoattr); + + if (genlmsg_end(msg, hdr) < 0) { + nlmsg_free(msg); + return; + } + + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); + return; + + nla_put_failure: + genlmsg_cancel(msg, hdr); + nlmsg_free(msg); +} + static int nl80211_netlink_notify(struct notifier_block * nb, unsigned long state, void *_notify) diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 30d2f939150..e3f7fa88696 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h @@ -25,6 +25,12 @@ void nl80211_send_deauth(struct cfg80211_registered_device *rdev, void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); +void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev, + struct net_device *netdev, + const u8 *buf, size_t len, gfp_t gfp); void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, struct net_device *netdev, const u8 *addr, gfp_t gfp); @@ -87,5 +93,9 @@ nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, struct net_device *netdev, enum nl80211_cqm_rssi_threshold_event rssi_event, gfp_t gfp); +void +nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev, + struct net_device *netdev, const u8 *peer, + u32 num_packets, gfp_t gfp); #endif /* __NET_WIRELESS_NL80211_H */ diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 4b9f8912526..37693b6ef23 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -32,6 +32,9 @@ * rely on some SHA1 checksum of the regdomain for example. * */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/slab.h> #include <linux/list.h> @@ -48,7 +51,7 @@ #ifdef CONFIG_CFG80211_REG_DEBUG #define REG_DBG_PRINT(format, args...) \ do { \ - printk(KERN_DEBUG format , ## args); \ + printk(KERN_DEBUG pr_fmt(format), ##args); \ } while (0) #else #define REG_DBG_PRINT(args...) @@ -96,6 +99,9 @@ struct reg_beacon { struct ieee80211_channel chan; }; +static void reg_todo(struct work_struct *work); +static DECLARE_WORK(reg_work, reg_todo); + /* We keep a static world regulatory domain in case of the absence of CRDA */ static const struct ieee80211_regdomain world_regdom = { .n_reg_rules = 5, @@ -367,11 +373,10 @@ static int call_crda(const char *alpha2) }; if (!is_world_regdom((char *) alpha2)) - printk(KERN_INFO "cfg80211: Calling CRDA for country: %c%c\n", + pr_info("Calling CRDA for country: %c%c\n", alpha2[0], alpha2[1]); else - printk(KERN_INFO "cfg80211: Calling CRDA to update world " - "regulatory domain\n"); + pr_info("Calling CRDA to update world regulatory domain\n"); /* query internal regulatory database (if it exists) */ reg_regdb_query(alpha2); @@ -656,7 +661,8 @@ static int freq_reg_info_regd(struct wiphy *wiphy, * Follow the driver's regulatory domain, if present, unless a country * IE has been processed or a user wants to help complaince further */ - if (last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + if (!custom_regd && + last_request->initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && last_request->initiator != NL80211_REGDOM_SET_BY_USER && wiphy->regd) regd = wiphy->regd; @@ -711,6 +717,60 @@ int freq_reg_info(struct wiphy *wiphy, } EXPORT_SYMBOL(freq_reg_info); +#ifdef CONFIG_CFG80211_REG_DEBUG +static const char *reg_initiator_name(enum nl80211_reg_initiator initiator) +{ + switch (initiator) { + case NL80211_REGDOM_SET_BY_CORE: + return "Set by core"; + case NL80211_REGDOM_SET_BY_USER: + return "Set by user"; + case NL80211_REGDOM_SET_BY_DRIVER: + return "Set by driver"; + case NL80211_REGDOM_SET_BY_COUNTRY_IE: + return "Set by country IE"; + default: + WARN_ON(1); + return "Set by bug"; + } +} + +static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, + u32 desired_bw_khz, + const struct ieee80211_reg_rule *reg_rule) +{ + const struct ieee80211_power_rule *power_rule; + const struct ieee80211_freq_range *freq_range; + char max_antenna_gain[32]; + + power_rule = ®_rule->power_rule; + freq_range = ®_rule->freq_range; + + if (!power_rule->max_antenna_gain) + snprintf(max_antenna_gain, 32, "N/A"); + else + snprintf(max_antenna_gain, 32, "%d", power_rule->max_antenna_gain); + + REG_DBG_PRINT("Updating information on frequency %d MHz " + "for a %d MHz width channel with regulatory rule:\n", + chan->center_freq, + KHZ_TO_MHZ(desired_bw_khz)); + + REG_DBG_PRINT("%d KHz - %d KHz @ KHz), (%s mBi, %d mBm)\n", + freq_range->start_freq_khz, + freq_range->end_freq_khz, + max_antenna_gain, + power_rule->max_eirp); +} +#else +static void chan_reg_rule_print_dbg(struct ieee80211_channel *chan, + u32 desired_bw_khz, + const struct ieee80211_reg_rule *reg_rule) +{ + return; +} +#endif + /* * Note that right now we assume the desired channel bandwidth * is always 20 MHz for each individual channel (HT40 uses 20 MHz @@ -720,7 +780,9 @@ EXPORT_SYMBOL(freq_reg_info); * on the wiphy with the target_bw specified. Then we can simply use * that below for the desired_bw_khz below. */ -static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, +static void handle_channel(struct wiphy *wiphy, + enum nl80211_reg_initiator initiator, + enum ieee80211_band band, unsigned int chan_idx) { int r; @@ -748,8 +810,27 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, desired_bw_khz, ®_rule); - if (r) + if (r) { + /* + * We will disable all channels that do not match our + * recieved regulatory rule unless the hint is coming + * from a Country IE and the Country IE had no information + * about a band. The IEEE 802.11 spec allows for an AP + * to send only a subset of the regulatory rules allowed, + * so an AP in the US that only supports 2.4 GHz may only send + * a country IE with information for the 2.4 GHz band + * while 5 GHz is still supported. + */ + if (initiator == NL80211_REGDOM_SET_BY_COUNTRY_IE && + r == -ERANGE) + return; + + REG_DBG_PRINT("Disabling freq %d MHz\n", chan->center_freq); + chan->flags = IEEE80211_CHAN_DISABLED; return; + } + + chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; @@ -784,7 +865,9 @@ static void handle_channel(struct wiphy *wiphy, enum ieee80211_band band, chan->max_power = (int) MBM_TO_DBM(power_rule->max_eirp); } -static void handle_band(struct wiphy *wiphy, enum ieee80211_band band) +static void handle_band(struct wiphy *wiphy, + enum ieee80211_band band, + enum nl80211_reg_initiator initiator) { unsigned int i; struct ieee80211_supported_band *sband; @@ -793,24 +876,42 @@ static void handle_band(struct wiphy *wiphy, enum ieee80211_band band) sband = wiphy->bands[band]; for (i = 0; i < sband->n_channels; i++) - handle_channel(wiphy, band, i); + handle_channel(wiphy, initiator, band, i); } static bool ignore_reg_update(struct wiphy *wiphy, enum nl80211_reg_initiator initiator) { - if (!last_request) + if (!last_request) { + REG_DBG_PRINT("Ignoring regulatory request %s since " + "last_request is not set\n", + reg_initiator_name(initiator)); return true; + } + if (initiator == NL80211_REGDOM_SET_BY_CORE && - wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) + wiphy->flags & WIPHY_FLAG_CUSTOM_REGULATORY) { + REG_DBG_PRINT("Ignoring regulatory request %s " + "since the driver uses its own custom " + "regulatory domain ", + reg_initiator_name(initiator)); return true; + } + /* * wiphy->regd will be set once the device has its own * desired regulatory domain set */ if (wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY && !wiphy->regd && - !is_world_regdom(last_request->alpha2)) + initiator != NL80211_REGDOM_SET_BY_COUNTRY_IE && + !is_world_regdom(last_request->alpha2)) { + REG_DBG_PRINT("Ignoring regulatory request %s " + "since the driver requires its own regulaotry " + "domain to be set first", + reg_initiator_name(initiator)); return true; + } + return false; } @@ -1030,7 +1131,7 @@ void wiphy_update_regulatory(struct wiphy *wiphy, goto out; for (band = 0; band < IEEE80211_NUM_BANDS; band++) { if (wiphy->bands[band]) - handle_band(wiphy, band); + handle_band(wiphy, band, initiator); } out: reg_process_beacons(wiphy); @@ -1066,10 +1167,17 @@ static void handle_channel_custom(struct wiphy *wiphy, regd); if (r) { + REG_DBG_PRINT("Disabling freq %d MHz as custom " + "regd has no rule that fits a %d MHz " + "wide channel\n", + chan->center_freq, + KHZ_TO_MHZ(desired_bw_khz)); chan->flags = IEEE80211_CHAN_DISABLED; return; } + chan_reg_rule_print_dbg(chan, desired_bw_khz, reg_rule); + power_rule = ®_rule->power_rule; freq_range = ®_rule->freq_range; @@ -1215,6 +1323,21 @@ static int ignore_request(struct wiphy *wiphy, return -EINVAL; } +static void reg_set_request_processed(void) +{ + bool need_more_processing = false; + + last_request->processed = true; + + spin_lock(®_requests_lock); + if (!list_empty(®_requests_list)) + need_more_processing = true; + spin_unlock(®_requests_lock); + + if (need_more_processing) + schedule_work(®_work); +} + /** * __regulatory_hint - hint to the wireless core a regulatory domain * @wiphy: if the hint comes from country information from an AP, this @@ -1290,8 +1413,10 @@ new_request: * have applied the requested regulatory domain before we just * inform userspace we have processed the request */ - if (r == -EALREADY) + if (r == -EALREADY) { nl80211_send_reg_change_event(last_request); + reg_set_request_processed(); + } return r; } @@ -1307,16 +1432,13 @@ static void reg_process_hint(struct regulatory_request *reg_request) BUG_ON(!reg_request->alpha2); - mutex_lock(&cfg80211_mutex); - mutex_lock(®_mutex); - if (wiphy_idx_valid(reg_request->wiphy_idx)) wiphy = wiphy_idx_to_wiphy(reg_request->wiphy_idx); if (reg_request->initiator == NL80211_REGDOM_SET_BY_DRIVER && !wiphy) { kfree(reg_request); - goto out; + return; } r = __regulatory_hint(wiphy, reg_request); @@ -1324,28 +1446,46 @@ static void reg_process_hint(struct regulatory_request *reg_request) if (r == -EALREADY && wiphy && wiphy->flags & WIPHY_FLAG_STRICT_REGULATORY) wiphy_update_regulatory(wiphy, initiator); -out: - mutex_unlock(®_mutex); - mutex_unlock(&cfg80211_mutex); } -/* Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* */ +/* + * Processes regulatory hints, this is all the NL80211_REGDOM_SET_BY_* + * Regulatory hints come on a first come first serve basis and we + * must process each one atomically. + */ static void reg_process_pending_hints(void) - { +{ struct regulatory_request *reg_request; + mutex_lock(&cfg80211_mutex); + mutex_lock(®_mutex); + + /* When last_request->processed becomes true this will be rescheduled */ + if (last_request && !last_request->processed) { + REG_DBG_PRINT("Pending regulatory request, waiting " + "for it to be processed..."); + goto out; + } + spin_lock(®_requests_lock); - while (!list_empty(®_requests_list)) { - reg_request = list_first_entry(®_requests_list, - struct regulatory_request, - list); - list_del_init(®_request->list); + if (list_empty(®_requests_list)) { spin_unlock(®_requests_lock); - reg_process_hint(reg_request); - spin_lock(®_requests_lock); + goto out; } + + reg_request = list_first_entry(®_requests_list, + struct regulatory_request, + list); + list_del_init(®_request->list); + spin_unlock(®_requests_lock); + + reg_process_hint(reg_request); + +out: + mutex_unlock(®_mutex); + mutex_unlock(&cfg80211_mutex); } /* Processes beacon hints -- this has nothing to do with country IEs */ @@ -1392,8 +1532,6 @@ static void reg_todo(struct work_struct *work) reg_process_pending_beacon_hints(); } -static DECLARE_WORK(reg_work, reg_todo); - static void queue_regulatory_request(struct regulatory_request *request) { if (isalpha(request->alpha2[0])) @@ -1428,12 +1566,7 @@ static int regulatory_hint_core(const char *alpha2) request->alpha2[1] = alpha2[1]; request->initiator = NL80211_REGDOM_SET_BY_CORE; - /* - * This ensures last_request is populated once modules - * come swinging in and calling regulatory hints and - * wiphy_apply_custom_regulatory(). - */ - reg_process_hint(request); + queue_regulatory_request(request); return 0; } @@ -1559,7 +1692,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) 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 " + REG_DBG_PRINT("Restoring regulatory settings " "including user preference\n"); user_alpha2[0] = '9'; user_alpha2[1] = '7'; @@ -1570,7 +1703,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) * back as they were for a full restore. */ if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("cfg80211: Keeping preference on " + REG_DBG_PRINT("Keeping preference on " "module parameter ieee80211_regdom: %c%c\n", ieee80211_regdom[0], ieee80211_regdom[1]); @@ -1578,7 +1711,7 @@ static void restore_alpha2(char *alpha2, bool reset_user) alpha2[1] = ieee80211_regdom[1]; } } else { - REG_DBG_PRINT("cfg80211: Restoring regulatory settings " + REG_DBG_PRINT("Restoring regulatory settings " "while preserving user preference for: %c%c\n", user_alpha2[0], user_alpha2[1]); @@ -1586,14 +1719,14 @@ static void restore_alpha2(char *alpha2, bool reset_user) alpha2[1] = user_alpha2[1]; } } else if (!is_world_regdom(ieee80211_regdom)) { - REG_DBG_PRINT("cfg80211: Keeping preference on " + REG_DBG_PRINT("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"); + REG_DBG_PRINT("Restoring regulatory settings\n"); } /* @@ -1661,7 +1794,7 @@ static void restore_regulatory_settings(bool reset_user) void regulatory_hint_disconnect(void) { - REG_DBG_PRINT("cfg80211: All devices are disconnected, going to " + REG_DBG_PRINT("All devices are disconnected, going to " "restore regulatory settings\n"); restore_regulatory_settings(false); } @@ -1691,7 +1824,7 @@ int regulatory_hint_found_beacon(struct wiphy *wiphy, if (!reg_beacon) return -ENOMEM; - REG_DBG_PRINT("cfg80211: Found new beacon on " + REG_DBG_PRINT("Found new beacon on " "frequency: %d MHz (Ch %d) on %s\n", beacon_chan->center_freq, ieee80211_frequency_to_channel(beacon_chan->center_freq), @@ -1721,8 +1854,7 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) const struct ieee80211_freq_range *freq_range = NULL; const struct ieee80211_power_rule *power_rule = NULL; - printk(KERN_INFO " (start_freq - end_freq @ bandwidth), " - "(max_antenna_gain, max_eirp)\n"); + pr_info(" (start_freq - end_freq @ bandwidth), (max_antenna_gain, max_eirp)\n"); for (i = 0; i < rd->n_reg_rules; i++) { reg_rule = &rd->reg_rules[i]; @@ -1734,16 +1866,14 @@ static void print_rd_rules(const struct ieee80211_regdomain *rd) * in certain regions */ if (power_rule->max_antenna_gain) - printk(KERN_INFO " (%d KHz - %d KHz @ %d KHz), " - "(%d mBi, %d mBm)\n", + pr_info(" (%d KHz - %d KHz @ %d KHz), (%d mBi, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, power_rule->max_antenna_gain, power_rule->max_eirp); else - printk(KERN_INFO " (%d KHz - %d KHz @ %d KHz), " - "(N/A, %d mBm)\n", + pr_info(" (%d KHz - %d KHz @ %d KHz), (N/A, %d mBm)\n", freq_range->start_freq_khz, freq_range->end_freq_khz, freq_range->max_bandwidth_khz, @@ -1762,27 +1892,20 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) rdev = cfg80211_rdev_by_wiphy_idx( last_request->wiphy_idx); if (rdev) { - printk(KERN_INFO "cfg80211: Current regulatory " - "domain updated by AP to: %c%c\n", + pr_info("Current regulatory domain updated by AP to: %c%c\n", rdev->country_ie_alpha2[0], rdev->country_ie_alpha2[1]); } else - printk(KERN_INFO "cfg80211: Current regulatory " - "domain intersected:\n"); + pr_info("Current regulatory domain intersected:\n"); } else - printk(KERN_INFO "cfg80211: Current regulatory " - "domain intersected:\n"); + pr_info("Current regulatory domain intersected:\n"); } else if (is_world_regdom(rd->alpha2)) - printk(KERN_INFO "cfg80211: World regulatory " - "domain updated:\n"); + pr_info("World regulatory domain updated:\n"); else { if (is_unknown_alpha2(rd->alpha2)) - printk(KERN_INFO "cfg80211: Regulatory domain " - "changed to driver built-in settings " - "(unknown country)\n"); + pr_info("Regulatory domain changed to driver built-in settings (unknown country)\n"); else - printk(KERN_INFO "cfg80211: Regulatory domain " - "changed to country: %c%c\n", + pr_info("Regulatory domain changed to country: %c%c\n", rd->alpha2[0], rd->alpha2[1]); } print_rd_rules(rd); @@ -1790,8 +1913,7 @@ static void print_regdomain(const struct ieee80211_regdomain *rd) static void print_regdomain_info(const struct ieee80211_regdomain *rd) { - printk(KERN_INFO "cfg80211: Regulatory domain: %c%c\n", - rd->alpha2[0], rd->alpha2[1]); + pr_info("Regulatory domain: %c%c\n", rd->alpha2[0], rd->alpha2[1]); print_rd_rules(rd); } @@ -1842,8 +1964,7 @@ static int __set_regdom(const struct ieee80211_regdomain *rd) return -EINVAL; if (!is_valid_rd(rd)) { - printk(KERN_ERR "cfg80211: Invalid " - "regulatory domain detected:\n"); + pr_err("Invalid regulatory domain detected:\n"); print_regdomain_info(rd); return -EINVAL; } @@ -1959,6 +2080,8 @@ int set_regdom(const struct ieee80211_regdomain *rd) nl80211_send_reg_change_event(last_request); + reg_set_request_processed(); + mutex_unlock(®_mutex); return r; @@ -2015,8 +2138,7 @@ int __init regulatory_init(void) * early boot for call_usermodehelper(). For now treat these * errors as non-fatal. */ - printk(KERN_ERR "cfg80211: kobject_uevent_env() was unable " - "to call CRDA during init"); + pr_err("kobject_uevent_env() was unable to call CRDA during init\n"); #ifdef CONFIG_CFG80211_REG_DEBUG /* We want to find out exactly why when debugging */ WARN_ON(err); diff --git a/net/wireless/scan.c b/net/wireless/scan.c index 503ebb86ba1..ea427f418f6 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -464,6 +464,9 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, if (res->pub.beacon_ies) { size_t used = dev->wiphy.bss_priv_size + sizeof(*res); size_t ielen = res->pub.len_beacon_ies; + bool information_elements_is_beacon_ies = + (found->pub.information_elements == + found->pub.beacon_ies); if (found->pub.beacon_ies && !found->beacon_ies_allocated && @@ -487,6 +490,14 @@ cfg80211_bss_update(struct cfg80211_registered_device *dev, found->pub.len_beacon_ies = ielen; } } + + /* Override IEs if they were from a beacon before */ + if (information_elements_is_beacon_ies) { + found->pub.information_elements = + found->pub.beacon_ies; + found->pub.len_information_elements = + found->pub.len_beacon_ies; + } } kref_put(&res->ref, bss_release); diff --git a/net/wireless/util.c b/net/wireless/util.c index 76120aeda57..7620ae2fcf1 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c @@ -502,7 +502,7 @@ int ieee80211_data_from_8023(struct sk_buff *skb, const u8 *addr, skb_orphan(skb); if (pskb_expand_head(skb, head_need, 0, GFP_ATOMIC)) { - printk(KERN_ERR "failed to reallocate Tx buffer\n"); + pr_err("failed to reallocate Tx buffer\n"); return -ENOMEM; } skb->truesize += head_need; @@ -685,20 +685,18 @@ void cfg80211_upload_connect_keys(struct wireless_dev *wdev) continue; if (rdev->ops->add_key(wdev->wiphy, dev, i, false, NULL, &wdev->connect_keys->params[i])) { - printk(KERN_ERR "%s: failed to set key %d\n", - dev->name, i); + netdev_err(dev, "failed to set key %d\n", i); continue; } if (wdev->connect_keys->def == i) - if (rdev->ops->set_default_key(wdev->wiphy, dev, i)) { - printk(KERN_ERR "%s: failed to set defkey %d\n", - dev->name, i); + if (rdev->ops->set_default_key(wdev->wiphy, dev, + i, true, true)) { + netdev_err(dev, "failed to set defkey %d\n", i); continue; } if (wdev->connect_keys->defmgmt == i) if (rdev->ops->set_default_mgmt_key(wdev->wiphy, dev, i)) - printk(KERN_ERR "%s: failed to set mgtdef %d\n", - dev->name, i); + netdev_err(dev, "failed to set mgtdef %d\n", i); } kfree(wdev->connect_keys); @@ -795,6 +793,7 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, if (ntype != otype) { dev->ieee80211_ptr->use_4addr = false; + dev->ieee80211_ptr->mesh_id_up_len = 0; switch (otype) { case NL80211_IFTYPE_ADHOC: diff --git a/net/wireless/wext-compat.c b/net/wireless/wext-compat.c index 12222ee6ebf..3e5dbd4e4cd 100644 --- a/net/wireless/wext-compat.c +++ b/net/wireless/wext-compat.c @@ -548,8 +548,8 @@ static int __cfg80211_set_encryption(struct cfg80211_registered_device *rdev, __cfg80211_leave_ibss(rdev, wdev->netdev, true); rejoin = true; } - err = rdev->ops->set_default_key(&rdev->wiphy, - dev, idx); + err = rdev->ops->set_default_key(&rdev->wiphy, dev, + idx, true, true); } if (!err) { wdev->wext.default_key = idx; @@ -627,8 +627,8 @@ int cfg80211_wext_siwencode(struct net_device *dev, err = 0; wdev_lock(wdev); if (wdev->current_bss) - err = rdev->ops->set_default_key(&rdev->wiphy, - dev, idx); + err = rdev->ops->set_default_key(&rdev->wiphy, dev, + idx, true, true); if (!err) wdev->wext.default_key = idx; wdev_unlock(wdev); diff --git a/net/wireless/wext-core.c b/net/wireless/wext-core.c index dc675a3daa3..fdbc23c10d8 100644 --- a/net/wireless/wext-core.c +++ b/net/wireless/wext-core.c @@ -467,8 +467,8 @@ void wireless_send_event(struct net_device * dev, * The best the driver could do is to log an error message. * We will do it ourselves instead... */ - printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", - dev->name, cmd); + netdev_err(dev, "(WE) : Invalid/Unknown Wireless Event (0x%04X)\n", + cmd); return; } @@ -476,11 +476,13 @@ void wireless_send_event(struct net_device * dev, if (descr->header_type == IW_HEADER_TYPE_POINT) { /* Check if number of token fits within bounds */ if (wrqu->data.length > descr->max_tokens) { - printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); + netdev_err(dev, "(WE) : Wireless Event too big (%d)\n", + wrqu->data.length); return; } if (wrqu->data.length < descr->min_tokens) { - printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); + netdev_err(dev, "(WE) : Wireless Event too small (%d)\n", + wrqu->data.length); return; } /* Calculate extra_len - extra is NULL for restricted events */ |