netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH ulogd2] nfct: add network namespace support
@ 2025-03-08 23:14 Corubba Smith
  2025-03-12  8:37 ` Florian Westphal
  0 siblings, 1 reply; 3+ messages in thread
From: Corubba Smith @ 2025-03-08 23:14 UTC (permalink / raw)
  To: netfilter-devel

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@gmx.de>
---
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


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH ulogd2] nfct: add network namespace support
  2025-03-08 23:14 [PATCH ulogd2] nfct: add network namespace support Corubba Smith
@ 2025-03-12  8:37 ` Florian Westphal
  2025-03-12 12:19   ` Corubba Smith
  0 siblings, 1 reply; 3+ messages in thread
From: Florian Westphal @ 2025-03-12  8:37 UTC (permalink / raw)
  To: Corubba Smith; +Cc: netfilter-devel

Corubba Smith <corubba@gmx.de> wrote:
> 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.

Makes sense to me.

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

Does it make sense to have this configured on a per-plugin basis?

>    Input plugins:
>      NFLOG plugin:			${enable_nflog}
>      NFCT plugin:			${enable_nfct}

> +#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;
> +		}

I think that in order to not have copypastry in all relevant plugins
it would be better to turn code in the NETNS_SUPPORT ifdefs section
into library helpers.

The helpers would always exist; in case ulogd2 is built without
support they would raise an error.

That would also keep the ifdef out of plugin code.

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH ulogd2] nfct: add network namespace support
  2025-03-12  8:37 ` Florian Westphal
@ 2025-03-12 12:19   ` Corubba Smith
  0 siblings, 0 replies; 3+ messages in thread
From: Corubba Smith @ 2025-03-12 12:19 UTC (permalink / raw)
  To: Florian Westphal; +Cc: netfilter-devel

On 3/12/25 09:37, Florian Westphal wrote:
> Corubba Smith <corubba@gmx.de> wrote:
>> 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.
>
> Makes sense to me.
>
>> This commit only implements it for NFCT. I wanted to gather some
>> feedback before also implementing it for the other netlink-based
>> plugins.
>
> Does it make sense to have this configured on a per-plugin basis?

I honestly don't see the usecase for it. Enabling namespace support
depends on whether the used libc supports the required syscall, which is
the same for all plugins. But even if namespace support is compiled in
globally, you still need to activate it per plugin instance by setting
the namespace path config option. That option is not mandatory, and if
not set the plugin will behave the same as before, opening the netlink
socket in the current network namespace. So compiling in namespace
support alone does not change the runtime behaviour (or actually use the
syscall), it only does when you also explicitly use the namespace path
option on a plugin instance.

>
>>    Input plugins:
>>      NFLOG plugin:			${enable_nflog}
>>      NFCT plugin:			${enable_nfct}
>
>> +#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;
>> +		}
>
> I think that in order to not have copypastry in all relevant plugins
> it would be better to turn code in the NETNS_SUPPORT ifdefs section
> into library helpers.
>
> The helpers would always exist; in case ulogd2 is built without
> support they would raise an error.
>
> That would also keep the ifdef out of plugin code.

Very good point, will do that. Thanks!

--
Corubba


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-03-12 12:20 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-03-08 23:14 [PATCH ulogd2] nfct: add network namespace support Corubba Smith
2025-03-12  8:37 ` Florian Westphal
2025-03-12 12:19   ` Corubba Smith

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).