[PATCH v3 10/12] tools: allow specifying the device namespace
Julian Orth
ju.orth at gmail.com
Tue Sep 11 21:13:09 CEST 2018
The user can now use
wg --netns <pid|file-path> <subcommand>
to specify the network namespace in which wg should act. This sets the
attribute WGDEVICE_A_DEV_NETNS_PID or WGDEVICE_A_DEV_NETNS_FD.
In the case of
wg --netns <pid|file-path> show all
we have to try to enter the network namespace because the kernel
interface does not allow us to list devices in a network namespace
referenced by pid or fd. Since entering a network namespace requires
CAP_SYS_ADMIN in the current user namespace and the target user
namespace, this is almost useless. TODO: Add the missing functionality
to the kernel.
---
src/tools/containers.h | 14 ++++++++++
src/tools/ipc.c | 22 +++++++++++----
src/tools/ipc.h | 7 +++--
src/tools/netns.c | 62 ++++++++++++++++++++++++++++++++++++++++++
src/tools/netns.h | 18 ++++++++++++
src/tools/set.c | 3 +-
src/tools/setconf.c | 3 +-
src/tools/show.c | 36 +++++++++++++++++-------
src/tools/showconf.c | 3 +-
src/tools/wg.c | 14 ++++++++--
10 files changed, 155 insertions(+), 27 deletions(-)
create mode 100644 src/tools/netns.c
create mode 100644 src/tools/netns.h
diff --git a/src/tools/containers.h b/src/tools/containers.h
index 8142d65..326071b 100644
--- a/src/tools/containers.h
+++ b/src/tools/containers.h
@@ -15,7 +15,21 @@
#include "../uapi/wireguard.h"
+struct wgnetns {
+ uint32_t flags;
+
+ uint32_t pid;
+ int fd;
+};
+
+enum {
+ WGNETNS_HAS_PID = 1U << 0,
+ WGNETNS_HAS_FD = 1U << 1,
+};
+
+
struct wgoptions {
+ struct wgnetns dev_netns;
};
struct wgallowedip {
diff --git a/src/tools/ipc.c b/src/tools/ipc.c
index e3ef789..6e0b4e7 100644
--- a/src/tools/ipc.c
+++ b/src/tools/ipc.c
@@ -544,7 +544,7 @@ cleanup:
return ret;
}
-static int kernel_set_device(struct wgdevice *dev)
+static int kernel_set_device(struct wgnetns *dev_netns, struct wgdevice *dev)
{
int ret = 0;
size_t i, j;
@@ -573,6 +573,10 @@ again:
mnl_attr_put_u32(nlh, WGDEVICE_A_FWMARK, dev->fwmark);
if (dev->flags & WGDEVICE_REPLACE_PEERS)
flags |= WGDEVICE_F_REPLACE_PEERS;
+ if (dev_netns->flags & WGNETNS_HAS_PID)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_DEV_NETNS_PID, dev_netns->pid);
+ if (dev_netns->flags & WGNETNS_HAS_FD)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_DEV_NETNS_FD, (uint32_t)dev_netns->fd);
if (flags)
mnl_attr_put_u32(nlh, WGDEVICE_A_FLAGS, flags);
}
@@ -879,7 +883,8 @@ static void coalesce_peers(struct wgdevice *device)
}
}
-static int kernel_get_device(struct wgdevice **device, const char *interface)
+static int kernel_get_device(struct wgnetns *dev_netns,
+ struct wgdevice **device, const char *interface)
{
int ret = 0;
struct nlmsghdr *nlh;
@@ -899,6 +904,10 @@ try_again:
nlh = mnlg_msg_prepare(nlg, WG_CMD_GET_DEVICE, NLM_F_REQUEST | NLM_F_ACK | NLM_F_DUMP);
mnl_attr_put_strz(nlh, WGDEVICE_A_IFNAME, interface);
+ if (dev_netns->flags & WGNETNS_HAS_PID)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_DEV_NETNS_PID, dev_netns->pid);
+ if (dev_netns->flags & WGNETNS_HAS_FD)
+ mnl_attr_put_u32(nlh, WGDEVICE_A_DEV_NETNS_FD, (uint32_t)dev_netns->fd);
if (mnlg_socket_send(nlg, nlh) < 0) {
ret = -errno;
goto out;
@@ -953,23 +962,24 @@ cleanup:
return buffer.buffer;
}
-int ipc_get_device(struct wgdevice **dev, const char *interface)
+int ipc_get_device(struct wgnetns *dev_netns, struct wgdevice **dev,
+ const char *interface)
{
#ifdef __linux__
if (userspace_has_wireguard_interface(interface))
return userspace_get_device(dev, interface);
- return kernel_get_device(dev, interface);
+ return kernel_get_device(dev_netns, dev, interface);
#else
return userspace_get_device(dev, interface);
#endif
}
-int ipc_set_device(struct wgdevice *dev)
+int ipc_set_device(struct wgnetns *dev_netns, struct wgdevice *dev)
{
#ifdef __linux__
if (userspace_has_wireguard_interface(dev->name))
return userspace_set_device(dev);
- return kernel_set_device(dev);
+ return kernel_set_device(dev_netns, dev);
#else
return userspace_set_device(dev);
#endif
diff --git a/src/tools/ipc.h b/src/tools/ipc.h
index 89e26cc..3ae2796 100644
--- a/src/tools/ipc.h
+++ b/src/tools/ipc.h
@@ -8,10 +8,13 @@
#include <stdbool.h>
+#include "containers.h"
+
struct wgdevice;
-int ipc_set_device(struct wgdevice *dev);
-int ipc_get_device(struct wgdevice **dev, const char *interface);
+int ipc_set_device(struct wgnetns *dev_netns, struct wgdevice *dev);
+int ipc_get_device(struct wgnetns *dev_netns, struct wgdevice **dev,
+ const char *interface);
char *ipc_list_devices(void);
#endif
diff --git a/src/tools/netns.c b/src/tools/netns.c
new file mode 100644
index 0000000..73ce762
--- /dev/null
+++ b/src/tools/netns.c
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Julian orth <ju.orth at gmail.com>. All Rights Reserved.
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sched.h>
+#include <ctype.h>
+
+#include "netns.h"
+
+struct wgnetns netns_current = { 0 };
+
+bool netns_enter(struct wgnetns *netns)
+{
+ int fd = netns->fd;
+
+ if (!netns->flags)
+ return true;
+
+ if (netns->flags & WGNETNS_HAS_PID) {
+ char path[64];
+ sprintf(path, "/proc/%d/ns/net", netns->pid);
+ fd = open(path, O_RDONLY);
+ if (fd == -1) {
+ perror("Unable to open netns by pid");
+ return false;
+ }
+ }
+
+ if (setns(fd, CLONE_NEWNET)) {
+ perror("setns");
+ return false;
+ }
+
+ return true;
+}
+
+bool netns_parse(struct wgnetns *netns, const char *arg)
+{
+ /* U32 arg -> PID */
+ if (isdigit(*arg)) {
+ char *end;
+ unsigned long pid = strtoul(arg, &end, 10);
+ if (!*end && pid <= UINT32_MAX) {
+ netns->pid = pid;
+ netns->flags |= WGNETNS_HAS_PID;
+ return true;
+ }
+ }
+
+ /* Otherwise -> file path */
+ netns->fd = open(arg, O_RDONLY);
+ if (netns->fd >= 0) {
+ netns->flags |= WGNETNS_HAS_FD;
+ return true;
+ }
+
+ perror("open");
+ return false;
+}
diff --git a/src/tools/netns.h b/src/tools/netns.h
new file mode 100644
index 0000000..df7723a
--- /dev/null
+++ b/src/tools/netns.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Julian orth <ju.orth at gmail.com>. All Rights Reserved.
+ */
+
+#ifndef NETNS_H
+#define NETNS_H
+
+#include <stdbool.h>
+
+#include "containers.h"
+
+bool netns_enter(struct wgnetns *netns);
+bool netns_parse(struct wgnetns *netns, const char *arg);
+
+extern struct wgnetns netns_current;
+
+#endif
diff --git a/src/tools/set.c b/src/tools/set.c
index 2fb782b..24b72b8 100644
--- a/src/tools/set.c
+++ b/src/tools/set.c
@@ -16,7 +16,6 @@ int set_main(int argc, char *argv[], struct wgoptions *options)
{
struct wgdevice *device = NULL;
int ret = 1;
- (void)options;
if (argc < 3) {
fprintf(stderr, "Usage: %s %s <interface> [listen-port <port>] [fwmark <mark>] [private-key <file path>] [peer <base64 public key> [remove] [preshared-key <file path>] [endpoint <ip>:<port>] [persistent-keepalive <interval seconds>] [allowed-ips <ip1>/<cidr1>[,<ip2>/<cidr2>]...] ]...\n", PROG_NAME, argv[0]);
@@ -29,7 +28,7 @@ int set_main(int argc, char *argv[], struct wgoptions *options)
strncpy(device->name, argv[1], IFNAMSIZ - 1);
device->name[IFNAMSIZ - 1] = '\0';
- if (ipc_set_device(device) != 0) {
+ if (ipc_set_device(&options->dev_netns, device) != 0) {
perror("Unable to modify interface");
goto cleanup;
}
diff --git a/src/tools/setconf.c b/src/tools/setconf.c
index c28dbac..32543d7 100644
--- a/src/tools/setconf.c
+++ b/src/tools/setconf.c
@@ -21,7 +21,6 @@ int setconf_main(int argc, char *argv[], struct wgoptions *options)
char *config_buffer = NULL;
size_t config_buffer_len = 0;
int ret = 1;
- (void)options;
if (argc != 3) {
fprintf(stderr, "Usage: %s %s <interface> <configuration filename>\n", PROG_NAME, argv[0]);
@@ -51,7 +50,7 @@ int setconf_main(int argc, char *argv[], struct wgoptions *options)
strncpy(device->name, argv[1], IFNAMSIZ - 1);
device->name[IFNAMSIZ - 1] = '\0';
- if (ipc_set_device(device) != 0) {
+ if (ipc_set_device(&options->dev_netns, device) != 0) {
perror("Unable to modify interface");
goto cleanup;
}
diff --git a/src/tools/show.c b/src/tools/show.c
index 41f1a11..5ccb7f9 100644
--- a/src/tools/show.c
+++ b/src/tools/show.c
@@ -23,6 +23,7 @@
#include "terminal.h"
#include "encoding.h"
#include "subcommands.h"
+#include "netns.h"
static int peer_cmp(const void *first, const void *second)
{
@@ -377,17 +378,14 @@ static bool ugly_print(struct wgdevice *device, const char *param, bool with_int
return true;
}
-int show_main(int argc, char *argv[], struct wgoptions *options)
+static int show_all(int argc, char *argv[], struct wgoptions *options)
{
int ret = 0;
- (void)options;
- COMMAND_NAME = argv[0];
-
- if (argc > 3) {
- show_usage();
+ // The kernel interface used by ipc_list_devices does not allow us to
+ // list devices in a namespace referenced via pid or fd.
+ if (!netns_enter(&options->dev_netns))
return 1;
- }
if (argc == 1 || !strcmp(argv[1], "all")) {
char *interfaces = ipc_list_devices(), *interface;
@@ -401,7 +399,7 @@ int show_main(int argc, char *argv[], struct wgoptions *options)
for (size_t len = 0; (len = strlen(interface)); interface += len + 1) {
struct wgdevice *device = NULL;
- if (ipc_get_device(&device, interface) < 0) {
+ if (ipc_get_device(&netns_current, &device, interface) < 0) {
fprintf(stderr, "Unable to access interface %s: %s\n", interface, strerror(errno));
continue;
}
@@ -436,12 +434,30 @@ int show_main(int argc, char *argv[], struct wgoptions *options)
for (size_t len = 0; (len = strlen(interface)); interface += len + 1)
printf("%s%c", interface, strlen(interface + len + 1) ? ' ' : '\n');
free(interfaces);
- } else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help")))
+ }
+
+ return ret;
+}
+
+int show_main(int argc, char *argv[], struct wgoptions *options)
+{
+ int ret = 0;
+
+ COMMAND_NAME = argv[0];
+
+ if (argc > 3) {
+ show_usage();
+ return 1;
+ }
+
+ if (argc == 1 || !strcmp(argv[1], "all") || !strcmp(argv[1], "interfaces"))
+ ret = show_all(argc, argv, options);
+ else if (argc == 2 && (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "help")))
show_usage();
else {
struct wgdevice *device = NULL;
- if (ipc_get_device(&device, argv[1]) < 0) {
+ if (ipc_get_device(&options->dev_netns, &device, argv[1]) < 0) {
perror("Unable to access interface");
return 1;
}
diff --git a/src/tools/showconf.c b/src/tools/showconf.c
index 0c1fdc3..a7d4053 100644
--- a/src/tools/showconf.c
+++ b/src/tools/showconf.c
@@ -26,14 +26,13 @@ int showconf_main(int argc, char *argv[], struct wgoptions *options)
struct wgpeer *peer;
struct wgallowedip *allowedip;
int ret = 1;
- (void)options;
if (argc != 2) {
fprintf(stderr, "Usage: %s %s <interface>\n", PROG_NAME, argv[0]);
return 1;
}
- if (ipc_get_device(&device, argv[1])) {
+ if (ipc_get_device(&options->dev_netns, &device, argv[1])) {
perror("Unable to access interface");
goto cleanup;
}
diff --git a/src/tools/wg.c b/src/tools/wg.c
index 3bf6252..3a81f74 100644
--- a/src/tools/wg.c
+++ b/src/tools/wg.c
@@ -11,6 +11,7 @@
#include "subcommands.h"
#include "containers.h"
+#include "netns.h"
const char *PROG_NAME;
@@ -46,21 +47,28 @@ static bool parse_options(int argc, char *argv[], struct wgoptions *options)
.name = "help",
.val = 'h',
},
+ {
+ .name = "netns",
+ .has_arg = 1,
+ .val = 'n',
+ },
{
0
}
};
- (void)options;
setenv("POSIXLY_CORRECT", "", 0);
- while ((ch = getopt_long(argc, argv, "h", opts, NULL)) != -1) {
+ while ((ch = getopt_long(argc, argv, "hn:", opts, NULL)) != -1) {
switch (ch) {
case '?':
return false;
case 'h':
show_usage(stdout);
exit(0);
+ case 'n':
+ netns_parse(&options->dev_netns, optarg);
+ break;
}
}
@@ -69,7 +77,7 @@ static bool parse_options(int argc, char *argv[], struct wgoptions *options)
int main(int argc, char *argv[])
{
- struct wgoptions options = { };
+ struct wgoptions options = { 0 };
PROG_NAME = argv[0];
--
2.18.0
More information about the WireGuard
mailing list