Commit 100cb9ff authored by Martin Willi's avatar Martin Willi Committed by Johannes Berg

mac80211_hwsim: Allow managing radios from non-initial namespaces

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: default avatarMartin Willi <martin@strongswan.org>
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 5617c6cd
...@@ -30,6 +30,8 @@ ...@@ -30,6 +30,8 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/ktime.h> #include <linux/ktime.h>
#include <net/genetlink.h> #include <net/genetlink.h>
#include <net/net_namespace.h>
#include <net/netns/generic.h>
#include "mac80211_hwsim.h" #include "mac80211_hwsim.h"
#define WARN_QUEUE 100 #define WARN_QUEUE 100
...@@ -250,6 +252,28 @@ static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c) ...@@ -250,6 +252,28 @@ static inline void hwsim_clear_chanctx_magic(struct ieee80211_chanctx_conf *c)
cp->magic = 0; 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 class *hwsim_class;
static struct net_device *hwsim_mon; /* global monitor netdev */ static struct net_device *hwsim_mon; /* global monitor netdev */
...@@ -526,6 +550,9 @@ struct mac80211_hwsim_data { ...@@ -526,6 +550,9 @@ struct mac80211_hwsim_data {
*/ */
u64 group; u64 group;
/* group shared by radios created in the same netns */
int netgroup;
int power_level; int power_level;
/* difference between this hw's clock and the real clock, in usecs */ /* difference between this hw's clock and the real clock, in usecs */
...@@ -568,6 +595,7 @@ static struct genl_family hwsim_genl_family = { ...@@ -568,6 +595,7 @@ static struct genl_family hwsim_genl_family = {
.name = "MAC80211_HWSIM", .name = "MAC80211_HWSIM",
.version = 1, .version = 1,
.maxattr = HWSIM_ATTR_MAX, .maxattr = HWSIM_ATTR_MAX,
.netnsok = true,
}; };
enum hwsim_multicast_groups { enum hwsim_multicast_groups {
...@@ -1202,6 +1230,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw, ...@@ -1202,6 +1230,9 @@ static bool mac80211_hwsim_tx_frame_no_nl(struct ieee80211_hw *hw,
if (!(data->group & data2->group)) if (!(data->group & data2->group))
continue; continue;
if (data->netgroup != data2->netgroup)
continue;
if (!hwsim_chans_compat(chan, data2->tmp_chan) && if (!hwsim_chans_compat(chan, data2->tmp_chan) &&
!hwsim_chans_compat(chan, data2->channel)) { !hwsim_chans_compat(chan, data2->channel)) {
ieee80211_iterate_active_interfaces_atomic( ieee80211_iterate_active_interfaces_atomic(
...@@ -2349,6 +2380,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ...@@ -2349,6 +2380,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
struct ieee80211_hw *hw; struct ieee80211_hw *hw;
enum nl80211_band band; enum nl80211_band band;
const struct ieee80211_ops *ops = &mac80211_hwsim_ops; const struct ieee80211_ops *ops = &mac80211_hwsim_ops;
struct net *net;
int idx; int idx;
if (WARN_ON(param->channels > 1 && !param->use_chanctx)) if (WARN_ON(param->channels > 1 && !param->use_chanctx))
...@@ -2366,6 +2398,13 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ...@@ -2366,6 +2398,13 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
err = -ENOMEM; err = -ENOMEM;
goto failed; goto failed;
} }
if (info)
net = genl_info_net(info);
else
net = &init_net;
wiphy_net_set(hw->wiphy, net);
data = hw->priv; data = hw->priv;
data->hw = hw; data->hw = hw;
...@@ -2541,6 +2580,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ...@@ -2541,6 +2580,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info,
data->group = 1; data->group = 1;
mutex_init(&data->mutex); mutex_init(&data->mutex);
data->netgroup = hwsim_net_get_netgroup(net);
/* Enable frame retransmissions for lossy channels */ /* Enable frame retransmissions for lossy channels */
hw->max_rates = 4; hw->max_rates = 4;
hw->max_rate_tries = 11; hw->max_rate_tries = 11;
...@@ -3013,6 +3054,9 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info) ...@@ -3013,6 +3054,9 @@ static int hwsim_del_radio_nl(struct sk_buff *msg, struct genl_info *info)
continue; continue;
} }
if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info)))
continue;
list_del(&data->list); list_del(&data->list);
spin_unlock_bh(&hwsim_radio_lock); spin_unlock_bh(&hwsim_radio_lock);
mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy), mac80211_hwsim_del_radio(data, wiphy_name(data->hw->wiphy),
...@@ -3039,6 +3083,9 @@ static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info) ...@@ -3039,6 +3083,9 @@ static int hwsim_get_radio_nl(struct sk_buff *msg, struct genl_info *info)
if (data->idx != idx) if (data->idx != idx)
continue; continue;
if (!net_eq(wiphy_net(data->hw->wiphy), genl_info_net(info)))
continue;
skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); skb = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb) { if (!skb) {
res = -ENOMEM; res = -ENOMEM;
...@@ -3078,6 +3125,9 @@ static int hwsim_dump_radio_nl(struct sk_buff *skb, ...@@ -3078,6 +3125,9 @@ static int hwsim_dump_radio_nl(struct sk_buff *skb,
if (data->idx < idx) if (data->idx < idx)
continue; continue;
if (!net_eq(wiphy_net(data->hw->wiphy), sock_net(skb->sk)))
continue;
res = mac80211_hwsim_get_radio(skb, data, res = mac80211_hwsim_get_radio(skb, data,
NETLINK_CB(cb->skb).portid, NETLINK_CB(cb->skb).portid,
cb->nlh->nlmsg_seq, cb, cb->nlh->nlmsg_seq, cb,
...@@ -3117,13 +3167,13 @@ static const struct genl_ops hwsim_ops[] = { ...@@ -3117,13 +3167,13 @@ static const struct genl_ops hwsim_ops[] = {
.cmd = HWSIM_CMD_NEW_RADIO, .cmd = HWSIM_CMD_NEW_RADIO,
.policy = hwsim_genl_policy, .policy = hwsim_genl_policy,
.doit = hwsim_new_radio_nl, .doit = hwsim_new_radio_nl,
.flags = GENL_ADMIN_PERM, .flags = GENL_UNS_ADMIN_PERM,
}, },
{ {
.cmd = HWSIM_CMD_DEL_RADIO, .cmd = HWSIM_CMD_DEL_RADIO,
.policy = hwsim_genl_policy, .policy = hwsim_genl_policy,
.doit = hwsim_del_radio_nl, .doit = hwsim_del_radio_nl,
.flags = GENL_ADMIN_PERM, .flags = GENL_UNS_ADMIN_PERM,
}, },
{ {
.cmd = HWSIM_CMD_GET_RADIO, .cmd = HWSIM_CMD_GET_RADIO,
...@@ -3205,6 +3255,40 @@ static int hwsim_init_netlink(void) ...@@ -3205,6 +3255,40 @@ static int hwsim_init_netlink(void)
return -EINVAL; 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) static void hwsim_exit_netlink(void)
{ {
/* unregister the notifier */ /* unregister the notifier */
...@@ -3241,10 +3325,14 @@ static int __init init_mac80211_hwsim(void) ...@@ -3241,10 +3325,14 @@ static int __init init_mac80211_hwsim(void)
spin_lock_init(&hwsim_radio_lock); spin_lock_init(&hwsim_radio_lock);
INIT_LIST_HEAD(&hwsim_radios); INIT_LIST_HEAD(&hwsim_radios);
err = platform_driver_register(&mac80211_hwsim_driver); err = register_pernet_device(&hwsim_net_ops);
if (err) if (err)
return err; return err;
err = platform_driver_register(&mac80211_hwsim_driver);
if (err)
goto out_unregister_pernet;
hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim"); hwsim_class = class_create(THIS_MODULE, "mac80211_hwsim");
if (IS_ERR(hwsim_class)) { if (IS_ERR(hwsim_class)) {
err = PTR_ERR(hwsim_class); err = PTR_ERR(hwsim_class);
...@@ -3362,6 +3450,8 @@ static int __init init_mac80211_hwsim(void) ...@@ -3362,6 +3450,8 @@ static int __init init_mac80211_hwsim(void)
mac80211_hwsim_free(); mac80211_hwsim_free();
out_unregister_driver: out_unregister_driver:
platform_driver_unregister(&mac80211_hwsim_driver); platform_driver_unregister(&mac80211_hwsim_driver);
out_unregister_pernet:
unregister_pernet_device(&hwsim_net_ops);
return err; return err;
} }
module_init(init_mac80211_hwsim); module_init(init_mac80211_hwsim);
...@@ -3375,5 +3465,6 @@ static void __exit exit_mac80211_hwsim(void) ...@@ -3375,5 +3465,6 @@ static void __exit exit_mac80211_hwsim(void)
mac80211_hwsim_free(); mac80211_hwsim_free();
unregister_netdev(hwsim_mon); unregister_netdev(hwsim_mon);
platform_driver_unregister(&mac80211_hwsim_driver); platform_driver_unregister(&mac80211_hwsim_driver);
unregister_pernet_device(&hwsim_net_ops);
} }
module_exit(exit_mac80211_hwsim); module_exit(exit_mac80211_hwsim);
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