diff options
-rw-r--r-- | include/linux/nl80211.h | 9 | ||||
-rw-r--r-- | include/net/cfg80211.h | 40 | ||||
-rw-r--r-- | net/core/dev.c | 1 | ||||
-rw-r--r-- | net/mac80211/cfg.c | 14 | ||||
-rw-r--r-- | net/wireless/core.c | 75 | ||||
-rw-r--r-- | net/wireless/core.h | 5 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 202 | ||||
-rw-r--r-- | net/wireless/scan.c | 22 | ||||
-rw-r--r-- | net/wireless/sme.c | 3 |
9 files changed, 272 insertions, 99 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h index 962e2232a074..cb3dc6027fd9 100644 --- a/include/linux/nl80211.h +++ b/include/linux/nl80211.h @@ -262,6 +262,9 @@ * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and * %NL80211_ATTR_REASON_CODE attributes are used. * + * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices + * associated with this wiphy must be down and will follow. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -336,6 +339,8 @@ enum nl80211_commands { NL80211_CMD_ROAM, NL80211_CMD_DISCONNECT, + NL80211_CMD_SET_WIPHY_NETNS, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -573,6 +578,8 @@ enum nl80211_commands { * and join_ibss(), key information is in a nested attribute each * with %NL80211_KEY_* sub-attributes * + * @NL80211_ATTR_PID: Process ID of a network namespace. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -701,6 +708,8 @@ enum nl80211_attrs { NL80211_ATTR_KEY, NL80211_ATTR_KEYS, + NL80211_ATTR_PID, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h index a981ca8a5701..0d278777e39c 100644 --- a/include/net/cfg80211.h +++ b/include/net/cfg80211.h @@ -542,7 +542,7 @@ struct cfg80211_ssid { * @ie: optional information element(s) to add into Probe Request or %NULL * @ie_len: length of ie in octets * @wiphy: the wiphy this was for - * @ifidx: the interface index + * @dev: the interface */ struct cfg80211_scan_request { struct cfg80211_ssid *ssids; @@ -554,7 +554,7 @@ struct cfg80211_scan_request { /* internal */ struct wiphy *wiphy; - int ifidx; + struct net_device *dev; bool aborted; }; @@ -845,7 +845,8 @@ struct cfg80211_bitrate_mask { * @resume: wiphy device needs to be resumed * * @add_virtual_intf: create a new virtual interface with the given name, - * must set the struct wireless_dev's iftype. + * must set the struct wireless_dev's iftype. Beware: You must create + * the new netdev in the wiphy's network namespace! * * @del_virtual_intf: remove the virtual interface determined by ifindex. * @@ -937,7 +938,7 @@ struct cfg80211_ops { int (*add_virtual_intf)(struct wiphy *wiphy, char *name, enum nl80211_iftype type, u32 *flags, struct vif_params *params); - int (*del_virtual_intf)(struct wiphy *wiphy, int ifindex); + int (*del_virtual_intf)(struct wiphy *wiphy, struct net_device *dev); int (*change_virtual_intf)(struct wiphy *wiphy, struct net_device *dev, enum nl80211_iftype type, u32 *flags, @@ -1088,6 +1089,9 @@ struct cfg80211_ops { * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold); * -1 = fragmentation disabled, only odd values >= 256 used * @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled + * @net: the network namespace this wiphy currently lives in + * @netnsok: if set to false, do not allow changing the netns of this + * wiphy at all */ struct wiphy { /* assign these fields before you register the wiphy */ @@ -1101,6 +1105,8 @@ struct wiphy { bool custom_regulatory; bool strict_regulatory; + bool netnsok; + enum cfg80211_signal_type signal_type; int bss_priv_size; @@ -1139,9 +1145,35 @@ struct wiphy { /* dir in debugfs: ieee80211/<wiphyname> */ struct dentry *debugfsdir; +#ifdef CONFIG_NET_NS + /* the network namespace this phy lives in currently */ + struct net *_net; +#endif + char priv[0] __attribute__((__aligned__(NETDEV_ALIGN))); }; +#ifdef CONFIG_NET_NS +static inline struct net *wiphy_net(struct wiphy *wiphy) +{ + return wiphy->_net; +} + +static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) +{ + wiphy->_net = net; +} +#else +static inline struct net *wiphy_net(struct wiphy *wiphy) +{ + return &init_net; +} + +static inline void wiphy_net_set(struct wiphy *wiphy, struct net *net) +{ +} +#endif + /** * wiphy_priv - return priv from wiphy * diff --git a/net/core/dev.c b/net/core/dev.c index d6c657ee413d..71347668c506 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -5344,6 +5344,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char out: return err; } +EXPORT_SYMBOL_GPL(dev_change_net_namespace); static int dev_cpu_callback(struct notifier_block *nfb, unsigned long action, diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 52928ad90570..4bbf5007799b 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -57,19 +57,9 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name, return 0; } -static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) +static int ieee80211_del_iface(struct wiphy *wiphy, struct net_device *dev) { - struct net_device *dev; - struct ieee80211_sub_if_data *sdata; - - /* we're under RTNL */ - dev = __dev_get_by_index(&init_net, ifindex); - if (!dev) - return -ENODEV; - - sdata = IEEE80211_DEV_TO_SUB_IF(dev); - - ieee80211_if_remove(sdata); + ieee80211_if_remove(IEEE80211_DEV_TO_SUB_IF(dev)); return 0; } diff --git a/net/wireless/core.c b/net/wireless/core.c index 6891cd0e38d5..442c9f389799 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c @@ -106,7 +106,7 @@ __cfg80211_rdev_from_info(struct genl_info *info) if (info->attrs[NL80211_ATTR_IFINDEX]) { ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(genl_info_net(info), ifindex); if (dev) { if (dev->ieee80211_ptr) byifidx = @@ -151,13 +151,13 @@ cfg80211_get_dev_from_info(struct genl_info *info) } struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(int ifindex) +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex) { struct cfg80211_registered_device *rdev = ERR_PTR(-ENODEV); struct net_device *dev; mutex_lock(&cfg80211_mutex); - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(net, ifindex); if (!dev) goto out; if (dev->ieee80211_ptr) { @@ -222,6 +222,42 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, return 0; } +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net) +{ + struct wireless_dev *wdev; + int err = 0; + + if (!rdev->wiphy.netnsok) + return -EOPNOTSUPP; + + list_for_each_entry(wdev, &rdev->netdev_list, list) { + wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; + err = dev_change_net_namespace(wdev->netdev, net, "wlan%d"); + if (err) + break; + wdev->netdev->features |= NETIF_F_NETNS_LOCAL; + } + + if (err) { + /* failed -- clean up to old netns */ + net = wiphy_net(&rdev->wiphy); + + list_for_each_entry_continue_reverse(wdev, &rdev->netdev_list, + list) { + wdev->netdev->features &= ~NETIF_F_NETNS_LOCAL; + err = dev_change_net_namespace(wdev->netdev, net, + "wlan%d"); + WARN_ON(err); + wdev->netdev->features |= NETIF_F_NETNS_LOCAL; + } + } + + wiphy_net_set(&rdev->wiphy, net); + + return err; +} + static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data) { struct cfg80211_registered_device *rdev = data; @@ -375,6 +411,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) rdev->wiphy.dev.class = &ieee80211_class; rdev->wiphy.dev.platform_data = rdev; + wiphy_net_set(&rdev->wiphy, &init_net); + rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), &rdev->wiphy.dev, RFKILL_TYPE_WLAN, @@ -615,6 +653,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, spin_lock_init(&wdev->event_lock); mutex_lock(&rdev->devlist_mtx); list_add(&wdev->list, &rdev->netdev_list); + /* can only change netns with wiphy */ + dev->features |= NETIF_F_NETNS_LOCAL; + if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj, "phy80211")) { printk(KERN_ERR "wireless: failed to add phy80211 " @@ -705,10 +746,32 @@ static struct notifier_block cfg80211_netdev_notifier = { .notifier_call = cfg80211_netdev_notifier_call, }; -static int cfg80211_init(void) +static void __net_exit cfg80211_pernet_exit(struct net *net) +{ + struct cfg80211_registered_device *rdev; + + rtnl_lock(); + mutex_lock(&cfg80211_mutex); + list_for_each_entry(rdev, &cfg80211_rdev_list, list) { + if (net_eq(wiphy_net(&rdev->wiphy), net)) + WARN_ON(cfg80211_switch_netns(rdev, &init_net)); + } + mutex_unlock(&cfg80211_mutex); + rtnl_unlock(); +} + +static struct pernet_operations cfg80211_pernet_ops = { + .exit = cfg80211_pernet_exit, +}; + +static int __init cfg80211_init(void) { int err; + err = register_pernet_device(&cfg80211_pernet_ops); + if (err) + goto out_fail_pernet; + err = wiphy_sysfs_init(); if (err) goto out_fail_sysfs; @@ -736,9 +799,10 @@ out_fail_nl80211: out_fail_notifier: wiphy_sysfs_exit(); out_fail_sysfs: + unregister_pernet_device(&cfg80211_pernet_ops); +out_fail_pernet: return err; } - subsys_initcall(cfg80211_init); static void cfg80211_exit(void) @@ -748,5 +812,6 @@ static void cfg80211_exit(void) unregister_netdevice_notifier(&cfg80211_netdev_notifier); wiphy_sysfs_exit(); regulatory_exit(); + unregister_pernet_device(&cfg80211_pernet_ops); } module_exit(cfg80211_exit); diff --git a/net/wireless/core.h b/net/wireless/core.h index 2ec8ddbe57de..4276b70cd975 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -170,7 +170,10 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); /* identical to cfg80211_get_dev_from_info but only operate on ifindex */ extern struct cfg80211_registered_device * -cfg80211_get_dev_from_ifindex(int ifindex); +cfg80211_get_dev_from_ifindex(struct net *net, int ifindex); + +int cfg80211_switch_netns(struct cfg80211_registered_device *rdev, + struct net *net); static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev) { diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index da450ef1fc7e..7880a9c4cdda 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -14,8 +14,10 @@ #include <linux/rtnetlink.h> #include <linux/netlink.h> #include <linux/etherdevice.h> +#include <net/net_namespace.h> #include <net/genetlink.h> #include <net/cfg80211.h> +#include <net/sock.h> #include "core.h" #include "nl80211.h" #include "reg.h" @@ -27,24 +29,26 @@ static struct genl_family nl80211_fam = { .hdrsize = 0, /* no private header */ .version = 1, /* no particular meaning now */ .maxattr = NL80211_ATTR_MAX, + .netnsok = true, }; /* internal helper: get rdev and dev */ -static int get_rdev_dev_by_info_ifindex(struct nlattr **attrs, +static int get_rdev_dev_by_info_ifindex(struct genl_info *info, struct cfg80211_registered_device **rdev, struct net_device **dev) { + struct nlattr **attrs = info->attrs; int ifindex; if (!attrs[NL80211_ATTR_IFINDEX]) return -EINVAL; ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]); - *dev = dev_get_by_index(&init_net, ifindex); + *dev = dev_get_by_index(genl_info_net(info), ifindex); if (!*dev) return -ENODEV; - *rdev = cfg80211_get_dev_from_ifindex(ifindex); + *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex); if (IS_ERR(*rdev)) { dev_put(*dev); return PTR_ERR(*rdev); @@ -133,6 +137,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, + [NL80211_ATTR_PID] = { .type = NLA_U32 }, }; /* policy for the attributes */ @@ -532,6 +537,10 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, CMD(deauth, DEAUTHENTICATE); CMD(disassoc, DISASSOCIATE); CMD(join_ibss, JOIN_IBSS); + if (dev->wiphy.netnsok) { + i++; + NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); + } #undef CMD @@ -562,6 +571,8 @@ static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb) mutex_lock(&cfg80211_mutex); list_for_each_entry(dev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + continue; if (++idx <= start) continue; if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid, @@ -867,6 +878,8 @@ static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback * mutex_lock(&cfg80211_mutex); list_for_each_entry(dev, &cfg80211_rdev_list, list) { + if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk))) + continue; if (wp_idx < wp_start) { wp_idx++; continue; @@ -907,7 +920,7 @@ static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info) struct net_device *netdev; int err; - err = get_rdev_dev_by_info_ifindex(info->attrs, &dev, &netdev); + err = get_rdev_dev_by_info_ifindex(info, &dev, &netdev); if (err) return err; @@ -975,7 +988,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1098,26 +1111,25 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) { struct cfg80211_registered_device *rdev; - int ifindex, err; + int err; struct net_device *dev; rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; - ifindex = dev->ifindex; - dev_put(dev); if (!rdev->ops->del_virtual_intf) { err = -EOPNOTSUPP; goto out; } - err = rdev->ops->del_virtual_intf(&rdev->wiphy, ifindex); + err = rdev->ops->del_virtual_intf(&rdev->wiphy, dev); out: cfg80211_unlock_rdev(rdev); + dev_put(dev); unlock_rtnl: rtnl_unlock(); return err; @@ -1195,7 +1207,7 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1274,7 +1286,7 @@ static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1333,7 +1345,7 @@ static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1380,7 +1392,7 @@ static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1429,7 +1441,7 @@ static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1516,7 +1528,7 @@ static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -1726,13 +1738,13 @@ static int nl80211_dump_station(struct sk_buff *skb, rtnl_lock(); - netdev = __dev_get_by_index(&init_net, ifidx); + netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); if (!netdev) { err = -ENODEV; goto out_rtnl; } - dev = cfg80211_get_dev_from_ifindex(ifidx); + dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_rtnl; @@ -1791,7 +1803,7 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -1829,14 +1841,16 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) /* * Get vlan interface making sure it is on the right wiphy. */ -static int get_vlan(struct nlattr *vlanattr, +static int get_vlan(struct genl_info *info, struct cfg80211_registered_device *rdev, struct net_device **vlan) { + struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN]; *vlan = NULL; if (vlanattr) { - *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr)); + *vlan = dev_get_by_index(genl_info_net(info), + nla_get_u32(vlanattr)); if (!*vlan) return -ENODEV; if (!(*vlan)->ieee80211_ptr) @@ -1891,11 +1905,11 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); + err = get_vlan(info, rdev, ¶ms.vlan); if (err) goto out; @@ -2004,11 +2018,11 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; - err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], rdev, ¶ms.vlan); + err = get_vlan(info, rdev, ¶ms.vlan); if (err) goto out; @@ -2079,7 +2093,7 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2185,13 +2199,13 @@ static int nl80211_dump_mpath(struct sk_buff *skb, rtnl_lock(); - netdev = __dev_get_by_index(&init_net, ifidx); + netdev = __dev_get_by_index(sock_net(skb->sk), ifidx); if (!netdev) { err = -ENODEV; goto out_rtnl; } - dev = cfg80211_get_dev_from_ifindex(ifidx); + dev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(dev)) { err = PTR_ERR(dev); goto out_rtnl; @@ -2255,7 +2269,7 @@ static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2314,7 +2328,7 @@ static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2362,7 +2376,7 @@ static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2404,7 +2418,7 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2455,7 +2469,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2574,7 +2588,7 @@ static int nl80211_get_mesh_params(struct sk_buff *skb, rtnl_lock(); /* Look up our device */ - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2691,7 +2705,7 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -2947,7 +2961,7 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto out_rtnl; @@ -3069,14 +3083,16 @@ static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info) request->ie_len); } - request->ifidx = dev->ifindex; + request->dev = dev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; err = rdev->ops->scan(&rdev->wiphy, dev, request); - if (!err) + if (!err) { nl80211_send_scan_start(rdev, dev); + dev_hold(dev); + } out_free: if (err) { @@ -3198,11 +3214,11 @@ static int nl80211_dump_scan(struct sk_buff *skb, cb->args[0] = ifidx; } - dev = dev_get_by_index(&init_net, ifidx); + dev = dev_get_by_index(sock_net(skb->sk), ifidx); if (!dev) return -ENODEV; - rdev = cfg80211_get_dev_from_ifindex(ifidx); + rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx); if (IS_ERR(rdev)) { err = PTR_ERR(rdev); goto out_put_netdev; @@ -3312,7 +3328,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3448,7 +3464,7 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3531,7 +3547,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3593,7 +3609,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3666,7 +3682,7 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3739,7 +3755,7 @@ static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -3924,7 +3940,7 @@ static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) return err; rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -4000,7 +4016,7 @@ static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) rtnl_lock(); - err = get_rdev_dev_by_info_ifindex(info->attrs, &rdev, &dev); + err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); if (err) goto unlock_rtnl; @@ -4024,6 +4040,47 @@ unlock_rtnl: return err; } +static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info) +{ + struct cfg80211_registered_device *rdev; + struct net *net; + int err; + u32 pid; + + if (!info->attrs[NL80211_ATTR_PID]) + return -EINVAL; + + pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]); + + rtnl_lock(); + + rdev = cfg80211_get_dev_from_info(info); + if (IS_ERR(rdev)) { + err = PTR_ERR(rdev); + goto out; + } + + net = get_net_ns_by_pid(pid); + if (IS_ERR(net)) { + err = PTR_ERR(net); + goto out; + } + + err = 0; + + /* check if anything to do */ + if (net_eq(wiphy_net(&rdev->wiphy), net)) + goto out_put_net; + + err = cfg80211_switch_netns(rdev, net); + out_put_net: + put_net(net); + out: + cfg80211_unlock_rdev(rdev); + rtnl_unlock(); + return err; +} + static struct genl_ops nl80211_ops[] = { { .cmd = NL80211_CMD_GET_WIPHY, @@ -4257,6 +4314,12 @@ static struct genl_ops nl80211_ops[] = { .policy = nl80211_policy, .flags = GENL_ADMIN_PERM, }, + { + .cmd = NL80211_CMD_SET_WIPHY_NETNS, + .doit = nl80211_wiphy_netns, + .policy = nl80211_policy, + .flags = GENL_ADMIN_PERM, + }, }; static struct genl_multicast_group nl80211_mlme_mcgrp = { .name = "mlme", @@ -4288,7 +4351,8 @@ void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev) return; } - genlmsg_multicast(msg, 0, nl80211_config_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_config_mcgrp.id, GFP_KERNEL); } static int nl80211_add_scan_req(struct sk_buff *msg, @@ -4365,7 +4429,8 @@ void nl80211_send_scan_start(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, @@ -4383,7 +4448,8 @@ void nl80211_send_scan_done(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, @@ -4401,7 +4467,8 @@ void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_scan_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_scan_mcgrp.id, GFP_KERNEL); } /* @@ -4450,7 +4517,10 @@ void nl80211_send_reg_change_event(struct regulatory_request *request) return; } - genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_KERNEL); + rtnl_lock(); + genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, + GFP_KERNEL); + rtnl_unlock(); return; @@ -4486,7 +4556,8 @@ static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4553,7 +4624,8 @@ static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4611,7 +4683,8 @@ void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4651,7 +4724,8 @@ void nl80211_send_roamed(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4691,7 +4765,8 @@ void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, GFP_KERNEL); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, GFP_KERNEL); return; nla_put_failure: @@ -4726,7 +4801,8 @@ void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4766,7 +4842,8 @@ void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, return; } - genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); + genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, + nl80211_mlme_mcgrp.id, gfp); return; nla_put_failure: @@ -4819,7 +4896,10 @@ void nl80211_send_beacon_hint_event(struct wiphy *wiphy, return; } - genlmsg_multicast(msg, 0, nl80211_regulatory_mcgrp.id, GFP_ATOMIC); + rcu_read_lock(); + genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id, + GFP_ATOMIC); + rcu_read_unlock(); return; diff --git a/net/wireless/scan.c b/net/wireless/scan.c index decc59fe0ee8..1b578b8cb1c9 100644 --- a/net/wireless/scan.c +++ b/net/wireless/scan.c @@ -32,9 +32,7 @@ void __cfg80211_scan_done(struct work_struct *wk) mutex_lock(&rdev->mtx); request = rdev->scan_req; - dev = dev_get_by_index(&init_net, request->ifidx); - if (!dev) - goto out; + dev = request->dev; /* * This must be before sending the other events! @@ -58,7 +56,6 @@ void __cfg80211_scan_done(struct work_struct *wk) dev_put(dev); - out: cfg80211_unlock_rdev(rdev); wiphy_to_dev(request->wiphy)->scan_req = NULL; kfree(request); @@ -66,17 +63,10 @@ void __cfg80211_scan_done(struct work_struct *wk) void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) { - struct net_device *dev = dev_get_by_index(&init_net, request->ifidx); - if (WARN_ON(!dev)) { - kfree(request); - return; - } - WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req); request->aborted = aborted; schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk); - dev_put(dev); } EXPORT_SYMBOL(cfg80211_scan_done); @@ -592,7 +582,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (!netif_running(dev)) return -ENETDOWN; - rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) return PTR_ERR(rdev); @@ -617,7 +607,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, } creq->wiphy = wiphy; - creq->ifidx = dev->ifindex; + creq->dev = dev; creq->ssids = (void *)(creq + 1); creq->channels = (void *)(creq->ssids + 1); creq->n_channels = n_channels; @@ -654,8 +644,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, if (err) { rdev->scan_req = NULL; kfree(creq); - } else + } else { nl80211_send_scan_start(rdev, dev); + dev_hold(dev); + } out: cfg80211_unlock_rdev(rdev); return err; @@ -948,7 +940,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, if (!netif_running(dev)) return -ENETDOWN; - rdev = cfg80211_get_dev_from_ifindex(dev->ifindex); + rdev = cfg80211_get_dev_from_ifindex(dev_net(dev), dev->ifindex); if (IS_ERR(rdev)) return PTR_ERR(rdev); diff --git a/net/wireless/sme.c b/net/wireless/sme.c index 82de2d9795f4..a19741097989 100644 --- a/net/wireless/sme.c +++ b/net/wireless/sme.c @@ -86,7 +86,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) wdev->conn->params.ssid_len); request->ssids[0].ssid_len = wdev->conn->params.ssid_len; - request->ifidx = wdev->netdev->ifindex; + request->dev = wdev->netdev; request->wiphy = &rdev->wiphy; rdev->scan_req = request; @@ -95,6 +95,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) if (!err) { wdev->conn->state = CFG80211_CONN_SCANNING; nl80211_send_scan_start(rdev, wdev->netdev); + dev_hold(wdev->netdev); } else { rdev->scan_req = NULL; kfree(request); |