[PATCH nft 1/3] libnftables: add nft_ctx_set_netns()

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This new function allows you to specify the net namespace for the
nftables command invocations.

Signed-off-by: Pablo Neira Ayuso <pablo@xxxxxxxxxxxxx>
---
 include/namespace.h            |  21 ++++++++
 include/nftables/libnftables.h |   3 ++
 src/Makefile.am                |   1 +
 src/libnftables.c              |  20 ++++++++
 src/libnftables.map            |   4 ++
 src/namespace.c                | 107 +++++++++++++++++++++++++++++++++++++++++
 6 files changed, 156 insertions(+)
 create mode 100644 include/namespace.h
 create mode 100644 src/namespace.c

diff --git a/include/namespace.h b/include/namespace.h
new file mode 100644
index 000000000000..0aa7524c1525
--- /dev/null
+++ b/include/namespace.h
@@ -0,0 +1,21 @@
+#ifndef __NAMESPACE_H__
+#define __NAMESPACE_H__
+
+#include <sched.h>
+#include <sys/mount.h>
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <errno.h>
+
+/* Use the same default path as iproute2. */
+#ifndef NETNS_RUN_DIR
+#define NETNS_RUN_DIR "/var/run/netns"
+#endif
+
+#ifndef NETNS_ETC_DIR
+#define NETNS_ETC_DIR "/etc/netns"
+#endif
+
+int netns_switch(const char *netns);
+
+#endif /* __NAMESPACE_H__ */
diff --git a/include/nftables/libnftables.h b/include/nftables/libnftables.h
index 765b20dd71ee..887628959ac6 100644
--- a/include/nftables/libnftables.h
+++ b/include/nftables/libnftables.h
@@ -34,10 +34,13 @@ enum nft_debug_level {
  * Possible flags to pass to nft_ctx_new()
  */
 #define NFT_CTX_DEFAULT		0
+#define NFT_CTX_NETNS		1
 
 struct nft_ctx *nft_ctx_new(uint32_t flags);
 void nft_ctx_free(struct nft_ctx *ctx);
 
+int nft_ctx_set_netns(struct nft_ctx *ctx, const char *netns);
+
 bool nft_ctx_get_dry_run(struct nft_ctx *ctx);
 void nft_ctx_set_dry_run(struct nft_ctx *ctx, bool dry);
 
diff --git a/src/Makefile.am b/src/Makefile.am
index 740c21f2cac8..ecd9bb2f1447 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -46,6 +46,7 @@ libnftables_la_SOURCES =			\
 		numgen.c			\
 		ct.c				\
 		xfrm.c				\
+		namespace.c			\
 		netlink.c			\
 		netlink_linearize.c		\
 		netlink_delinearize.c		\
diff --git a/src/libnftables.c b/src/libnftables.c
index cd2fcf2fd522..cd1763657cec 100644
--- a/src/libnftables.c
+++ b/src/libnftables.c
@@ -12,6 +12,7 @@
 #include <parser.h>
 #include <utils.h>
 #include <iface.h>
+#include <namespace.h>
 
 #include <errno.h>
 #include <stdlib.h>
@@ -166,6 +167,25 @@ struct nft_ctx *nft_ctx_new(uint32_t flags)
 	return ctx;
 }
 
+EXPORT_SYMBOL(nft_ctx_set_netns);
+int nft_ctx_set_netns(struct nft_ctx *ctx, const char *netns)
+{
+	int err;
+
+	if (ctx->flags != NFT_CTX_NETNS) {
+		errno = EOPNOTSUPP;
+		return -1;
+	}
+
+	err = netns_switch(netns);
+	if (err < 0)
+		return err;
+
+	nft_ctx_netlink_init(ctx);
+
+	return 0;
+}
+
 static ssize_t cookie_write(void *cptr, const char *buf, size_t buflen)
 {
 	struct cookie *cookie = cptr;
diff --git a/src/libnftables.map b/src/libnftables.map
index 955af3803ee0..bb8bc5de9e59 100644
--- a/src/libnftables.map
+++ b/src/libnftables.map
@@ -23,3 +23,7 @@ global:
 
 local: *;
 };
+
+LIBNFTABLES_2 {
+  nft_ctx_set_netns;
+} LIBNFTABLES_1;
diff --git a/src/namespace.c b/src/namespace.c
new file mode 100644
index 000000000000..3ca465d68808
--- /dev/null
+++ b/src/namespace.c
@@ -0,0 +1,107 @@
+/*
+ * namespace.c
+ *
+ *		This program is free software; you can redistribute it and/or
+ *		modify it under the terms of the GNU General Public License
+ *		as published by the Free Software Foundation; either version
+ *		2 of the License, or (at your option) any later version.
+ *
+ * This code has been extracted from iproute/lib/namespace.c
+ */
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/statvfs.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <limits.h>
+#include <sched.h>
+#include <namespace.h>
+
+static void bind_etc(const char *name)
+{
+	char etc_netns_path[sizeof(NETNS_ETC_DIR) + NAME_MAX];
+	char netns_name[PATH_MAX];
+	char etc_name[PATH_MAX];
+	struct dirent *entry;
+	DIR *dir;
+
+	if (strlen(name) >= NAME_MAX)
+		return;
+
+	snprintf(etc_netns_path, sizeof(etc_netns_path), "%s/%s", NETNS_ETC_DIR, name);
+	dir = opendir(etc_netns_path);
+	if (!dir)
+		return;
+
+	while ((entry = readdir(dir)) != NULL) {
+		if (strcmp(entry->d_name, ".") == 0)
+			continue;
+		if (strcmp(entry->d_name, "..") == 0)
+			continue;
+		snprintf(netns_name, sizeof(netns_name), "%s/%s", etc_netns_path, entry->d_name);
+		snprintf(etc_name, sizeof(etc_name), "/etc/%s", entry->d_name);
+		if (mount(netns_name, etc_name, "none", MS_BIND, NULL) < 0) {
+			fprintf(stderr, "Bind %s -> %s failed: %s\n",
+				netns_name, etc_name, strerror(errno));
+		}
+	}
+	closedir(dir);
+}
+
+int netns_switch(const char *name)
+{
+	char net_path[PATH_MAX];
+	int netns;
+	unsigned long mountflags = 0;
+	struct statvfs fsstat;
+
+	snprintf(net_path, sizeof(net_path), "%s/%s", NETNS_RUN_DIR, name);
+	netns = open(net_path, O_RDONLY | O_CLOEXEC);
+	if (netns < 0) {
+		fprintf(stderr, "Cannot open network namespace \"%s\": %s\n",
+			name, strerror(errno));
+		return -1;
+	}
+
+	if (setns(netns, CLONE_NEWNET) < 0) {
+		fprintf(stderr, "setting the network namespace \"%s\" failed: %s\n",
+			name, strerror(errno));
+		close(netns);
+		return -1;
+	}
+	close(netns);
+
+	if (unshare(CLONE_NEWNS) < 0) {
+		fprintf(stderr, "unshare failed: %s\n", strerror(errno));
+		return -1;
+	}
+	/* Don't let any mounts propagate back to the parent */
+	if (mount("", "/", "none", MS_SLAVE | MS_REC, NULL)) {
+		fprintf(stderr, "\"mount --make-rslave /\" failed: %s\n",
+			strerror(errno));
+		return -1;
+	}
+
+	/* Mount a version of /sys that describes the network namespace */
+
+	if (umount2("/sys", MNT_DETACH) < 0) {
+		/* If this fails, perhaps there wasn't a sysfs instance mounted. Good. */
+		if (statvfs("/sys", &fsstat) == 0) {
+			/* We couldn't umount the sysfs, we'll attempt to overlay it.
+			 * A read-only instance can't be shadowed with a read-write one. */
+			if (fsstat.f_flag & ST_RDONLY)
+				mountflags = MS_RDONLY;
+		}
+	}
+	if (mount(name, "/sys", "sysfs", mountflags, NULL) < 0) {
+		fprintf(stderr, "mount of /sys failed: %s\n",strerror(errno));
+		return -1;
+	}
+
+	/* Setup bind mounts for config files in /etc */
+	bind_etc(name);
+	return 0;
+}
-- 
2.11.0




[Index of Archives]     [Netfitler Users]     [Berkeley Packet Filter]     [LARTC]     [Bugtraq]     [Yosemite Forum]

  Powered by Linux