Commit 463d0183 authored by Johannes Berg's avatar Johannes Berg Committed by John W. Linville

cfg80211: make aware of net namespaces

In order to make cfg80211/nl80211 aware of network namespaces,
we have to do the following things:

 * del_virtual_intf method takes an interface index rather
   than a netdev pointer - simply change this

 * nl80211 uses init_net a lot, it changes to use the sender's
   network namespace

 * scan requests use the interface index, hold a netdev pointer
   and reference instead

 * we want a wiphy and its associated virtual interfaces to be
   in one netns together, so
    - we need to be able to change ns for a given interface, so
      export dev_change_net_namespace()
    - for each virtual interface set the NETIF_F_NETNS_LOCAL
      flag, and clear that flag only when the wiphy changes ns,
      to disallow breaking this invariant

 * when a network namespace goes away, we need to reparent the
   wiphy to init_net

 * cfg80211 users that support creating virtual interfaces must
   create them in the wiphy's namespace, currently this affects
   only mac80211

The end result is that you can now switch an entire wiphy into
a different network namespace with the new command
	iw phy#<idx> set netns <pid>
and all virtual interfaces will follow (or the operation fails).
Signed-off-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarJohn W. Linville <linville@tuxdriver.com>
parent 5061b0c2
...@@ -262,6 +262,9 @@ ...@@ -262,6 +262,9 @@
* reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and * reasons, for this the %NL80211_ATTR_DISCONNECTED_BY_AP and
* %NL80211_ATTR_REASON_CODE attributes are used. * %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_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use * @__NL80211_CMD_AFTER_LAST: internal use
*/ */
...@@ -336,6 +339,8 @@ enum nl80211_commands { ...@@ -336,6 +339,8 @@ enum nl80211_commands {
NL80211_CMD_ROAM, NL80211_CMD_ROAM,
NL80211_CMD_DISCONNECT, NL80211_CMD_DISCONNECT,
NL80211_CMD_SET_WIPHY_NETNS,
/* add new commands above here */ /* add new commands above here */
/* used to define NL80211_CMD_MAX below */ /* used to define NL80211_CMD_MAX below */
...@@ -573,6 +578,8 @@ enum nl80211_commands { ...@@ -573,6 +578,8 @@ enum nl80211_commands {
* and join_ibss(), key information is in a nested attribute each * and join_ibss(), key information is in a nested attribute each
* with %NL80211_KEY_* sub-attributes * with %NL80211_KEY_* sub-attributes
* *
* @NL80211_ATTR_PID: Process ID of a network namespace.
*
* @NL80211_ATTR_MAX: highest attribute number currently defined * @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use * @__NL80211_ATTR_AFTER_LAST: internal use
*/ */
...@@ -701,6 +708,8 @@ enum nl80211_attrs { ...@@ -701,6 +708,8 @@ enum nl80211_attrs {
NL80211_ATTR_KEY, NL80211_ATTR_KEY,
NL80211_ATTR_KEYS, NL80211_ATTR_KEYS,
NL80211_ATTR_PID,
/* add attributes here, update the policy in nl80211.c */ /* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST, __NL80211_ATTR_AFTER_LAST,
......
...@@ -542,7 +542,7 @@ struct cfg80211_ssid { ...@@ -542,7 +542,7 @@ struct cfg80211_ssid {
* @ie: optional information element(s) to add into Probe Request or %NULL * @ie: optional information element(s) to add into Probe Request or %NULL
* @ie_len: length of ie in octets * @ie_len: length of ie in octets
* @wiphy: the wiphy this was for * @wiphy: the wiphy this was for
* @ifidx: the interface index * @dev: the interface
*/ */
struct cfg80211_scan_request { struct cfg80211_scan_request {
struct cfg80211_ssid *ssids; struct cfg80211_ssid *ssids;
...@@ -554,7 +554,7 @@ struct cfg80211_scan_request { ...@@ -554,7 +554,7 @@ struct cfg80211_scan_request {
/* internal */ /* internal */
struct wiphy *wiphy; struct wiphy *wiphy;
int ifidx; struct net_device *dev;
bool aborted; bool aborted;
}; };
...@@ -845,7 +845,8 @@ struct cfg80211_bitrate_mask { ...@@ -845,7 +845,8 @@ struct cfg80211_bitrate_mask {
* @resume: wiphy device needs to be resumed * @resume: wiphy device needs to be resumed
* *
* @add_virtual_intf: create a new virtual interface with the given name, * @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. * @del_virtual_intf: remove the virtual interface determined by ifindex.
* *
...@@ -937,7 +938,7 @@ struct cfg80211_ops { ...@@ -937,7 +938,7 @@ struct cfg80211_ops {
int (*add_virtual_intf)(struct wiphy *wiphy, char *name, int (*add_virtual_intf)(struct wiphy *wiphy, char *name,
enum nl80211_iftype type, u32 *flags, enum nl80211_iftype type, u32 *flags,
struct vif_params *params); 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, int (*change_virtual_intf)(struct wiphy *wiphy,
struct net_device *dev, struct net_device *dev,
enum nl80211_iftype type, u32 *flags, enum nl80211_iftype type, u32 *flags,
...@@ -1088,6 +1089,9 @@ struct cfg80211_ops { ...@@ -1088,6 +1089,9 @@ struct cfg80211_ops {
* @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold); * @frag_threshold: Fragmentation threshold (dot11FragmentationThreshold);
* -1 = fragmentation disabled, only odd values >= 256 used * -1 = fragmentation disabled, only odd values >= 256 used
* @rts_threshold: RTS threshold (dot11RTSThreshold); -1 = RTS/CTS disabled * @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 { struct wiphy {
/* assign these fields before you register the wiphy */ /* assign these fields before you register the wiphy */
...@@ -1101,6 +1105,8 @@ struct wiphy { ...@@ -1101,6 +1105,8 @@ struct wiphy {
bool custom_regulatory; bool custom_regulatory;
bool strict_regulatory; bool strict_regulatory;
bool netnsok;
enum cfg80211_signal_type signal_type; enum cfg80211_signal_type signal_type;
int bss_priv_size; int bss_priv_size;
...@@ -1139,9 +1145,35 @@ struct wiphy { ...@@ -1139,9 +1145,35 @@ struct wiphy {
/* dir in debugfs: ieee80211/<wiphyname> */ /* dir in debugfs: ieee80211/<wiphyname> */
struct dentry *debugfsdir; 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))); 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 * wiphy_priv - return priv from wiphy
* *
......
...@@ -5344,6 +5344,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char ...@@ -5344,6 +5344,7 @@ int dev_change_net_namespace(struct net_device *dev, struct net *net, const char
out: out:
return err; return err;
} }
EXPORT_SYMBOL_GPL(dev_change_net_namespace);
static int dev_cpu_callback(struct notifier_block *nfb, static int dev_cpu_callback(struct notifier_block *nfb,
unsigned long action, unsigned long action,
......
...@@ -57,19 +57,9 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name, ...@@ -57,19 +57,9 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name,
return 0; 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; ieee80211_if_remove(IEEE80211_DEV_TO_SUB_IF(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);
return 0; return 0;
} }
......
...@@ -106,7 +106,7 @@ __cfg80211_rdev_from_info(struct genl_info *info) ...@@ -106,7 +106,7 @@ __cfg80211_rdev_from_info(struct genl_info *info)
if (info->attrs[NL80211_ATTR_IFINDEX]) { if (info->attrs[NL80211_ATTR_IFINDEX]) {
ifindex = nla_get_u32(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) {
if (dev->ieee80211_ptr) if (dev->ieee80211_ptr)
byifidx = byifidx =
...@@ -151,13 +151,13 @@ cfg80211_get_dev_from_info(struct genl_info *info) ...@@ -151,13 +151,13 @@ cfg80211_get_dev_from_info(struct genl_info *info)
} }
struct cfg80211_registered_device * 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 cfg80211_registered_device *rdev = ERR_PTR(-ENODEV);
struct net_device *dev; struct net_device *dev;
mutex_lock(&cfg80211_mutex); mutex_lock(&cfg80211_mutex);
dev = dev_get_by_index(&init_net, ifindex); dev = dev_get_by_index(net, ifindex);
if (!dev) if (!dev)
goto out; goto out;
if (dev->ieee80211_ptr) { if (dev->ieee80211_ptr) {
...@@ -222,6 +222,42 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev, ...@@ -222,6 +222,42 @@ int cfg80211_dev_rename(struct cfg80211_registered_device *rdev,
return 0; 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) static void cfg80211_rfkill_poll(struct rfkill *rfkill, void *data)
{ {
struct cfg80211_registered_device *rdev = data; struct cfg80211_registered_device *rdev = data;
...@@ -375,6 +411,8 @@ struct wiphy *wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv) ...@@ -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.class = &ieee80211_class;
rdev->wiphy.dev.platform_data = rdev; rdev->wiphy.dev.platform_data = rdev;
wiphy_net_set(&rdev->wiphy, &init_net);
rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block; rdev->rfkill_ops.set_block = cfg80211_rfkill_set_block;
rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev), rdev->rfkill = rfkill_alloc(dev_name(&rdev->wiphy.dev),
&rdev->wiphy.dev, RFKILL_TYPE_WLAN, &rdev->wiphy.dev, RFKILL_TYPE_WLAN,
...@@ -615,6 +653,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, ...@@ -615,6 +653,9 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
spin_lock_init(&wdev->event_lock); spin_lock_init(&wdev->event_lock);
mutex_lock(&rdev->devlist_mtx); mutex_lock(&rdev->devlist_mtx);
list_add(&wdev->list, &rdev->netdev_list); 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, if (sysfs_create_link(&dev->dev.kobj, &rdev->wiphy.dev.kobj,
"phy80211")) { "phy80211")) {
printk(KERN_ERR "wireless: failed to add phy80211 " printk(KERN_ERR "wireless: failed to add phy80211 "
...@@ -705,10 +746,32 @@ static struct notifier_block cfg80211_netdev_notifier = { ...@@ -705,10 +746,32 @@ static struct notifier_block cfg80211_netdev_notifier = {
.notifier_call = cfg80211_netdev_notifier_call, .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; int err;
err = register_pernet_device(&cfg80211_pernet_ops);
if (err)
goto out_fail_pernet;
err = wiphy_sysfs_init(); err = wiphy_sysfs_init();
if (err) if (err)
goto out_fail_sysfs; goto out_fail_sysfs;
...@@ -736,9 +799,10 @@ out_fail_nl80211: ...@@ -736,9 +799,10 @@ out_fail_nl80211:
out_fail_notifier: out_fail_notifier:
wiphy_sysfs_exit(); wiphy_sysfs_exit();
out_fail_sysfs: out_fail_sysfs:
unregister_pernet_device(&cfg80211_pernet_ops);
out_fail_pernet:
return err; return err;
} }
subsys_initcall(cfg80211_init); subsys_initcall(cfg80211_init);
static void cfg80211_exit(void) static void cfg80211_exit(void)
...@@ -748,5 +812,6 @@ static void cfg80211_exit(void) ...@@ -748,5 +812,6 @@ static void cfg80211_exit(void)
unregister_netdevice_notifier(&cfg80211_netdev_notifier); unregister_netdevice_notifier(&cfg80211_netdev_notifier);
wiphy_sysfs_exit(); wiphy_sysfs_exit();
regulatory_exit(); regulatory_exit();
unregister_pernet_device(&cfg80211_pernet_ops);
} }
module_exit(cfg80211_exit); module_exit(cfg80211_exit);
...@@ -170,7 +170,10 @@ struct wiphy *wiphy_idx_to_wiphy(int wiphy_idx); ...@@ -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 */ /* identical to cfg80211_get_dev_from_info but only operate on ifindex */
extern struct cfg80211_registered_device * 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) static inline void cfg80211_lock_rdev(struct cfg80211_registered_device *rdev)
{ {
......
This diff is collapsed.
...@@ -32,9 +32,7 @@ void __cfg80211_scan_done(struct work_struct *wk) ...@@ -32,9 +32,7 @@ void __cfg80211_scan_done(struct work_struct *wk)
mutex_lock(&rdev->mtx); mutex_lock(&rdev->mtx);
request = rdev->scan_req; request = rdev->scan_req;
dev = dev_get_by_index(&init_net, request->ifidx); dev = request->dev;
if (!dev)
goto out;
/* /*
* This must be before sending the other events! * This must be before sending the other events!
...@@ -58,7 +56,6 @@ void __cfg80211_scan_done(struct work_struct *wk) ...@@ -58,7 +56,6 @@ void __cfg80211_scan_done(struct work_struct *wk)
dev_put(dev); dev_put(dev);
out:
cfg80211_unlock_rdev(rdev); cfg80211_unlock_rdev(rdev);
wiphy_to_dev(request->wiphy)->scan_req = NULL; wiphy_to_dev(request->wiphy)->scan_req = NULL;
kfree(request); kfree(request);
...@@ -66,17 +63,10 @@ void __cfg80211_scan_done(struct work_struct *wk) ...@@ -66,17 +63,10 @@ void __cfg80211_scan_done(struct work_struct *wk)
void cfg80211_scan_done(struct cfg80211_scan_request *request, bool aborted) 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); WARN_ON(request != wiphy_to_dev(request->wiphy)->scan_req);
request->aborted = aborted; request->aborted = aborted;
schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk); schedule_work(&wiphy_to_dev(request->wiphy)->scan_done_wk);
dev_put(dev);
} }
EXPORT_SYMBOL(cfg80211_scan_done); EXPORT_SYMBOL(cfg80211_scan_done);
...@@ -592,7 +582,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, ...@@ -592,7 +582,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
if (!netif_running(dev)) if (!netif_running(dev))
return -ENETDOWN; 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)) if (IS_ERR(rdev))
return PTR_ERR(rdev); return PTR_ERR(rdev);
...@@ -617,7 +607,7 @@ int cfg80211_wext_siwscan(struct net_device *dev, ...@@ -617,7 +607,7 @@ int cfg80211_wext_siwscan(struct net_device *dev,
} }
creq->wiphy = wiphy; creq->wiphy = wiphy;
creq->ifidx = dev->ifindex; creq->dev = dev;
creq->ssids = (void *)(creq + 1); creq->ssids = (void *)(creq + 1);
creq->channels = (void *)(creq->ssids + 1); creq->channels = (void *)(creq->ssids + 1);
creq->n_channels = n_channels; creq->n_channels = n_channels;
...@@ -654,8 +644,10 @@ int cfg80211_wext_siwscan(struct net_device *dev, ...@@ -654,8 +644,10 @@ int cfg80211_wext_siwscan(struct net_device *dev,
if (err) { if (err) {
rdev->scan_req = NULL; rdev->scan_req = NULL;
kfree(creq); kfree(creq);
} else } else {
nl80211_send_scan_start(rdev, dev); nl80211_send_scan_start(rdev, dev);
dev_hold(dev);
}
out: out:
cfg80211_unlock_rdev(rdev); cfg80211_unlock_rdev(rdev);
return err; return err;
...@@ -948,7 +940,7 @@ int cfg80211_wext_giwscan(struct net_device *dev, ...@@ -948,7 +940,7 @@ int cfg80211_wext_giwscan(struct net_device *dev,
if (!netif_running(dev)) if (!netif_running(dev))
return -ENETDOWN; 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)) if (IS_ERR(rdev))
return PTR_ERR(rdev); return PTR_ERR(rdev);
......
...@@ -86,7 +86,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) ...@@ -86,7 +86,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
wdev->conn->params.ssid_len); wdev->conn->params.ssid_len);
request->ssids[0].ssid_len = 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; request->wiphy = &rdev->wiphy;
rdev->scan_req = request; rdev->scan_req = request;
...@@ -95,6 +95,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev) ...@@ -95,6 +95,7 @@ static int cfg80211_conn_scan(struct wireless_dev *wdev)
if (!err) { if (!err) {
wdev->conn->state = CFG80211_CONN_SCANNING; wdev->conn->state = CFG80211_CONN_SCANNING;
nl80211_send_scan_start(rdev, wdev->netdev); nl80211_send_scan_start(rdev, wdev->netdev);
dev_hold(wdev->netdev);
} else { } else {
rdev->scan_req = NULL; rdev->scan_req = NULL;
kfree(request); kfree(request);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment