Skip to content

Commit

Permalink
mac80211_hwsim: Allow managing radios from non-initial namespaces
Browse files Browse the repository at this point in the history
While wiphys can be moved into network namespaces over nl80211, the
creation and removal of hwsim radios is currently limited to the initial
namespace. This patch allows management of namespaced radios from the
owning namespace by setting genetlink netnsok.

To prevent two arbitrary namespaces to communicate over the simulated
shared medium, radios are separated by netgroups. Each radio created in
the same namespace lives in the same netgroup and hence can communicate
with other radios in that group. When moving radios to other namespaces,
the netgroup is preserved, so two radios having the same netgroup can
communicate even if not in the same namespace; This allows a controlling
namespace to create radios and move them to other namespaces for
communication.

When a net namespace owning a radio exits, the radio is destroyed unless
it was created in the initial network namespace. This keeps the previous
behavior by returning them to the init namespace, but prevents unprivileged
users from creating radios in the initial namespace.

Signed-off-by: Martin Willi <martin@strongswan.org>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Martin Willi authored and Johannes Berg committed May 31, 2016
1 parent 5617c6c commit 100cb9f
Showing 1 changed file with 94 additions and 3 deletions.
97 changes: 94 additions & 3 deletions drivers/net/wireless/mac80211_hwsim.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
#include <linux/module.h>
#include <linux/ktime.h>
#include <net/genetlink.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include "mac80211_hwsim.h"

#define WARN_QUEUE 100
Expand Down Expand Up @@ -250,6 +252,28 @@ static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c)
cp->magic = 0;
}

static unsigned int hwsim_net_id;

static int hwsim_netgroup;

struct hwsim_net {
int netgroup;
};

static inline int hwsim_net_get_netgroup(struct net *net)
{
struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);

return hwsim_net->netgroup;
}

static inline void hwsim_net_set_netgroup(struct net *net)
{
struct hwsim_net *hwsim_net = net_generic(net, hwsim_net_id);

hwsim_net->netgroup = hwsim_netgroup++;
}

static struct class *hwsim_class;

static struct net_device *hwsim_mon; /* global monitor netdev */
Expand Down Expand Up @@ -526,6 +550,9 @@ struct mac80211_hwsim_data {
*/
u64 group;

/* group shared by radios created in the same netns */
int netgroup;

int power_level;

/* difference between this hw's clock and the real clock, in usecs */
Expand Down Expand Up @@ -568,6 +595,7 @@ static struct genl_family hwsim_genl_family = {
.name = "MAC80211_HWSIM",
.version = 1,
.maxattr = HWSIM_ATTR_MAX,
.netnsok = true,
};

enum hwsim_multicast_groups {
Expand Down Expand Up @@ -1202,6 +1230,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
if (!(data->group & data2->group))
continue;

if (data->netgroup != data2->netgroup)
continue;

if (!hwsim_chans_compat(chan, data2->tmp_chan) &&
!hwsim_chans_compat(chan, data2->channel)) {
ieee80211_iterate_active_interfaces_atomic(
Expand Down Expand Up @@ -2349,6 +2380,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
struct ieee80211_hw *hw;
enum nl80211_band band;
const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
struct net *net;
int idx;

if (WARN_ON(param->channels > 1 && !param->use_chanctx))
Expand All @@ -2366,6 +2398,13 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
err = -ENOMEM;
goto failed;
}

if (info)
net = genl_info_net(info);
else
net = &init_net;
wiphy_net_set(hw->wiphy, net);

data = hw->priv;
data->hw = hw;

Expand Down Expand Up @@ -2541,6 +2580,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
data->group = 1;
mutex_init(&data->mutex);

data->netgroup = hwsim_net_get_netgroup(net);

/* Enable frame retransmissions for lossy channels */
hw->max_rates = 4;
hw->max_rate_tries = 11;
Expand Down Expand Up @@ -3013,6 +3054,9 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
continue;
}

if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info)))
continue;

list_del(&data->list);
spin_unlock_bh(&hwsim_radio_lock);
mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
Expand All @@ -3039,6 +3083,9 @@ static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
if (data->idx != idx)
continue;

if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info)))
continue;

skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb) {
res = -ENOMEM;
Expand Down Expand Up @@ -3078,6 +3125,9 @@ static int hwsim_dump_radio_nl(struct sk_buff *skb,
if (data->idx < idx)
continue;

if (!net_eq(wiphy_net(data->hw->wiphy), sock_net(skb->sk)))
continue;

res = mac80211_hwsim_get_radio(skb, data,
NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, cb,
Expand Down Expand Up @@ -3117,13 +3167,13 @@ static const struct genl_ops hwsim_ops[] = {
.cmd = HWSIM_CMD_NEW_RADIO,
.policy = hwsim_genl_policy,
.doit = hwsim_new_radio_nl,
.flags = GENL_ADMIN_PERM,
.flags = GENL_UNS_ADMIN_PERM,
},
{
.cmd = HWSIM_CMD_DEL_RADIO,
.policy = hwsim_genl_policy,
.doit = hwsim_del_radio_nl,
.flags = GENL_ADMIN_PERM,
.flags = GENL_UNS_ADMIN_PERM,
},
{
.cmd = HWSIM_CMD_GET_RADIO,
Expand Down Expand Up @@ -3205,6 +3255,40 @@ static int hwsim_init_netlink(void)
return -EINVAL;
}

static __net_init int hwsim_init_net(struct net *net)
{
hwsim_net_set_netgroup(net);

return 0;
}

static void __net_exit hwsim_exit_net(struct net *net)
{
struct mac80211_hwsim_data *data, *tmp;

spin_lock_bh(&hwsim_radio_lock);
list_for_each_entry_safe(data, tmp, &hwsim_radios, list) {
if (!net_eq(wiphy_net(data->hw->wiphy), net))
continue;

/* Radios created in init_net are returned to init_net. */
if (data->netgroup == hwsim_net_get_netgroup(&init_net))
continue;

list_del(&data->list);
INIT_WORK(&data->destroy_work, destroy_radio);
schedule_work(&data->destroy_work);
}
spin_unlock_bh(&hwsim_radio_lock);
}

static struct pernet_operations hwsim_net_ops = {
.init = hwsim_init_net,
.exit = hwsim_exit_net,
.id = &hwsim_net_id,
.size = sizeof(struct hwsim_net),
};

static void hwsim_exit_netlink(void)
{
/* unregister the notifier */
Expand Down Expand Up @@ -3241,10 +3325,14 @@ static int __init init_mac80211_hwsim(void)
spin_lock_init(&hwsim_radio_lock);
INIT_LIST_HEAD(&hwsim_radios);

err = platform_driver_register(&mac80211_hwsim_driver);
err = register_pernet_device(&hwsim_net_ops);
if (err)
return err;

err = platform_driver_register(&mac80211_hwsim_driver);
if (err)
goto out_unregister_pernet;

hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
if (IS_ERR(hwsim_class)) {
err = PTR_ERR(hwsim_class);
Expand Down Expand Up @@ -3362,6 +3450,8 @@ static int __init init_mac80211_hwsim(void)
mac80211_hwsim_free();
out_unregister_driver:
platform_driver_unregister(&mac80211_hwsim_driver);
out_unregister_pernet:
unregister_pernet_device(&hwsim_net_ops);
return err;
}
module_init(init_mac80211_hwsim);
Expand All @@ -3375,5 +3465,6 @@ static void __exit exit_mac80211_hwsim(void)
mac80211_hwsim_free();
unregister_netdev(hwsim_mon);
platform_driver_unregister(&mac80211_hwsim_driver);
unregister_pernet_device(&hwsim_net_ops);
}
module_exit(exit_mac80211_hwsim);

0 comments on commit 100cb9f

Please sign in to comment.