summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac22
-rw-r--r--include/ulogd/Makefile.am4
-rw-r--r--include/ulogd/namespace.h8
-rw-r--r--src/Makefile.am3
-rw-r--r--src/namespace.c224
5 files changed, 259 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 55e6bc6..daaf69f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -236,6 +236,27 @@ 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([namespace],
+ [AS_HELP_STRING([--enable-namespace], [Enable linux namespace functionality in plugins supporting it [default=test]])])
+AS_IF([test "x$enable_namespace" != "xno"], [
+ AC_CHECK_DECLS([setns, CLONE_NEWNET], [
+ enable_namespace=yes
+ ], [
+ AS_IF([test "x$enable_namespace" = "xyes"], [
+ AC_MSG_ERROR([linux namespace support enabled, but required symbols not available])
+ ], [
+ enable_namespace=no
+ ])
+ ], [[
+ #define _GNU_SOURCE 1
+ #include <fcntl.h>
+ #include <sched.h>
+ ]])
+])
+AS_IF([test "x$enable_namespace" = "xyes"], [
+ AC_DEFINE([ENABLE_NAMESPACE], [1], [Define to 1 if you want linux namespace support.])
+])
+
AC_ARG_WITH([ulogd2libdir],
[AS_HELP_STRING([--with-ulogd2libdir=PATH], [Default directory to load ulogd2 plugin from [[LIBDIR/ulogd]]])],
[ulogd2libdir="$withval"],
@@ -283,6 +304,7 @@ EXPAND_VARIABLE(ulogd2libdir, e_ulogd2libdir)
echo "
Ulogd configuration:
Default plugins directory: ${e_ulogd2libdir}
+ Linux namespace support: ${enable_namespace}
Input plugins:
NFLOG plugin: ${enable_nflog}
NFCT plugin: ${enable_nfct}
diff --git a/include/ulogd/Makefile.am b/include/ulogd/Makefile.am
index e4b41c4..65d74ba 100644
--- a/include/ulogd/Makefile.am
+++ b/include/ulogd/Makefile.am
@@ -1 +1,3 @@
-noinst_HEADERS = conffile.h db.h ipfix_protocol.h linuxlist.h ulogd.h printpkt.h printflow.h common.h linux_rbtree.h timer.h slist.h hash.h jhash.h addr.h
+noinst_HEADERS = addr.h common.h conffile.h db.h hash.h ipfix_protocol.h \
+ jhash.h linux_rbtree.h linuxlist.h namespace.h printflow.h \
+ printpkt.h slist.h timer.h ulogd.h
diff --git a/include/ulogd/namespace.h b/include/ulogd/namespace.h
new file mode 100644
index 0000000..48e2e9a
--- /dev/null
+++ b/include/ulogd/namespace.h
@@ -0,0 +1,8 @@
+#ifndef _NAMESPACE_H_
+#define _NAMESPACE_H_
+
+int join_netns_fd(const int target_netns_fd, int *const source_netns_fd_ptr);
+int join_netns_path(const char *const target_netns_path,
+ int *const source_netns_fd_ptr);
+
+#endif
diff --git a/src/Makefile.am b/src/Makefile.am
index 7a12a72..4004c2b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -6,6 +6,7 @@ AM_CPPFLAGS += -DULOGD_CONFIGFILE='"$(sysconfdir)/ulogd.conf"' \
sbin_PROGRAMS = ulogd
-ulogd_SOURCES = ulogd.c select.c timer.c rbtree.c conffile.c hash.c addr.c
+ulogd_SOURCES = ulogd.c select.c timer.c rbtree.c conffile.c hash.c \
+ addr.c namespace.c
ulogd_LDADD = ${libdl_LIBS} ${libpthread_LIBS}
ulogd_LDFLAGS = -export-dynamic
diff --git a/src/namespace.c b/src/namespace.c
new file mode 100644
index 0000000..d91f1e6
--- /dev/null
+++ b/src/namespace.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+/* ulogd namespace helper
+ *
+ * (C) 2025 The netfilter project
+ *
+ * Helper library to switch linux namespaces, primarily network. Provides
+ * ulogd-internally a stable api regardless whether namespace support is
+ * compiled in. Library-internally uses conditional compilation to allow the
+ * wanted level (full/none) of namespace support. Namespaces can be specified
+ * as open file descriptor or file path.
+ */
+
+#include "config.h"
+
+/* Enable GNU extension */
+#define _GNU_SOURCE 1
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sched.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ulogd/ulogd.h"
+#include "ulogd/namespace.h"
+
+
+#ifdef ENABLE_NAMESPACE
+/**
+ * open_namespace_path() - Open a namespace link by path.
+ * @ns_path: Path of the file to open.
+ *
+ * Effectively just a wrapper around the open() syscall with fixed flags
+ * suitable for namespaces.
+ *
+ * Return: Open fd on success, -1 on error (and set errno).
+ */
+static int open_namespace_path(const char *const ns_path) {
+ return open(ns_path, O_RDONLY | O_CLOEXEC);
+}
+
+/**
+ * SELF_NAMESPACE_PATH() - Path for own current namespace.
+ * @x: Name of the namespace link.
+ *
+ * Return: String-constant of the absolute path to the namespace link.
+ */
+#define SELF_NAMESPACE_PATH(x) "/proc/self/ns/" #x
+
+/**
+ * open_source_namespace() - Get file descriptor to current namespace.
+ * @nstype: Namespace type, use one of the CLONE_NEW* constants.
+ *
+ * Return: Open fd on success, -1 on error.
+ */
+static int open_source_namespace(const int nstype) {
+ const char *ns_path = NULL;
+ int ns_fd = -1;
+
+ switch (nstype) {
+ case CLONE_NEWNET:
+ ns_path = SELF_NAMESPACE_PATH(net);
+ break;
+ default:
+ ulogd_log(ULOGD_FATAL,
+ "unsupported namespace type: %d\n", nstype);
+ return -1;
+ }
+
+ ns_fd = open_namespace_path(ns_path);
+ if (ns_fd < 0) {
+ ulogd_log(ULOGD_FATAL,
+ "error opening namespace '%s': %s\n",
+ ns_path, strerror(errno));
+ return -1;
+ }
+
+ return ns_fd;
+}
+#else
+
+/* These constants are used by the nstype-specific functions, and need to be
+ * defined even when no namespace support is available because only the generic
+ * functions will error.
+ */
+#ifndef CLONE_NEWNET
+#define CLONE_NEWNET -1
+#endif
+
+#endif /* ENABLE_NAMESPACE */
+
+/**
+ * join_namespace_fd() - Join a namespace by file descriptor.
+ * @nstype: Namespace type, use one of the CLONE_NEW* constants.
+ * @target_ns_fd: Open file descriptor of the namespace to join. Will be closed
+ * after successful join.
+ * @source_ns_fd_ptr: If not NULL, writes an open fd of the previous namespace
+ * to it if join was successful. May point to negative value
+ * after return.
+ *
+ * Return: ULOGD_IRET_OK on success, ULOGD_IRET_ERR otherwise.
+ */
+static int join_namespace_fd(const int nstype, const int target_ns_fd,
+ int *const source_ns_fd_ptr)
+{
+#ifdef ENABLE_NAMESPACE
+ if (target_ns_fd < 0) {
+ ulogd_log(ULOGD_DEBUG, "invalid target namespace fd\n");
+ return ULOGD_IRET_ERR;
+ }
+
+ if (source_ns_fd_ptr != NULL) {
+ *source_ns_fd_ptr = open_source_namespace(nstype);
+ if (*source_ns_fd_ptr < 0) {
+ ulogd_log(ULOGD_FATAL,
+ "error opening source namespace\n");
+ return ULOGD_IRET_ERR;
+ }
+ }
+
+ if (setns(target_ns_fd, nstype) < 0) {
+ ulogd_log(ULOGD_FATAL, "error joining target namespace: %s\n",
+ strerror(errno));
+
+ if (source_ns_fd_ptr != NULL) {
+ if (close(*source_ns_fd_ptr) < 0) {
+ ulogd_log(ULOGD_NOTICE,
+ "error closing source namespace: %s\n",
+ strerror(errno));
+ }
+ *source_ns_fd_ptr = -1;
+ }
+
+ return ULOGD_IRET_ERR;
+ }
+ ulogd_log(ULOGD_DEBUG, "successfully switched namespace\n");
+
+ if (close(target_ns_fd) < 0) {
+ ulogd_log(ULOGD_NOTICE, "error closing target namespace: %s\n",
+ strerror(errno));
+ }
+
+ return ULOGD_IRET_OK;
+#else
+ if (source_ns_fd_ptr != NULL) {
+ *source_ns_fd_ptr = -1;
+ }
+ ulogd_log(ULOGD_FATAL,
+ "ulogd was compiled without linux namespace support.\n");
+ return ULOGD_IRET_ERR;
+#endif /* ENABLE_NAMESPACE */
+}
+
+/**
+ * join_namespace_path() - Join a namespace by path.
+ * @nstype: Namespace type, use one of the CLONE_NEW* constants.
+ * @target_ns_path: Path of the namespace to join.
+ * @source_ns_fd_ptr: If not NULL, writes an open fd of the previous namespace
+ * to it if join was successful. May point to negative value
+ * after return.
+ *
+ * Return: ULOGD_IRET_OK on success, ULOGD_IRET_ERR otherwise.
+ */
+static int join_namespace_path(const int nstype, const char *const target_ns_path,
+ int *const source_ns_fd_ptr)
+{
+#ifdef ENABLE_NAMESPACE
+ int target_ns_fd, ret;
+
+ target_ns_fd = open_namespace_path(target_ns_path);
+ if (target_ns_fd < 0) {
+ ulogd_log(ULOGD_FATAL, "error opening target namespace: %s\n",
+ strerror(errno));
+ return ULOGD_IRET_ERR;
+ }
+
+ ret = join_namespace_fd(nstype, target_ns_fd, source_ns_fd_ptr);
+ if (ret != ULOGD_IRET_OK) {
+ if (close(target_ns_fd) < 0) {
+ ulogd_log(ULOGD_NOTICE,
+ "error closing target namespace: %s\n",
+ strerror(errno));
+ }
+ return ULOGD_IRET_ERR;
+ }
+
+ return ULOGD_IRET_OK;
+#else
+ return join_namespace_fd(nstype, -1, source_ns_fd_ptr);
+#endif /* ENABLE_NAMESPACE */
+}
+
+
+/**
+ * join_netns_fd() - Join a network namespace by file descriptor.
+ * @target_netns_fd: Open file descriptor of the network namespace to join. Will
+ * be closed after successful join.
+ * @source_netns_fd_ptr: If not NULL, writes an open fd of the previous network
+ * namespace to it if join was successful. May point to
+ * negative value after return.
+ *
+ * Return: ULOGD_IRET_OK on success, ULOGD_IRET_ERR otherwise.
+ */
+int join_netns_fd(const int target_netns_fd, int *const source_netns_fd_ptr)
+{
+ return join_namespace_fd(CLONE_NEWNET, target_netns_fd,
+ source_netns_fd_ptr);
+}
+
+/**
+ * join_netns_path() - Join a network namespace by path.
+ * @target_netns_path: Path of the network namespace to join.
+ * @source_netns_fd_ptr: If not NULL, writes an open fd of the previous network
+ * namespace to it if join was successful. May point to
+ * negative value after return.
+ *
+ * Return: ULOGD_IRET_OK on success, ULOGD_IRET_ERR otherwise.
+ */
+int join_netns_path(const char *const target_netns_path,
+ int *const source_netns_fd_ptr)
+{
+ return join_namespace_path(CLONE_NEWNET, target_netns_path,
+ source_netns_fd_ptr);
+}