[PATCH ulogd2] nfct: add network namespace support

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

 



Add a new option which allows opening the netlink socket in a different
network namespace. This way you can run ulogd in one (management)
network namespace which is able to talk with your export target (e.g.
database or IPFIX collector), and import flows from multiple (customer)
network namespaces.

The config option is a path and not just a name so you can also use
anonymous network namespaces (e.g. `/proc/20/ns/net`)

Signed-off-by: Corubba Smith <corubba@xxxxxx>
---
Since the required `setns()` syscall is part of the libc GNU extension,
`_GNU_SOURCE` needs to be defined before importing the headers. Even
after reading about it, I am still unsure about the ramifications of
that with regard to compatibility. That is why I maybe went a bit
overboard with the autoconf feature toggle, autodetection and
conditional compilation. Any feedback is appreciated.

Also tested to compile and run against musl.

This commit only implements it for NFCT. I wanted to gather some
feedback before also implementing it for the other netlink-based
plugins.


 configure.ac                    | 39 ++++++++++++++++++
 input/flow/ulogd_inpflow_NFCT.c | 72 ++++++++++++++++++++++++++++++++-
 2 files changed, 109 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 3c9249e..70aa1d7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -243,6 +243,44 @@ AS_IF([test "x$enable_json" != "xno"],
 AS_IF([test "x$libjansson_LIBS" != "x"], [enable_json=yes], [enable_json=no])
 AM_CONDITIONAL([HAVE_JANSSON], [test "x$libjansson_LIBS" != "x"])

+AC_ARG_ENABLE([netns],
+              [AS_HELP_STRING([--enable-netns], [Enable network namespace support for netlink-based plugins [default=test]])])
+AS_IF([test "x$enable_netns" != "xno"], [
+  AC_CHECK_DECLS([setns, CLONE_NEWNET], [
+    enable_netns=yes
+  ], [], [[#include <fcntl.h>], [#include <sched.h>]])
+])
+AS_IF([test "x$enable_netns" != "xno"], [
+  AC_MSG_CHECKING([whether setns and CLONE_NEWNET are declared using _GNU_SOURCE])
+  AC_LINK_IFELSE([
+    AC_LANG_SOURCE([
+      #define _GNU_SOURCE
+      #include <fcntl.h>
+      #include <sched.h>
+      int main() {
+        setns(0, CLONE_NEWNET);
+        return 0;
+      }
+    ])
+  ],[
+    AC_MSG_RESULT([yes])
+    enable_netns=yes
+    AC_DEFINE([HAVE_DECL_SETNS], [1], [])
+    AC_DEFINE([HAVE_DECL_CLONE_NEWNET], [1], [])
+    AC_DEFINE([NETNS_REQUIRES_GNUSOURCE], [1], [Define if network namespace functionality requires _GNU_SOURCE])
+  ],[
+    AC_MSG_RESULT([no])
+    AS_IF([test "x$enable_netns" = "xyes"], [
+      AC_MSG_ERROR([network namespace support enabled, but required symbols not available])
+    ], [
+      enable_netns=no
+    ])
+  ])
+])
+AS_IF([test "x$enable_netns" = "xyes"], [
+  AC_DEFINE([NETNS_SUPPORT], [1], [Define if network namespace support is enabled])
+], [])
+
 AC_ARG_WITH([ulogd2libdir],
             [AS_HELP_STRING([--with-ulogd2libdir=PATH], [Default directory to load ulogd2 plugin from [[LIBDIR/ulogd]]])],
             [ulogd2libdir="$withval"],
@@ -293,6 +331,7 @@ EXPAND_VARIABLE(ulogd2libdir, e_ulogd2libdir)
 echo "
 Ulogd configuration:
   Default plugins directory:		${e_ulogd2libdir}
+  Network namespace support:		${enable_netns}
   Input plugins:
     NFLOG plugin:			${enable_nflog}
     NFCT plugin:			${enable_nfct}
diff --git a/input/flow/ulogd_inpflow_NFCT.c b/input/flow/ulogd_inpflow_NFCT.c
index 899b7e3..61f9f71 100644
--- a/input/flow/ulogd_inpflow_NFCT.c
+++ b/input/flow/ulogd_inpflow_NFCT.c
@@ -29,6 +29,12 @@
  * 	  network wide connection hash table.
  */

+#include "config.h"
+
+#ifdef NETNS_REQUIRES_GNUSOURCE
+#define _GNU_SOURCE
+#endif /* NETNS_REQUIRES_GNUSOURCE */
+
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
@@ -37,6 +43,8 @@
 #include <time.h>
 #include <netinet/in.h>
 #include <netdb.h>
+#include <fcntl.h>
+#include <sched.h>
 #include <ulogd/linuxlist.h>
 #include <ulogd/jhash.h>
 #include <ulogd/hash.h>
@@ -78,7 +86,7 @@ struct nfct_pluginstance {
 #define EVENT_MASK	NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY

 static struct config_keyset nfct_kset = {
-	.num_ces = 12,
+	.num_ces = 13,
 	.ces = {
 		{
 			.key	 = "pollinterval",
@@ -149,6 +157,11 @@ static struct config_keyset nfct_kset = {
 			.type	 = CONFIG_TYPE_STRING,
 			.options = CONFIG_OPT_NONE,
 		},
+		{
+			.key     = "network_namespace_path",
+			.type    = CONFIG_TYPE_STRING,
+			.options = CONFIG_OPT_NONE,
+		},
 	},
 };
 #define pollint_ce(x)	(x->ces[0])
@@ -163,6 +176,7 @@ static struct config_keyset nfct_kset = {
 #define src_filter_ce(x)	((x)->ces[9])
 #define dst_filter_ce(x)	((x)->ces[10])
 #define proto_filter_ce(x)	((x)->ces[11])
+#define network_namespace_path_ce(x)	((x)->ces[12])

 enum nfct_keys {
 	NFCT_ORIG_IP_SADDR = 0,
@@ -1286,6 +1300,40 @@ static int constructor_nfct_events(struct ulogd_pluginstance *upi)
 	struct nfct_pluginstance *cpi =
 			(struct nfct_pluginstance *)upi->private;

+	const char *const target_netns_path = network_namespace_path_ce(upi->config_kset).u.string;
+	int original_netns_fd = -1, target_netns_fd = -1;
+
+#ifdef NETNS_SUPPORT
+	if (strlen(target_netns_path) > 0) {
+		errno = 0;
+		original_netns_fd = open("/proc/self/ns/net", O_RDONLY | O_CLOEXEC);
+		if (original_netns_fd < 0) {
+			ulogd_log(ULOGD_FATAL, "error opening original network namespace: %s\n", strerror(errno));
+			goto err_ons;
+		}
+
+		target_netns_fd = open(target_netns_path, O_RDONLY | O_CLOEXEC);
+		if (target_netns_fd < 0) {
+			ulogd_log(ULOGD_FATAL, "error opening target network namespace: %s\n", strerror(errno));
+			goto err_tns;
+		}
+
+		if (setns(target_netns_fd, CLONE_NEWNET) < 0) {
+			ulogd_log(ULOGD_FATAL, "error joining target network namespace: %s\n", strerror(errno));
+			goto err_cth;
+		}
+
+		if (close(target_netns_fd) < 0) {
+			ulogd_log(ULOGD_NOTICE, "error closing target network namespace: %s\n", strerror(errno));
+		}
+		target_netns_fd = -1;
+	}
+#else
+	if (strlen(target_netns_path) > 0) {
+		ulogd_log(ULOGD_FATAL, "network namespace support is not compiled in.\n");
+		goto err_ons;
+	}
+#endif /* NETNS_SUPPORT */

 	cpi->cth = nfct_open(NFNL_SUBSYS_CTNETLINK,
 			     eventmask_ce(upi->config_kset).u.value);
@@ -1294,13 +1342,28 @@ static int constructor_nfct_events(struct ulogd_pluginstance *upi)
 		goto err_cth;
 	}

+#ifdef NETNS_SUPPORT
+	if (strlen(target_netns_path) > 0) {
+		errno = 0;
+		if (setns(original_netns_fd, CLONE_NEWNET) < 0) {
+			ulogd_log(ULOGD_FATAL, "error joining original network namespace: %s\n", strerror(errno));
+			goto err_nsr;
+		}
+
+		if (close(original_netns_fd) < 0) {
+			ulogd_log(ULOGD_NOTICE, "error closing original network namespace: %s\n", strerror(errno));
+		}
+		original_netns_fd = -1;
+	}
+#endif /* NETNS_SUPPORT */
+
 	if ((strlen(src_filter_ce(upi->config_kset).u.string) != 0) ||
 		(strlen(dst_filter_ce(upi->config_kset).u.string) != 0) ||
 		(strlen(proto_filter_ce(upi->config_kset).u.string) != 0)
 	   ) {
 		if (build_nfct_filter(upi) != 0) {
 			ulogd_log(ULOGD_FATAL, "error creating NFCT filter\n");
-			goto err_cth;
+			goto err_nsr;
 		}
 	}

@@ -1408,8 +1471,13 @@ err_hashtable:
 	nfct_destroy(cpi->ct);
 err_nfctobj:
 	ulogd_unregister_fd(&cpi->nfct_fd);
+err_nsr:
 	nfct_close(cpi->cth);
 err_cth:
+	if (target_netns_fd >= 0) close(target_netns_fd);
+err_tns:
+	if (original_netns_fd >= 0) close(original_netns_fd);
+err_ons:
 	return -1;
 }

--
2.48.1






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

  Powered by Linux