From b794e7b89ab0324eceac8f3e16560cd5a50d63a8 Mon Sep 17 00:00:00 2001 From: Corubba Smith Date: Thu, 27 Mar 2025 00:07:49 +0100 Subject: nfct: add network namespace support Allow the plugin to fetch data from a different network namespace. This is possible by changing the network namespace before opening the netlink socket, and immediately changing back to the original network namespace once the socket is open. The number of nfct_open usages here warranted a dedicated wrapper function. If changing back to the original network namespace fails, ulogd will log an error, but continue to run in a different network namespace than it was started in, which may cause unexpected behaviour. But I don't see a way to properly "escalate" it such that ulogd aborts entirely. Also slightly adjust the error log messages to specify which socket failed to open. Signed-off-by: Corubba Smith Signed-off-by: Florian Westphal --- input/flow/ulogd_inpflow_NFCT.c | 82 ++++++++++++++++++++++++++++++++++------- ulogd.conf.in | 1 + 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/input/flow/ulogd_inpflow_NFCT.c b/input/flow/ulogd_inpflow_NFCT.c index fbebfb0..8746b88 100644 --- a/input/flow/ulogd_inpflow_NFCT.c +++ b/input/flow/ulogd_inpflow_NFCT.c @@ -45,6 +45,7 @@ #include #include #include +#include #include @@ -78,7 +79,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 +150,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 +169,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, @@ -980,6 +987,55 @@ static int read_cb_ovh(int fd, unsigned int what, void *param) return 0; } +/** + * nfct_open_in_netns() - Open conntrack netlink socket in a namespace + * @subscriptions: ctnetlink groups to subscribe to events + * @target_netns_path: path to the network namespace, can be NULL + * + * On error, NULL is returned and errno is explicitly set. + */ +struct nfct_handle *nfct_open_in_netns(unsigned int subscriptions, + const char *const target_netns_path) +{ + struct nfct_handle *result = NULL; + int source_netns_fd = -1; + + if ((target_netns_path != NULL) && + (strlen(target_netns_path) > 0) && + (join_netns_path(target_netns_path, &source_netns_fd) != ULOGD_IRET_OK)) { + ulogd_log(ULOGD_FATAL, "error joining target network " + "namespace\n"); + goto err_tns; + } + + result = nfct_open(NFNL_SUBSYS_CTNETLINK, subscriptions); + if (result == NULL) { + ulogd_log(ULOGD_FATAL, "error opening ctnetlink: %s\n", + strerror(errno)); + goto err_nfct; + } + + if ((target_netns_path != NULL) && + (strlen(target_netns_path) > 0) && + (join_netns_fd(source_netns_fd, NULL) != ULOGD_IRET_OK)) { + ulogd_log(ULOGD_FATAL, "error joining source network " + "namespace\n"); + goto err_sns; + } + /* join_netns_fd() closes the fd after successful join */ + source_netns_fd = -1; + + return result; + +err_sns: + nfct_close(result); +err_nfct: + if (source_netns_fd >= 0) + close(source_netns_fd); +err_tns: + return NULL; +} + static int dump_reset_handler(enum nf_conntrack_msg_type type, struct nf_conntrack *ct, void *data) @@ -1029,7 +1085,7 @@ static void get_ctr_zero(struct ulogd_pluginstance *upi) struct nfct_handle *h; int family = AF_UNSPEC; - h = nfct_open(CONNTRACK, 0); + h = nfct_open_in_netns(0, network_namespace_path_ce(upi->config_kset).u.string); if (h == NULL) { ulogd_log(ULOGD_FATAL, "Cannot dump and reset counters\n"); return; @@ -1305,10 +1361,10 @@ static int constructor_nfct_events(struct ulogd_pluginstance *upi) (struct nfct_pluginstance *)upi->private; - cpi->cth = nfct_open(NFNL_SUBSYS_CTNETLINK, - eventmask_ce(upi->config_kset).u.value); + cpi->cth = nfct_open_in_netns(eventmask_ce(upi->config_kset).u.value, + network_namespace_path_ce(upi->config_kset).u.string); if (!cpi->cth) { - ulogd_log(ULOGD_FATAL, "error opening ctnetlink\n"); + ulogd_log(ULOGD_FATAL, "error opening event netlink socket\n"); goto err_cth; } @@ -1376,9 +1432,9 @@ static int constructor_nfct_events(struct ulogd_pluginstance *upi) /* populate the hashtable: we use a disposable handler, we * may hit overrun if we use cpi->cth. This ensures that the * initial dump is successful. */ - h = nfct_open(CONNTRACK, 0); + h = nfct_open_in_netns(0, network_namespace_path_ce(upi->config_kset).u.string); if (!h) { - ulogd_log(ULOGD_FATAL, "error opening ctnetlink\n"); + ulogd_log(ULOGD_FATAL, "error opening initial-fill netlink socket\n"); goto err_ovh; } nfct_callback_register(h, NFCT_T_ALL, @@ -1388,9 +1444,9 @@ static int constructor_nfct_events(struct ulogd_pluginstance *upi) /* the overrun handler only make sense with the hashtable, * if we hit overrun, we resync with ther kernel table. */ - cpi->ovh = nfct_open(NFNL_SUBSYS_CTNETLINK, 0); + cpi->ovh = nfct_open_in_netns(0, network_namespace_path_ce(upi->config_kset).u.string); if (!cpi->ovh) { - ulogd_log(ULOGD_FATAL, "error opening ctnetlink\n"); + ulogd_log(ULOGD_FATAL, "error opening overrun-read netlink socket\n"); goto err_ovh; } @@ -1407,9 +1463,9 @@ static int constructor_nfct_events(struct ulogd_pluginstance *upi) ulogd_register_fd(&cpi->nfct_ov); /* we use this to purge old entries during overruns.*/ - cpi->pgh = nfct_open(NFNL_SUBSYS_CTNETLINK, 0); + cpi->pgh = nfct_open_in_netns(0, network_namespace_path_ce(upi->config_kset).u.string); if (!cpi->pgh) { - ulogd_log(ULOGD_FATAL, "error opening ctnetlink\n"); + ulogd_log(ULOGD_FATAL, "error opening overrun-purge netlink socket\n"); goto err_pgh; } } @@ -1442,9 +1498,9 @@ static int constructor_nfct_polling(struct ulogd_pluginstance *upi) goto err; } - cpi->pgh = nfct_open(NFNL_SUBSYS_CTNETLINK, 0); + cpi->pgh = nfct_open_in_netns(0, network_namespace_path_ce(upi->config_kset).u.string); if (!cpi->pgh) { - ulogd_log(ULOGD_FATAL, "error opening ctnetlink\n"); + ulogd_log(ULOGD_FATAL, "error opening polling netlink socket\n"); goto err; } nfct_callback_register(cpi->pgh, NFCT_T_ALL, &polling_handler, upi); diff --git a/ulogd.conf.in b/ulogd.conf.in index 6e0b7c7..77d7086 100644 --- a/ulogd.conf.in +++ b/ulogd.conf.in @@ -135,6 +135,7 @@ logfile="/var/log/ulogd.log" #netlink_socket_buffer_size=217088 #netlink_socket_buffer_maxsize=1085440 #reliable=1 # enable reliable flow-based logging (may drop packets) +#network_namespace_path=/run/netns/other # import flows from a different network namespace hash_enable=0 # Logging of system packet through NFLOG -- cgit v1.2.3