aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorJulian Orth <ju.orth@gmail.com>2018-09-11 18:52:27 +0200
committerJulian Orth <ju.orth@gmail.com>2018-12-15 17:19:05 +0100
commit5b31e577def99e0855d5fdbcc1a66e97fdd18688 (patch)
tree4d0275d47430f401506d801b4cc3a30a309d977c
parentnetlink: check for CAP_NET_ADMIN manually (diff)
downloadwireguard-monolithic-historical-5b31e577def99e0855d5fdbcc1a66e97fdd18688.tar.xz
wireguard-monolithic-historical-5b31e577def99e0855d5fdbcc1a66e97fdd18688.zip
netlink: allow specifying the device namespace
This commit adds two new attributes of which at most one may be provided: * WGDEVICE_A_DEV_NETNS_PID: NLA_U32 * WGDEVICE_A_DEV_NETNS_FD: NLA_U32 The Wireguard device is then looked up in this namespace instead of the namespace of the netlink socket.
-rw-r--r--src/netlink.c82
-rw-r--r--src/uapi/wireguard.h26
2 files changed, 87 insertions, 21 deletions
diff --git a/src/netlink.c b/src/netlink.c
index 364d4d8..96ad555 100644
--- a/src/netlink.c
+++ b/src/netlink.c
@@ -24,7 +24,9 @@ static const struct nla_policy device_policy[WGDEVICE_A_MAX + 1] = {
[WGDEVICE_A_FLAGS] = { .type = NLA_U32 },
[WGDEVICE_A_LISTEN_PORT] = { .type = NLA_U16 },
[WGDEVICE_A_FWMARK] = { .type = NLA_U32 },
- [WGDEVICE_A_PEERS] = { .type = NLA_NESTED }
+ [WGDEVICE_A_PEERS] = { .type = NLA_NESTED },
+ [WGDEVICE_A_DEV_NETNS_PID] = { .type = NLA_U32 },
+ [WGDEVICE_A_DEV_NETNS_FD] = { .type = NLA_U32 },
};
static const struct nla_policy peer_policy[WGPEER_A_MAX + 1] = {
@@ -47,17 +49,17 @@ static const struct nla_policy allowedip_policy[WGALLOWEDIP_A_MAX + 1] = {
};
static struct wg_device *lookup_interface(struct nlattr **attrs,
- struct sk_buff *skb)
+ struct net *net)
{
struct net_device *dev = NULL;
if (!attrs[WGDEVICE_A_IFINDEX] == !attrs[WGDEVICE_A_IFNAME])
return ERR_PTR(-EBADR);
if (attrs[WGDEVICE_A_IFINDEX])
- dev = dev_get_by_index(sock_net(skb->sk),
+ dev = dev_get_by_index(net,
nla_get_u32(attrs[WGDEVICE_A_IFINDEX]));
else if (attrs[WGDEVICE_A_IFNAME])
- dev = dev_get_by_name(sock_net(skb->sk),
+ dev = dev_get_by_name(net,
nla_data(attrs[WGDEVICE_A_IFNAME]));
if (!dev)
return ERR_PTR(-ENODEV);
@@ -160,10 +162,22 @@ err:
return -EMSGSIZE;
}
+static struct net *get_attr_net(struct nlattr *net_pid, struct nlattr *net_fd)
+{
+ if (net_pid && net_fd)
+ return ERR_PTR(-EINVAL);
+ if (net_pid)
+ return get_net_ns_by_pid(nla_get_u32(net_pid));
+ if (net_fd)
+ return get_net_ns_by_fd(nla_get_u32(net_fd));
+ return NULL;
+}
+
static int wg_get_device_start(struct netlink_callback *cb)
{
struct nlattr **attrs = genl_family_attrbuf(&genl_family);
- struct net *dev_net = sock_net(cb->skb->sk);
+ struct net *owned_dev_net = NULL, *dev_net;
+ struct allowedips_cursor *cursor = NULL;
struct wg_device *wg;
int ret;
@@ -171,20 +185,42 @@ static int wg_get_device_start(struct netlink_callback *cb)
genl_family.maxattr, device_policy, NULL);
if (ret < 0)
return ret;
- if (!netlink_ns_capable(cb->skb, dev_net->user_ns, CAP_NET_ADMIN))
- return -EPERM;
- cb->args[2] = (long)kzalloc(sizeof(struct allowedips_cursor),
- GFP_KERNEL);
- if (unlikely(!cb->args[2]))
- return -ENOMEM;
- wg = lookup_interface(attrs, cb->skb);
+
+ owned_dev_net = get_attr_net(attrs[WGDEVICE_A_DEV_NETNS_PID],
+ attrs[WGDEVICE_A_DEV_NETNS_FD]);
+ if (IS_ERR(owned_dev_net)) {
+ ret = PTR_ERR(owned_dev_net);
+ owned_dev_net = NULL;
+ goto out;
+ }
+ dev_net = owned_dev_net ? : sock_net(cb->skb->sk);
+ if (!netlink_ns_capable(cb->skb, dev_net->user_ns, CAP_NET_ADMIN)) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ cursor = kzalloc(sizeof(*cursor), GFP_KERNEL);
+ if (unlikely(!cursor)) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ wg = lookup_interface(attrs, dev_net);
if (IS_ERR(wg)) {
- kfree((void *)cb->args[2]);
- cb->args[2] = 0;
- return PTR_ERR(wg);
+ ret = PTR_ERR(wg);
+ goto out;
}
+
cb->args[0] = (long)wg;
- return 0;
+ cb->args[2] = (long)cursor;
+ cursor = NULL;
+
+out:
+ if (cursor)
+ kfree(cursor);
+ if (owned_dev_net)
+ put_net(owned_dev_net);
+ return ret;
}
static int wg_get_device_dump(struct sk_buff *skb, struct netlink_callback *cb)
@@ -473,16 +509,23 @@ out:
static int wg_set_device(struct sk_buff *skb, struct genl_info *info)
{
- struct net *dev_net = sock_net(skb->sk);
+ struct net *owned_dev_net, *dev_net;
struct wg_device *wg;
int ret;
+ owned_dev_net = get_attr_net(info->attrs[WGDEVICE_A_DEV_NETNS_PID],
+ info->attrs[WGDEVICE_A_DEV_NETNS_FD]);
+ if (IS_ERR(owned_dev_net)) {
+ ret = PTR_ERR(owned_dev_net);
+ goto out_nonet;
+ }
+ dev_net = owned_dev_net ? : sock_net(skb->sk);
if (!netlink_ns_capable(skb, dev_net->user_ns, CAP_NET_ADMIN)) {
ret = -EPERM;
goto out_nodev;
}
- wg = lookup_interface(info->attrs, skb);
+ wg = lookup_interface(info->attrs, dev_net);
if (IS_ERR(wg)) {
ret = PTR_ERR(wg);
@@ -565,6 +608,9 @@ out:
rtnl_unlock();
dev_put(wg->dev);
out_nodev:
+ if (owned_dev_net)
+ put_net(owned_dev_net);
+out_nonet:
if (info->attrs[WGDEVICE_A_PRIVATE_KEY])
memzero_explicit(nla_data(info->attrs[WGDEVICE_A_PRIVATE_KEY]),
nla_len(info->attrs[WGDEVICE_A_PRIVATE_KEY]));
diff --git a/src/uapi/wireguard.h b/src/uapi/wireguard.h
index 0203f2c..bcfcf4f 100644
--- a/src/uapi/wireguard.h
+++ b/src/uapi/wireguard.h
@@ -20,6 +20,16 @@
* WGDEVICE_A_IFINDEX: NLA_U32
* WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
*
+ * At most one of the following may be provided:
+ *
+ * WGDEVICE_A_DEV_NETNS_PID: NLA_U32
+ * WGDEVICE_A_DEV_NETNS_FD: NLA_U32
+ *
+ * If they are provided, the Wireguard device will be looked up in this network
+ * namespace. Otherwise it is looked up in the network namespace of the netlink
+ * socket. The caller must have CAP_NET_ADMIN in the namespace of the Wireguard
+ * device.
+ *
* The kernel will then return several messages (NLM_F_MULTI) containing the
* following tree of nested items:
*
@@ -72,9 +82,15 @@
* WG_CMD_SET_DEVICE
* -----------------
*
- * May only be called via NLM_F_REQUEST. The command should contain the
- * following tree of nested items, containing one but not both of
- * WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME:
+ * May only be called via NLM_F_REQUEST. The command must contain the following
+ * tree of nested items. Exactly one of WGDEVICE_A_IFINDEX and WGDEVICE_A_IFNAME
+ * must be provided. All other top-level items are optional. At most one of
+ * WGDEVICE_A_DEV_NETNS_PID and WGDEVICE_A_DEV_NETNS_FD may be provided.
+ *
+ * If WGDEVICE_A_DEV_NETNS_PID/FD is provided, the Wireguard device is looked up
+ * in this network namespace. Otherwise it is looked up in the network namespace
+ * of the netlink socket. The caller must have CAP_NET_ADMIN in the namespace of
+ * the Wireguard device.
*
* WGDEVICE_A_IFINDEX: NLA_U32
* WGDEVICE_A_IFNAME: NLA_NUL_STRING, maxlen IFNAMESIZ - 1
@@ -82,6 +98,8 @@
* peers should be removed prior to adding the list below.
* WGDEVICE_A_PRIVATE_KEY: len WG_KEY_LEN, all zeros to remove
* WGDEVICE_A_LISTEN_PORT: NLA_U16, 0 to choose randomly
+ * WGDEVICE_A_DEV_NETNS_PID: NLA_U32
+ * WGDEVICE_A_DEV_NETNS_FD: NLA_U32
* WGDEVICE_A_FWMARK: NLA_U32, 0 to disable
* WGDEVICE_A_PEERS: NLA_NESTED
* 0: NLA_NESTED
@@ -154,6 +172,8 @@ enum wgdevice_attribute {
WGDEVICE_A_LISTEN_PORT,
WGDEVICE_A_FWMARK,
WGDEVICE_A_PEERS,
+ WGDEVICE_A_DEV_NETNS_PID,
+ WGDEVICE_A_DEV_NETNS_FD,
__WGDEVICE_A_LAST
};
#define WGDEVICE_A_MAX (__WGDEVICE_A_LAST - 1)