diff options
-rw-r--r-- | configure.ac | 22 | ||||
-rw-r--r-- | include/ulogd/Makefile.am | 4 | ||||
-rw-r--r-- | include/ulogd/namespace.h | 8 | ||||
-rw-r--r-- | src/Makefile.am | 3 | ||||
-rw-r--r-- | src/namespace.c | 224 |
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); +} |