* [PATCH] Added netfilter output plugin with ability to write into pcap nflog packets
@ 2025-11-09 9:23 Serhii Ivanov
2025-11-10 18:41 ` Florian Westphal
0 siblings, 1 reply; 2+ messages in thread
From: Serhii Ivanov @ 2025-11-09 9:23 UTC (permalink / raw)
To: netfilter-devel; +Cc: Serhii Ivanov
Extend inpit nflog plugin
to get more information
Chenged conmfiguration files for
proper dependencies to ntlog/ct/ctacc
Signed-off-by: Serhii Ivanov <icegood1980@gmail.com>
---
configure.ac | 3 +-
include/ulogd/ulogd.h | 2 +-
input/packet/Makefile.am | 18 +-
input/packet/ulogd_inppkt_NFLOG.c | 111 +++--
output/Makefile.am | 30 +-
output/pcap/Makefile.am | 15 +
output/pcap/ulogd_output_PCAP_NFLOG.c | 573 ++++++++++++++++++++++++++
output/ulogd_output_XML.c | 3 +-
src/ulogd.c | 1 +
ulogd.conf.in | 1 +
10 files changed, 705 insertions(+), 52 deletions(-)
create mode 100644 output/pcap/ulogd_output_PCAP_NFLOG.c
diff --git a/configure.ac b/configure.ac
index daaf69f..5b55e1c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -45,7 +45,8 @@ AC_ARG_ENABLE([nflog],
[enable_nflog=$enableval],
[enable_nflog=yes])
AS_IF([test "x$enable_nflog" = "xyes"],
- [PKG_CHECK_MODULES([LIBNETFILTER_LOG], [libnetfilter_log >= 1.0.2])
+ [PKG_CHECK_MODULES([LIBMNL], [libmnl >= 1.0.5])
+ PKG_CHECK_MODULES([LIBNETFILTER_LOG], [libnetfilter_log >= 1.0.2])
AC_DEFINE([BUILD_NFLOG], [1], [Building nflog module])],
[enable_nflog=no])
AM_CONDITIONAL([BUILD_NFLOG], [test "x$enable_nflog" = "xyes"])
diff --git a/include/ulogd/ulogd.h b/include/ulogd/ulogd.h
index 29082df..b0d2236 100644
--- a/include/ulogd/ulogd.h
+++ b/include/ulogd/ulogd.h
@@ -19,7 +19,7 @@
#include <inttypes.h>
#include <netinet/in.h>
#include <string.h>
-#include <config.h>
+#include <config.h> // all config stuff there
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
diff --git a/input/packet/Makefile.am b/input/packet/Makefile.am
index 20c51ec..f63cc6e 100644
--- a/input/packet/Makefile.am
+++ b/input/packet/Makefile.am
@@ -1,17 +1,21 @@
include $(top_srcdir)/Make_global.am
-AM_CPPFLAGS += ${LIBNETFILTER_LOG_CFLAGS} ${LIBNETFILTER_CONNTRACK_CFLAGS}
-
pkglib_LTLIBRARIES = ulogd_inppkt_UNIXSOCK.la
ulogd_inppkt_UNIXSOCK_la_SOURCES = ulogd_inppkt_UNIXSOCK.c
ulogd_inppkt_UNIXSOCK_la_LDFLAGS = -avoid-version -module
if BUILD_NFLOG
-pkglib_LTLIBRARIES += ulogd_inppkt_NFLOG.la
+ AM_CPPFLAGS += ${LIBNETFILTER_LOG_CFLAGS}
+
+ pkglib_LTLIBRARIES += ulogd_inppkt_NFLOG.la
-ulogd_inppkt_NFLOG_la_SOURCES = ulogd_inppkt_NFLOG.c
-ulogd_inppkt_NFLOG_la_LDFLAGS = -avoid-version -module
-ulogd_inppkt_NFLOG_la_LIBADD = $(LIBNETFILTER_LOG_LIBS) \
- $(LIBNETFILTER_CONNTRACK_LIBS)
+ ulogd_inppkt_NFLOG_la_SOURCES = ulogd_inppkt_NFLOG.c
+ ulogd_inppkt_NFLOG_la_LDFLAGS = -avoid-version -module
+ ulogd_inppkt_NFLOG_la_LIBADD = $(LIBNETFILTER_LOG_LIBS)
+
+if BUILD_NFCT
+ AM_CPPFLAGS += $(LIBMNL_CFLAGS) ${LIBNETFILTER_CONNTRACK_CFLAGS}
+ ulogd_inppkt_NFLOG_la_LIBADD += $(LIBMNL_LIBS) $(LIBNETFILTER_CONNTRACK_LIBS)
+endif
endif
diff --git a/input/packet/ulogd_inppkt_NFLOG.c b/input/packet/ulogd_inppkt_NFLOG.c
index b7042be..4840ddc 100644
--- a/input/packet/ulogd_inppkt_NFLOG.c
+++ b/input/packet/ulogd_inppkt_NFLOG.c
@@ -16,10 +16,15 @@
#ifdef BUILD_NFCT
#include <libmnl/libmnl.h>
#include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+#include <linux/netfilter/nf_conntrack_common.h>
#else
struct nf_conntrack;
#endif
+struct connttrack {
+ struct nf_conntrack *ct;
+ uint32_t info;
+};
#ifndef NFLOG_GROUP_DEFAULT
#define NFLOG_GROUP_DEFAULT 0
@@ -154,6 +159,8 @@ enum nflog_keys {
NFLOG_KEY_OOB_SEQ_LOCAL,
NFLOG_KEY_OOB_SEQ_GLOBAL,
NFLOG_KEY_OOB_FAMILY,
+ NFLOG_KEY_OOB_VERSION,
+ NFLOG_KEY_OOB_RES_ID,
NFLOG_KEY_OOB_PROTOCOL,
NFLOG_KEY_OOB_UID,
NFLOG_KEY_OOB_GID,
@@ -163,6 +170,7 @@ enum nflog_keys {
NFLOG_KEY_RAW_MAC_ADDRLEN,
NFLOG_KEY_RAW,
NFLOG_KEY_RAW_CT,
+ NFLOG_KEY_CT_INFO,
};
static struct ulogd_key output_keys[] = {
@@ -304,6 +312,24 @@ static struct ulogd_key output_keys[] = {
.flags = ULOGD_RETF_NONE,
.name = "oob.family",
},
+ [NFLOG_KEY_OOB_VERSION] = {
+ .type = ULOGD_RET_UINT8,
+ .flags = ULOGD_RETF_NONE,
+ .name = "oob.version",
+ .ipfix = {
+ .vendor = IPFIX_VENDOR_NETFILTER,
+ .field_id = IPFIX_NF_hook,
+ },
+ },
+ [NFLOG_KEY_OOB_RES_ID] = {
+ .type = ULOGD_RET_UINT16,
+ .flags = ULOGD_RETF_NONE,
+ .name = "oob.resid",
+ .ipfix = {
+ .vendor = IPFIX_VENDOR_IETF,
+ .field_id = IPFIX_flowStartSeconds,
+ },
+ },
[NFLOG_KEY_OOB_PROTOCOL] = {
.type = ULOGD_RET_UINT16,
.flags = ULOGD_RETF_NONE,
@@ -339,47 +365,67 @@ static struct ulogd_key output_keys[] = {
.flags = ULOGD_RETF_NONE,
.name = "ct",
},
+ [NFLOG_KEY_CT_INFO] = {
+ .type = ULOGD_RET_UINT32,
+ .flags = ULOGD_RETF_NONE,
+ .name = "ct_info",
+ },
};
-struct nf_conntrack *build_ct(struct nfgenmsg *nfmsg)
+void build_ct(struct nfgenmsg *nfmsg, struct connttrack *res)
{
#ifdef BUILD_NFCT
struct nlmsghdr *nlh =
(struct nlmsghdr *)((void *)nfmsg - sizeof(*nlh));
struct nlattr *attr, *ctattr = NULL;
- struct nf_conntrack *ct;
+ int found = 0;
+
+ res->ct = NULL;
+ res->info = (uint32_t)-1;
mnl_attr_for_each(attr, nlh, sizeof(struct nfgenmsg)) {
- if (mnl_attr_get_type(attr) == NFULA_CT) {
- ctattr = attr;
+ switch (mnl_attr_get_type(attr)) {
+ case NFULA_CT:
+ {
+ ctattr = attr;
+ found |= 1;
+ break;
+ }
+ case NFULA_CT_INFO: {
+ res->info = ntohl(mnl_attr_get_u32(attr));
+ found |= 2;
+ break;
+ }
+ }
+ if (found == 3) {
break;
}
}
- if (!ctattr)
- return NULL;
- ct = nfct_new();
- if (!ct) {
+ if (!ctattr) {
+ ulogd_log(ULOGD_INFO, "ct attribute not present\n");
+ return;
+ }
+
+ res->ct = nfct_new();
+ if (!res->ct) {
ulogd_log(ULOGD_ERROR, "failed to allocate nfct\n");
- return NULL;
+ return;
}
if (nfct_payload_parse(mnl_attr_get_payload(ctattr),
mnl_attr_get_payload_len(ctattr),
- nfmsg->nfgen_family, ct) < 0) {
+ nfmsg->nfgen_family, res->ct) < 0) {
ulogd_log(ULOGD_ERROR, "failed to parse nfct payload\n");
- nfct_destroy(ct);
- return NULL;
+ nfct_destroy(res->ct);
+ res->ct = NULL;
+ return;
}
-
- return ct;
-#else
- return NULL;
#endif
}
static inline int
-interp_packet(struct ulogd_pluginstance *upi, uint8_t pf_family,
- struct nflog_data *ldata, struct nf_conntrack *ct)
+interp_packet(struct ulogd_pluginstance *upi, struct nfgenmsg *header,
+ struct nflog_data *ldata, struct connttrack *ct)
{
struct ulogd_key *ret = upi->output.keys;
@@ -396,8 +442,9 @@ interp_packet(struct ulogd_pluginstance *upi, uint8_t pf_family,
uint32_t uid;
uint32_t gid;
- okey_set_u8(&ret[NFLOG_KEY_OOB_FAMILY],
- pf_family);
+ okey_set_u8(&ret[NFLOG_KEY_OOB_FAMILY], header->nfgen_family);
+ okey_set_u16(&ret[NFLOG_KEY_OOB_RES_ID], ntohs(header->res_id));
+ okey_set_u8(&ret[NFLOG_KEY_OOB_VERSION], header->version);
okey_set_u8(&ret[NFLOG_KEY_RAW_LABEL],
label_ce(upi->config_kset).u.value);
@@ -460,8 +507,10 @@ interp_packet(struct ulogd_pluginstance *upi, uint8_t pf_family,
okey_set_ptr(&ret[NFLOG_KEY_RAW], ldata);
- if (ct != NULL)
- okey_set_ptr(&ret[NFLOG_KEY_RAW_CT], ct);
+ if (ct->ct != NULL) {
+ okey_set_ptr(&ret[NFLOG_KEY_RAW_CT], ct->ct);
+ okey_set_u32(&ret[NFLOG_KEY_CT_INFO], ct->info);
+ }
ulogd_propagate_results(upi);
return 0;
@@ -537,22 +586,24 @@ static int msg_cb(struct nflog_g_handle *gh, struct nfgenmsg *nfmsg,
{
struct ulogd_pluginstance *upi = data;
struct ulogd_pluginstance *npi = NULL;
- void *ct = build_ct(nfmsg);
+ struct connttrack ct;
int ret = 0;
+ build_ct(nfmsg, &ct);
+
/* since we support the re-use of one instance in several
* different stacks, we duplicate the message to let them know */
llist_for_each_entry(npi, &upi->plist, plist) {
- ret = interp_packet(npi, nfmsg->nfgen_family, nfa, ct);
+ ret = interp_packet(npi, nfmsg, nfa, &ct);
if (ret != 0)
goto release_ct;
}
- ret = interp_packet(upi, nfmsg->nfgen_family, nfa, ct);
+ ret = interp_packet(upi, nfmsg, nfa, &ct);
release_ct:
#ifdef BUILD_NFCT
- if (ct != NULL)
- nfct_destroy(ct);
+ if (ct.ct != NULL)
+ nfct_destroy(ct.ct);
#endif
return ret;
@@ -598,8 +649,7 @@ static int start(struct ulogd_pluginstance *upi)
if ((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");
+ ulogd_log(ULOGD_FATAL, "error joining target network namespace\n");
goto out_ns;
}
@@ -611,8 +661,7 @@ static int start(struct ulogd_pluginstance *upi)
if ((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");
+ ulogd_log(ULOGD_FATAL, "error joining source network namespace\n");
goto out_handle;
}
/* join_netns_fd() closes the fd after successful join */
diff --git a/output/Makefile.am b/output/Makefile.am
index cdb49df..d6e9718 100644
--- a/output/Makefile.am
+++ b/output/Makefile.am
@@ -27,10 +27,6 @@ SUBDIRS = $(OPT_SUBDIR_PCAP) \
include $(top_srcdir)/Make_global.am
-AM_CPPFLAGS += ${LIBNETFILTER_ACCT_CFLAGS} \
- ${LIBNETFILTER_CONNTRACK_CFLAGS} \
- ${LIBNETFILTER_LOG_CFLAGS}
-
pkglib_LTLIBRARIES = ulogd_output_LOGEMU.la ulogd_output_SYSLOG.la \
ulogd_output_OPRINT.la ulogd_output_GPRINT.la \
ulogd_output_NACCT.la ulogd_output_XML.la \
@@ -55,12 +51,6 @@ ulogd_output_OPRINT_la_LDFLAGS = -avoid-version -module
ulogd_output_NACCT_la_SOURCES = ulogd_output_NACCT.c
ulogd_output_NACCT_la_LDFLAGS = -avoid-version -module
-ulogd_output_XML_la_SOURCES = ulogd_output_XML.c
-ulogd_output_XML_la_LIBADD = ${LIBNETFILTER_LOG_LIBS} \
- ${LIBNETFILTER_CONNTRACK_LIBS} \
- ${LIBNETFILTER_ACCT_LIBS}
-ulogd_output_XML_la_LDFLAGS = -avoid-version -module
-
ulogd_output_GRAPHITE_la_SOURCES = ulogd_output_GRAPHITE.c
ulogd_output_GRAPHITE_la_LDFLAGS = -avoid-version -module
@@ -69,3 +59,23 @@ ulogd_output_JSON_la_SOURCES = ulogd_output_JSON.c
ulogd_output_JSON_la_LIBADD = ${libjansson_LIBS}
ulogd_output_JSON_la_LDFLAGS = -avoid-version -module
endif
+
+ulogd_output_XML_la_SOURCES = ulogd_output_XML.c
+ulogd_output_XML_la_LIBADD =
+
+if BUILD_NFLOG
+ AM_CPPFLAGS += ${LIBNETFILTER_LOG_CFLAGS}
+ ulogd_output_XML_la_LIBADD += ${LIBNETFILTER_LOG_LIBS}
+endif
+
+if BUILD_NFCT
+ AM_CPPFLAGS += ${LIBNETFILTER_CONNTRACK_CFLAGS}
+ ulogd_output_XML_la_LIBADD += ${LIBNETFILTER_CONNTRACK_LIBS}
+endif
+
+if BUILD_NFACCT
+ AM_CPPFLAGS += ${LIBNETFILTER_ACCT_CFLAGS}
+ ulogd_output_XML_la_LIBADD += ${LIBNETFILTER_ACCT_LIBS}
+endif
+
+ulogd_output_XML_la_LDFLAGS = -avoid-version -module
\ No newline at end of file
diff --git a/output/pcap/Makefile.am b/output/pcap/Makefile.am
index b5064ea..ecaaa4c 100644
--- a/output/pcap/Makefile.am
+++ b/output/pcap/Makefile.am
@@ -7,3 +7,18 @@ pkglib_LTLIBRARIES = ulogd_output_PCAP.la
ulogd_output_PCAP_la_SOURCES = ulogd_output_PCAP.c
ulogd_output_PCAP_la_LIBADD = ${libpcap_LIBS}
ulogd_output_PCAP_la_LDFLAGS = -avoid-version -module
+
+if BUILD_NFLOG
+ AM_CPPFLAGS += ${LIBNETFILTER_LOG_CFLAGS}
+
+ pkglib_LTLIBRARIES += ulogd_output_PCAP_NFLOG.la
+
+ ulogd_output_PCAP_NFLOG_la_SOURCES = ulogd_output_PCAP_NFLOG.c
+ ulogd_output_PCAP_NFLOG_la_LIBADD = ${libpcap_LIBS} $(LIBNETFILTER_LOG_LIBS)
+ ulogd_output_PCAP_NFLOG_la_LDFLAGS = -avoid-version -module
+
+if BUILD_NFCT
+ AM_CPPFLAGS += ${LIBNETFILTER_CONNTRACK_CFLAGS}
+ ulogd_output_PCAP_NFLOG_la_LIBADD += $(LIBNETFILTER_CONNTRACK_LIBS)
+endif
+endif
diff --git a/output/pcap/ulogd_output_PCAP_NFLOG.c b/output/pcap/ulogd_output_PCAP_NFLOG.c
new file mode 100644
index 0000000..297e27e
--- /dev/null
+++ b/output/pcap/ulogd_output_PCAP_NFLOG.c
@@ -0,0 +1,573 @@
+/*
+ * ulogd_output_PCAP_CT.c - ULOGD plugin to merge packet and flow data
+ *
+ * Enriches NFLOG packets with connection tracking flow information
+ * by querying the NFCT table on each packet.
+ *
+ * Stack configuration:
+ * stack=log42:NFLOG,pcapct42:PCAP_NFLOG
+ */
+
+#include <errno.h>
+#include <endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/netfilter.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <linux/netfilter/nfnetlink_log.h>
+#include <libnetfilter_log/libnetfilter_log.h>
+#include <ulogd/ulogd.h>
+#include <ulogd/conffile.h>
+#include <sys/stat.h>
+
+#ifdef BUILD_NFCT
+ #include <linux/netfilter/nfnetlink_conntrack.h>
+ #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
+ #include <linux/netfilter/nf_conntrack_common.h>
+#endif
+
+#include <pcap/pcap.h> // for pcap_file_header, PCAP_VERSION_MAJOR/MINOR
+
+#define NFLOG_TLV_FLOW_INFO 19 /* Enriched flow metadata */
+
+#define LINKTYPE_NFLOG 0xEF
+#define TCPDUMP_MAGIC 0xa1b2c3d4
+
+static struct config_keyset pcap_nflog_kset = {
+ .num_ces = 2,
+ .ces = {
+ [0] = { .key = "file", .type = CONFIG_TYPE_STRING, .options = CONFIG_OPT_NONE, .u.string = "/tmp/ulogd_enriched.pcap" },
+ [1] = { .key = "sync", .type = CONFIG_TYPE_INT, .options = CONFIG_OPT_NONE, .u.value = 1 }
+ }
+};
+
+struct pcap_nflog_data {
+ FILE *output_file;
+};
+
+enum pcap_nflog_input_keys_id {
+ NFLOG_KEY_RAW_MAC = 0,
+ NFLOG_KEY_RAW_PCKT,
+ NFLOG_KEY_RAW_PCKTLEN,
+ NFLOG_KEY_RAW_PCKTCOUNT,
+ NFLOG_KEY_OOB_PREFIX,
+ NFLOG_KEY_OOB_TIME_SEC,
+ NFLOG_KEY_OOB_TIME_USEC,
+ NFLOG_KEY_OOB_MARK,
+ NFLOG_KEY_OOB_IFINDEX_IN,
+ NFLOG_KEY_OOB_IFINDEX_OUT,
+ NFLOG_KEY_OOB_HOOK,
+ NFLOG_KEY_RAW_MAC_LEN,
+ NFLOG_KEY_OOB_SEQ_LOCAL,
+ NFLOG_KEY_OOB_SEQ_GLOBAL,
+ NFLOG_KEY_OOB_FAMILY,
+ NFLOG_KEY_OOB_VERSION,
+ NFLOG_KEY_OOB_RES_ID,
+ NFLOG_KEY_OOB_PROTOCOL,
+ NFLOG_KEY_OOB_UID,
+ NFLOG_KEY_OOB_GID,
+ NFLOG_KEY_RAW_LABEL,
+ NFLOG_KEY_RAW_TYPE,
+ NFLOG_KEY_RAW_MAC_SADDR,
+ NFLOG_KEY_RAW_MAC_ADDRLEN,
+ NFLOG_KEY_RAW,
+ NFLOG_KEY_RAW_CT,
+ NFLOG_KEY_CT_INFO,
+ __PCAP_NFLOG_INPUT_KEY_MAX
+};
+
+static struct ulogd_key pcap_nflog_input_keys[] = {
+ [NFLOG_KEY_RAW_MAC] = { .type = ULOGD_RET_RAW, .name = "raw.mac" },
+ [NFLOG_KEY_RAW_PCKT] = { .type = ULOGD_RET_RAW, .name = "raw.pkt" },
+ [NFLOG_KEY_RAW_PCKTLEN] = { .type = ULOGD_RET_UINT32, .name = "raw.pktlen" },
+ [NFLOG_KEY_RAW_PCKTCOUNT] = { .type = ULOGD_RET_UINT32, .name = "raw.pktcount" },
+ [NFLOG_KEY_OOB_PREFIX] = { .type = ULOGD_RET_STRING, .name = "oob.prefix" },
+ [NFLOG_KEY_OOB_TIME_SEC] = { .type = ULOGD_RET_UINT32, .name = "oob.time.sec" },
+ [NFLOG_KEY_OOB_TIME_USEC] = { .type = ULOGD_RET_UINT32, .name = "oob.time.usec" },
+ [NFLOG_KEY_OOB_MARK] = { .type = ULOGD_RET_UINT32, .name = "oob.mark" },
+ [NFLOG_KEY_OOB_IFINDEX_IN] = { .type = ULOGD_RET_UINT32, .name = "oob.ifindex_in" },
+ [NFLOG_KEY_OOB_IFINDEX_OUT]= { .type = ULOGD_RET_UINT32, .name = "oob.ifindex_out" },
+ [NFLOG_KEY_OOB_HOOK] = { .type = ULOGD_RET_UINT8, .name = "oob.hook" },
+ [NFLOG_KEY_RAW_MAC_LEN] = { .type = ULOGD_RET_UINT16, .name = "raw.mac_len" },
+ [NFLOG_KEY_OOB_SEQ_LOCAL] = { .type = ULOGD_RET_UINT32, .name = "oob.seq.local" },
+ [NFLOG_KEY_OOB_SEQ_GLOBAL] = { .type = ULOGD_RET_UINT32, .name = "oob.seq.global" },
+ [NFLOG_KEY_OOB_FAMILY] = { .type = ULOGD_RET_UINT8, .name = "oob.family" },
+ [NFLOG_KEY_OOB_VERSION] = { .type = ULOGD_RET_UINT8, .name = "oob.version" },
+ [NFLOG_KEY_OOB_RES_ID] = { .type = ULOGD_RET_UINT16, .name = "oob.resid" },
+ [NFLOG_KEY_OOB_PROTOCOL] = { .type = ULOGD_RET_UINT16, .name = "oob.protocol" },
+ [NFLOG_KEY_OOB_UID] = { .type = ULOGD_RET_UINT32, .name = "oob.uid" },
+ [NFLOG_KEY_OOB_GID] = { .type = ULOGD_RET_UINT32, .name = "oob.gid" },
+ [NFLOG_KEY_RAW_LABEL] = { .type = ULOGD_RET_UINT8, .name = "raw.label" },
+ [NFLOG_KEY_RAW_TYPE] = { .type = ULOGD_RET_UINT16, .name = "raw.type" },
+ [NFLOG_KEY_RAW_MAC_SADDR] = { .type = ULOGD_RET_RAW, .name = "raw.mac.saddr" },
+ [NFLOG_KEY_RAW_MAC_ADDRLEN]= { .type = ULOGD_RET_UINT16, .name = "raw.mac.addrlen" },
+ [NFLOG_KEY_RAW] = { .type = ULOGD_RET_RAW, .name = "raw" },
+ [NFLOG_KEY_RAW_CT] = { .type = ULOGD_RET_RAW, .name = "ct" },
+ [NFLOG_KEY_CT_INFO] = { .type = ULOGD_RET_UINT32, .name = "ct_info" },
+};
+
+#define get(proc,key) (pp_is_valid(pi->input.keys, key) ? ikey_get_ ## proc (&pi->input.keys[key]) : 0)
+
+#define write_section_proc(section, type, getter) \
+{ \
+ uint##type##_t temp = getter; \
+ if (write_nflog_tlv(&buf_ptr, buf_end, section, &temp, sizeof(temp)) < 0) { \
+ return -1; \
+ } \
+}
+
+#define write_section(section, key, type) write_section_proc(section, type, get(u##type, key))
+
+#define write_u32_section(section, key) write_section(section, key, 32)
+#define write_u16_section(section, key) write_section(section, key, 16)
+
+static int write_pcap_header(FILE *f)
+{
+ struct pcap_file_header pcfh;
+ pcfh.magic = TCPDUMP_MAGIC;
+ pcfh.version_major = PCAP_VERSION_MAJOR;
+ pcfh.version_minor = PCAP_VERSION_MINOR;
+ pcfh.thiszone = 0;
+ pcfh.sigfigs = 0;
+ pcfh.snaplen = 4096;
+ pcfh.linktype = LINKTYPE_NFLOG;
+
+ return fwrite(&pcfh, sizeof(pcfh), 1, f) == 1 ? 0 : -1;
+}
+
+static int write_nflog_tlv(char **buf, const char *buf_end, uint16_t type,
+ const void *data, uint16_t data_len)
+{
+ uint16_t tlv_len = data_len + 4;
+ uint16_t padded_len = (tlv_len + 3) & ~3;
+
+ int delta = *buf + padded_len - buf_end;
+ if (0 <= delta) {
+ ulogd_log(ULOGD_NOTICE, "Cannot write %u. No space left in buffer (above = %d)\n", type, delta);
+ return -1;
+ }
+
+ *(uint16_t*)*buf = tlv_len;
+ *(uint16_t*)(*buf + 2) = type;
+ memcpy(*buf + 4, data, data_len);
+
+ if (padded_len > tlv_len)
+ memset(*buf + tlv_len, 0, padded_len - tlv_len);
+
+ *buf += padded_len;
+ return padded_len;
+}
+
+static uint64_t u32_to_be64(uint32_t x) {
+ uint64_t v = (uint64_t)x;
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+ v = __builtin_bswap64(v);
+#endif
+ return v;
+}
+
+#define nfct_get_attr_(ct, attr_id) (const char*)nfct_get_attr(ct, attr_id)
+
+#define print_ct(fmt, ...) \
+{ \
+ int res = snprintf(flow_info + pos, sizeof(flow_info) - pos, fmt "\n", __VA_ARGS__); \
+ if (res < 0) { \
+ ulogd_log(ULOGD_NOTICE, "CT: cannot write to buffer: %m\n"); \
+ goto print_ct_section; \
+ } \
+ pos += res; \
+}
+
+#define print_ct_attribute(attr_id, format, type) \
+{ \
+ if (nfct_attr_is_set(ct, attr_id)) { \
+ print_ct(format, nfct_get_attr_##type(ct, attr_id)); \
+ } \
+ if (pos >= sizeof(flow_info)) { \
+ goto print_ct_section; \
+ } \
+}
+
+#define print_ct_attribute_pair(attr1, attr2, format, type) \
+{ \
+ if (nfct_attr_is_set(ct, attr1) && nfct_attr_is_set(ct, attr2)) { \
+ print_ct(format, nfct_get_attr_##type(ct, attr1), nfct_get_attr_##type(ct, attr2)); \
+ } \
+ if (pos >= sizeof(flow_info)) { \
+ goto print_ct_section; \
+ } \
+}
+
+#define print_ct_ip4_pair(attr1, attr2, format) \
+{ \
+ if (nfct_attr_is_set(ct, attr1) && nfct_attr_is_set(ct, attr2)) { \
+ uint32_t ip1_be = nfct_get_attr_u32(ct, attr1); \
+ uint32_t ip2_be = nfct_get_attr_u32(ct, attr2); \
+ \
+ struct in_addr attr1_ip = { .s_addr = ip1_be }; \
+ struct in_addr attr2_ip = { .s_addr = ip2_be }; \
+ \
+ print_ct(format, inet_ntoa(attr1_ip), inet_ntoa(attr2_ip)); \
+ } \
+ \
+ if (pos >= sizeof(flow_info)) { \
+ goto print_ct_section; \
+ } \
+}
+
+#define print_ct_ip6_pair(attr1, attr2, format) \
+{ \
+ if (nfct_attr_is_set(ct, attr1) && nfct_attr_is_set(ct, attr2)) { \
+ const void * ip1_raw = nfct_get_attr(ct, attr1); \
+ const void * ip2_raw = nfct_get_attr(ct, attr2); \
+ \
+ char ip1_str[INET6_ADDRSTRLEN]; \
+ char ip2_str[INET6_ADDRSTRLEN]; \
+ \
+ inet_ntop(AF_INET6, ip1_raw, ip1_str, INET6_ADDRSTRLEN); \
+ inet_ntop(AF_INET6, ip2_raw, ip2_str, INET6_ADDRSTRLEN); \
+ \
+ print_ct(format, ip1_str, ip2_str); \
+ } \
+ \
+ if (pos >= sizeof(flow_info)) { \
+ goto print_ct_section; \
+ } \
+}
+
+static int write_pcap_nflog_packet(struct ulogd_pluginstance *pi, FILE *of)
+{
+ char buffer[4096];
+ char *buf_ptr = buffer;
+ const char *buf_end = buffer + sizeof(buffer);
+#ifdef BUILD_NFCT
+ struct nf_conntrack *ct = get(ptr, NFLOG_KEY_RAW_CT);
+#endif
+ struct nflog_data *nflog = get(ptr, NFLOG_KEY_RAW);
+
+ struct nfgenmsg pkt_hdr = {
+ .nfgen_family=get(u8, NFLOG_KEY_OOB_FAMILY),
+ .version=get(u8, NFLOG_KEY_OOB_VERSION),
+ .res_id=htons(get(u16, NFLOG_KEY_OOB_RES_ID))};
+ memcpy(buf_ptr, &pkt_hdr, sizeof(pkt_hdr));
+ buf_ptr += sizeof(pkt_hdr);
+
+ // --- NFULA_PACKET_HDR ---
+ { struct nfulnl_msg_packet_hdr pkt_hdr_tlv = {
+ .hw_protocol = htons(get(u16, NFLOG_KEY_OOB_PROTOCOL)),
+ .hook = get(u8, NFLOG_KEY_OOB_HOOK),
+ ._pad = 0
+ };
+ if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_PACKET_HDR,
+ &pkt_hdr_tlv, sizeof(pkt_hdr_tlv)) < 0) {
+ return -1;
+ }
+ }
+
+ // --- NFULA_MARK ---
+ write_u32_section(NFULA_MARK, NFLOG_KEY_OOB_MARK);
+
+ // --- NFULA_TIMESTAMP ---
+ {
+ struct nfulnl_msg_packet_timestamp ts = {
+ .sec = u32_to_be64(get(u32, NFLOG_KEY_OOB_TIME_SEC)),
+ .usec = u32_to_be64(get(u32, NFLOG_KEY_OOB_TIME_USEC))
+ };
+
+ if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_TIMESTAMP, &ts, sizeof(ts)) < 0) {
+ return -1;
+ }
+ }
+
+ // --- NFULA_IFINDEX_INDEV ---
+ write_u32_section(NFULA_IFINDEX_INDEV, NFLOG_KEY_OOB_IFINDEX_IN);
+
+ // --- NFULA_IFINDEX_OUTDEV ---
+ write_u32_section(NFULA_IFINDEX_OUTDEV, NFLOG_KEY_OOB_IFINDEX_OUT);
+
+ // --- NFULA_IFINDEX_PHYSINDEV ---
+ write_section_proc(NFULA_IFINDEX_PHYSINDEV, 32, nflog_get_physindev(nflog));
+
+ // --- NFULA_IFINDEX_PHYSOUTDEV ---
+ write_section_proc(NFULA_IFINDEX_PHYSOUTDEV, 32, nflog_get_physoutdev(nflog));
+
+ // --- NFULA_HWADDR ---
+ {
+ uint16_t len = get(u16, NFLOG_KEY_RAW_MAC_ADDRLEN);
+
+ if (len > 0) {
+ struct nfulnl_msg_packet_hw temp = {
+ .hw_addrlen = htons(len),
+ .hw_addr = {0},
+ };
+ if (pp_is_valid(pi->input.keys, NFLOG_KEY_RAW_MAC_SADDR)) {
+ void* value = ikey_get_ptr(&pi->input.keys[NFLOG_KEY_RAW_MAC_SADDR]);
+ if (value) {
+ memcpy(&temp.hw_addr, value, len);
+ }
+ }
+ if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_HWADDR, &temp, sizeof(temp)) < 0) {
+ return -1;
+ }
+ }
+ }
+
+ // --- NFULA_PAYLOAD ---
+ {
+ void *pkt_data = get(ptr, NFLOG_KEY_RAW_PCKT);
+ uint32_t pkt_len = get(u32, NFLOG_KEY_RAW_PCKTLEN);
+
+ if (pkt_data) {
+ if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_PAYLOAD, pkt_data, pkt_len) < 0) {
+ return -1;
+ }
+ }
+ }
+
+ // --- NFULA_PREFIX ---
+ { char *prefix = (char *)get(ptr, NFLOG_KEY_OOB_PREFIX);
+
+ if (prefix) {
+ if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_PREFIX, prefix, strlen(prefix)) < 0) {
+ return -1;
+ }
+ }
+ }
+
+ // --- NFULA_UID ---
+ write_u32_section(NFULA_UID, NFLOG_KEY_OOB_UID);
+
+ // --- NFULA_SEQ ---
+ write_u32_section(NFULA_SEQ, NFLOG_KEY_OOB_SEQ_LOCAL);
+
+ // --- NFULA_SEQ_GLOBAL ---
+ write_u32_section(NFULA_SEQ_GLOBAL, NFLOG_KEY_OOB_SEQ_GLOBAL);
+
+ // --- NFULA_GID ---
+ write_u32_section(NFULA_GID, NFLOG_KEY_OOB_GID);
+
+ // --- NFULA_HWTYPE ---
+ write_u16_section(NFULA_HWTYPE, NFLOG_KEY_RAW_TYPE);
+ // --- NFULA_HWLEN ---
+ write_u16_section(NFULA_HWLEN, NFLOG_KEY_RAW_MAC_LEN);
+ // --- NFULA_HWHEADER ---
+ {
+ char *data = get(ptr, NFLOG_KEY_RAW_MAC);
+ size_t len = get(u16, NFLOG_KEY_RAW_MAC_LEN);
+
+ if ((len > 0) && (data != NULL)) {
+ if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_HWHEADER, data, len) < 0) {
+ return -1;
+ }
+ }
+ }
+#ifdef BUILD_NFCT
+ if (ct != NULL) {
+ char flow_info[2048];
+ size_t pos = 0;
+
+ // --- NFULA_CT_INFO ---
+ write_u32_section(NFULA_CT_INFO, NFLOG_KEY_CT_INFO);
+
+ // --- NFULA_CT ---
+ print_ct_attribute(ATTR_ID, "id=%u", u32);
+ print_ct_attribute(ATTR_USE, "use=%u", u32);
+ print_ct_attribute(ATTR_STATUS, "status=0x%08x", u32);
+ print_ct_attribute(ATTR_TIMEOUT, "timeout=%u", u32);
+ print_ct_attribute(ATTR_MARK, "mark=%u", u32);
+ print_ct_attribute(ATTR_SECMARK, "security mark=%u", u32);
+
+ print_ct_attribute_pair(ATTR_ORIG_COUNTER_PACKETS, ATTR_ORIG_COUNTER_BYTES, "orig packets/bytes=%lu/%lu", u64);
+ print_ct_attribute_pair(ATTR_REPL_COUNTER_PACKETS, ATTR_REPL_COUNTER_BYTES, "repl packets/bytes=%lu/%lu", u64);
+
+ print_ct_ip4_pair(ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV4_DST, "orig ip4 src/dest=%s/%s");
+ print_ct_ip6_pair(ATTR_ORIG_IPV6_SRC, ATTR_ORIG_IPV6_DST, "orig ip6 src/dest=%s/%s");
+ print_ct_attribute_pair(ATTR_ORIG_PORT_SRC, ATTR_ORIG_PORT_DST, "orig port src/dest=%u/%u", u16);
+
+
+ print_ct_ip4_pair(ATTR_REPL_IPV4_SRC, ATTR_REPL_IPV4_DST, "repl ip4 src/dest=%s/%s");
+ print_ct_ip6_pair(ATTR_REPL_IPV6_SRC, ATTR_REPL_IPV6_DST, "repl ip6 src/dest=%s/%s");
+ print_ct_attribute_pair(ATTR_REPL_PORT_SRC, ATTR_REPL_PORT_DST, "repl port src/dest=%u/%u", u16);
+
+ print_ct_attribute_pair(ATTR_ORIG_L3PROTO, ATTR_REPL_L3PROTO, "l3proto orig/repl=%u/%u", u8);
+ print_ct_attribute_pair(ATTR_ORIG_L4PROTO, ATTR_REPL_L4PROTO, "l4proto orig/repl=%u/%u", u8);
+
+ print_ct_ip4_pair(ATTR_MASTER_IPV4_SRC, ATTR_MASTER_IPV4_DST, "master ip4 src/dest=%s/%s");
+ print_ct_ip6_pair(ATTR_MASTER_IPV6_SRC, ATTR_MASTER_IPV6_DST, "master ip6 src/dest=%s/%s");
+ print_ct_attribute_pair(ATTR_MASTER_PORT_SRC, ATTR_MASTER_PORT_DST, "master port src/dest=%u/%u", u16);
+ print_ct_attribute_pair(ATTR_MASTER_L3PROTO, ATTR_MASTER_L4PROTO, "master proto l3/l4=%u/%u", u8);
+
+ print_ct_attribute(ATTR_ZONE, "zone=%u", u16);
+ print_ct_attribute_pair(ATTR_ORIG_ZONE, ATTR_REPL_ZONE, "zone orig/repl=%u/%u", u16);
+ // tcp
+ print_ct_attribute(ATTR_TCP_STATE, "tcp_state=%u", u8);
+ print_ct_attribute_pair(ATTR_TCP_FLAGS_ORIG, ATTR_TCP_FLAGS_REPL, "tcp_flags orig/repl=%u/%u", u8);
+ print_ct_attribute_pair(ATTR_TCP_MASK_ORIG, ATTR_TCP_MASK_REPL, "tcp_mask orig/repl=%u/%u", u8);
+ // icmp
+ print_ct_attribute(ATTR_ICMP_TYPE, "icmp_type=%u", u8);
+ print_ct_attribute(ATTR_ICMP_CODE, "icmp_code=%u", u8);
+ print_ct_attribute(ATTR_ICMP_ID, "icmp_id=%u", u16);
+ // SCTP
+ print_ct_attribute(ATTR_SCTP_STATE, "sctp_state=%u", u8);
+ print_ct_attribute_pair(ATTR_SCTP_VTAG_ORIG, ATTR_SCTP_VTAG_REPL, "sctp vtag orig/repl=%u/%u", u8);
+ // DCCP
+ print_ct_attribute(ATTR_DCCP_STATE, "dccp_state=%u", u8);
+ print_ct_attribute(ATTR_DCCP_ROLE, "dccp_role=%u", u8);
+ print_ct_attribute(ATTR_DCCP_HANDSHAKE_SEQ, "dccp_handshake_seq=%lu", u64);
+
+ print_ct_attribute(ATTR_HELPER_NAME, "helper=%s", );
+ print_ct_attribute(ATTR_HELPER_INFO, "helper info=%s", );
+ print_ct_attribute(ATTR_SECCTX, "sec context=%s", );
+
+ // NAT
+ print_ct_ip4_pair(ATTR_SNAT_IPV4, ATTR_DNAT_IPV4, "NAT ip4 src/dest=%s/%s");
+ print_ct_ip6_pair(ATTR_SNAT_IPV6, ATTR_DNAT_IPV6, "NAT ip6 src/dest=%s/%s");
+ print_ct_attribute_pair(ATTR_SNAT_PORT, ATTR_DNAT_PORT, "NAT port src/dest=%u/%u", u16);
+
+ print_ct_attribute_pair(ATTR_ORIG_NAT_SEQ_CORRECTION_POS, ATTR_REPL_NAT_SEQ_CORRECTION_POS, "NAT SEQ Corr Pos orig/repl=%u/%u", u32);
+ print_ct_attribute_pair(ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE, ATTR_REPL_NAT_SEQ_OFFSET_BEFORE, "NAT SEQ Offset Bef orig/repl=%u/%u", u32);
+ print_ct_attribute_pair(ATTR_ORIG_NAT_SEQ_OFFSET_AFTER, ATTR_REPL_NAT_SEQ_OFFSET_AFTER, "NAT SEQ Offset After orig/repl=%u/%u", u32);
+
+print_ct_section:
+ if (pos > 0)
+ flow_info[pos - 1] = '\0';
+
+ if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_CT, flow_info, pos) < 0) {
+ return -1;
+ }
+ }
+#endif
+ size_t total_len = buf_ptr - buffer;
+ // --- PCAP record header ---
+ struct {
+ uint32_t ts_sec;
+ uint32_t ts_usec;
+ uint32_t caplen;
+ uint32_t len;
+ } ph;
+
+ ph.ts_sec = get(u32, NFLOG_KEY_OOB_TIME_SEC);
+ ph.ts_usec = get(u32, NFLOG_KEY_OOB_TIME_USEC);
+ ph.caplen = total_len; // length of data written
+ ph.len = total_len; // original length on wire
+
+ /* Write PCAP record */
+ if ((fwrite(&ph, sizeof(ph), 1, of) != 1) || (fwrite(buffer, total_len, 1, of) != 1)) {
+ ulogd_log(ULOGD_NOTICE, "Cannot write pcap record\n");
+ return -1;
+ }
+
+ if (pi->config_kset->ces[1].u.value)
+ fflush(of);
+
+ return 0;
+}
+
+static int pcap_nflog_output(struct ulogd_pluginstance *pi)
+{
+ struct pcap_nflog_data *data = (struct pcap_nflog_data*)pi->private;
+
+ if (!pp_is_valid(pi->input.keys, NFLOG_KEY_RAW_PCKT) || !pp_is_valid(pi->input.keys, NFLOG_KEY_RAW_PCKTLEN)) {
+ ulogd_log(ULOGD_NOTICE, "Cannot get payload. Skipping packet\n");
+ return -1;
+ }
+
+ if (!get(ptr, NFLOG_KEY_RAW)) {
+ ulogd_log(ULOGD_NOTICE, "No nflog pointer present\n");
+ return -1;
+ }
+
+ return write_pcap_nflog_packet(pi, data->output_file);
+}
+
+static int reopen_file(struct ulogd_pluginstance *pi)
+{
+ const char *filename = pi->config_kset->ces[0].u.string;
+ struct pcap_nflog_data *data = (struct pcap_nflog_data*)pi->private;
+
+ if (data->output_file)
+ fclose(data->output_file);
+ data->output_file = NULL;
+
+ FILE *of = fopen(filename, "a");
+ if (!of) {
+ ulogd_log(ULOGD_ERROR, "Cannot create output file %s: %m\n", filename);
+ return -EPERM;
+ }
+ data->output_file = of;
+
+ int empty = 1;
+ struct stat st;
+
+ if (stat(filename, &st) == 0) {
+ empty = st.st_size == 0;
+ }
+
+ if (!empty) {
+ return 0;
+ } else {
+ ulogd_log(ULOGD_NOTICE, "Output '%s' is empty. To write header\n", filename);
+ }
+
+ if (write_pcap_header(data->output_file) < 0) {
+ ulogd_log(ULOGD_ERROR, "Cannot write header to file '%s'\n", filename);
+ fclose(data->output_file);
+ data->output_file = NULL;
+ return -ENOSPC;
+ }
+ return 0;
+}
+
+static int pcap_nflog_init(struct ulogd_pluginstance *pi)
+{
+ struct pcap_nflog_data *data = (struct pcap_nflog_data*)pi->private;
+ memset(data, 0, sizeof(*data));
+
+ return reopen_file(pi);
+}
+
+static int pcap_nflog_destroy(struct ulogd_pluginstance *pi)
+{
+ struct pcap_nflog_data *data = (struct pcap_nflog_data*)pi->private;
+
+ if (data->output_file) {
+ fclose(data->output_file);
+ data->output_file = NULL;
+ }
+ return 0;
+}
+
+static void pcap_nflog_signal(struct ulogd_pluginstance *upi, int signal)
+{
+ ulogd_log(ULOGD_INFO, "pcap_nflog_signal: Delivered %d \n", signal);
+ if (signal == SIGHUP) {
+ ulogd_log(ULOGD_NOTICE, "reopening capture file\n");
+ reopen_file(upi);
+ }
+}
+
+static struct ulogd_plugin pcap_nflog_plugin = {
+ .name = "PCAP_NFLOG",
+ .input = { .keys = pcap_nflog_input_keys, .num_keys = ARRAY_SIZE(pcap_nflog_input_keys),
+ .type = ULOGD_DTYPE_PACKET | ULOGD_DTYPE_RAW },
+ .output = { .type = ULOGD_DTYPE_SINK },
+ .config_kset = &pcap_nflog_kset,
+ .priv_size = sizeof(struct pcap_nflog_data),
+ .start = pcap_nflog_init,
+ .stop = pcap_nflog_destroy,
+ .signal = &pcap_nflog_signal,
+ .interp = pcap_nflog_output,
+ .version = VERSION,
+};
+
+void __attribute__ ((constructor)) init(void)
+{
+ ulogd_register_plugin(&pcap_nflog_plugin);
+}
\ No newline at end of file
diff --git a/output/ulogd_output_XML.c b/output/ulogd_output_XML.c
index 0e1ae7b..c661174 100644
--- a/output/ulogd_output_XML.c
+++ b/output/ulogd_output_XML.c
@@ -20,7 +20,7 @@
#include <sys/types.h>
#include <inttypes.h>
-#include "../config.h"
+#include <ulogd/ulogd.h>
#ifdef BUILD_NFLOG
#include <libnetfilter_log/libnetfilter_log.h>
#endif
@@ -30,7 +30,6 @@
#ifdef BUILD_NFACCT
#include <libnetfilter_acct/libnetfilter_acct.h>
#endif
-#include <ulogd/ulogd.h>
#include <sys/param.h>
#include <time.h>
#include <errno.h>
diff --git a/src/ulogd.c b/src/ulogd.c
index 917ae3a..9004286 100644
--- a/src/ulogd.c
+++ b/src/ulogd.c
@@ -1111,6 +1111,7 @@ static int create_stack(const char *option)
/* add head of pluginstance stack to list of stacks */
llist_add(&stack->stack_list, &ulogd_pi_stacks);
+ ulogd_log(ULOGD_DEBUG, "pluginstance stack created: '%s'\n", option);
free(buf);
return 0;
diff --git a/ulogd.conf.in b/ulogd.conf.in
index 77d7086..8c91e1f 100644
--- a/ulogd.conf.in
+++ b/ulogd.conf.in
@@ -43,6 +43,7 @@ logfile="/var/log/ulogd.log"
#plugin="@pkglibdir@/ulogd_output_GPRINT.so"
#plugin="@pkglibdir@/ulogd_output_NACCT.so"
#plugin="@pkglibdir@/ulogd_output_PCAP.so"
+#plugin="@pkglibdir@/ulogd_output_PCAP_NFLOG.so"
#plugin="@pkglibdir@/ulogd_output_PGSQL.so"
#plugin="@pkglibdir@/ulogd_output_MYSQL.so"
#plugin="@pkglibdir@/ulogd_output_DBI.so"
--
2.51.0
^ permalink raw reply related [flat|nested] 2+ messages in thread* Re: [PATCH] Added netfilter output plugin with ability to write into pcap nflog packets
2025-11-09 9:23 [PATCH] Added netfilter output plugin with ability to write into pcap nflog packets Serhii Ivanov
@ 2025-11-10 18:41 ` Florian Westphal
0 siblings, 0 replies; 2+ messages in thread
From: Florian Westphal @ 2025-11-10 18:41 UTC (permalink / raw)
To: Serhii Ivanov; +Cc: netfilter-devel
Serhii Ivanov <icegood1980@gmail.com> wrote:
> Extend inpit nflog plugin
> to get more information
>
> Chenged conmfiguration files for
> proper dependencies to ntlog/ct/ctacc
This is way too many different changes in one patch,
sorry.
> diff --git a/include/ulogd/ulogd.h b/include/ulogd/ulogd.h
> index 29082df..b0d2236 100644
> --- a/include/ulogd/ulogd.h
> +++ b/include/ulogd/ulogd.h
> @@ -19,7 +19,7 @@
> #include <inttypes.h>
> #include <netinet/in.h>
> #include <string.h>
> -#include <config.h>
> +#include <config.h> // all config stuff there
Please drop this hunk.
> diff --git a/input/packet/Makefile.am b/input/packet/Makefile.am
> index 20c51ec..f63cc6e 100644
> --- a/input/packet/Makefile.am
> +++ b/input/packet/Makefile.am
> @@ -1,17 +1,21 @@
> include $(top_srcdir)/Make_global.am
>
> -AM_CPPFLAGS += ${LIBNETFILTER_LOG_CFLAGS} ${LIBNETFILTER_CONNTRACK_CFLAGS}
> -
> pkglib_LTLIBRARIES = ulogd_inppkt_UNIXSOCK.la
>
> ulogd_inppkt_UNIXSOCK_la_SOURCES = ulogd_inppkt_UNIXSOCK.c
> ulogd_inppkt_UNIXSOCK_la_LDFLAGS = -avoid-version -module
>
> if BUILD_NFLOG
> -pkglib_LTLIBRARIES += ulogd_inppkt_NFLOG.la
> + AM_CPPFLAGS += ${LIBNETFILTER_LOG_CFLAGS}
> +
> + pkglib_LTLIBRARIES += ulogd_inppkt_NFLOG.la
>
> -ulogd_inppkt_NFLOG_la_SOURCES = ulogd_inppkt_NFLOG.c
> -ulogd_inppkt_NFLOG_la_LDFLAGS = -avoid-version -module
> -ulogd_inppkt_NFLOG_la_LIBADD = $(LIBNETFILTER_LOG_LIBS) \
> - $(LIBNETFILTER_CONNTRACK_LIBS)
> + ulogd_inppkt_NFLOG_la_SOURCES = ulogd_inppkt_NFLOG.c
> + ulogd_inppkt_NFLOG_la_LDFLAGS = -avoid-version -module
> + ulogd_inppkt_NFLOG_la_LIBADD = $(LIBNETFILTER_LOG_LIBS)
> +
> +if BUILD_NFCT
> + AM_CPPFLAGS += $(LIBMNL_CFLAGS) ${LIBNETFILTER_CONNTRACK_CFLAGS}
> + ulogd_inppkt_NFLOG_la_LIBADD += $(LIBMNL_LIBS) $(LIBNETFILTER_CONNTRACK_LIBS)
> +endif
> endif
This looks like it includes additional, unrelated move of lines,
please do not do this, it makes it harder to review.
> +struct connttrack {
> + struct nf_conntrack *ct;
> + uint32_t info;
> +};
Typo, but please please follow what kernel is doing instead,
see below.
> #ifndef NFLOG_GROUP_DEFAULT
> #define NFLOG_GROUP_DEFAULT 0
> @@ -154,6 +159,8 @@ enum nflog_keys {
> NFLOG_KEY_OOB_SEQ_LOCAL,
> NFLOG_KEY_OOB_SEQ_GLOBAL,
> NFLOG_KEY_OOB_FAMILY,
> + NFLOG_KEY_OOB_VERSION,
> + NFLOG_KEY_OOB_RES_ID,
Indendation is off, this should be one tab and not 4 spaces.
> -struct nf_conntrack *build_ct(struct nfgenmsg *nfmsg)
> +void build_ct(struct nfgenmsg *nfmsg, struct connttrack *res)
Please use:
struct nf_conntrack *build_ct(struct nfgenmsg *nfmsg, uint32_t *info)
Alternatively, if you plan further changes that warrant a
result-structure please make one preparation patch and explain
that this is going to be needed to return N new attributes
to the caller.
> + res->ct = NULL;
> + res->info = (uint32_t)-1;
I think this should be set to 0, i.e.
*info = 0;
> mnl_attr_for_each(attr, nlh, sizeof(struct nfgenmsg)) {
> - if (mnl_attr_get_type(attr) == NFULA_CT) {
> - ctattr = attr;
> + switch (mnl_attr_get_type(attr)) {
> + case NFULA_CT:
> + {
> + ctattr = attr;
> + found |= 1;
> + break;
> + }
> + case NFULA_CT_INFO: {
> + res->info = ntohl(mnl_attr_get_u32(attr));
> + found |= 2;
> + break;
> + }
> + }
> + if (found == 3) {
> break;
I don't think this new 'found' variable is needed.
> @@ -396,8 +442,9 @@ interp_packet(struct ulogd_pluginstance *upi, uint8_t pf_family,
> uint32_t uid;
> uint32_t gid;
>
> - okey_set_u8(&ret[NFLOG_KEY_OOB_FAMILY],
> - pf_family);
> + okey_set_u8(&ret[NFLOG_KEY_OOB_FAMILY], header->nfgen_family);
Why is this change here in the first place?
I think the part that adds the version and res_id fields should
be extra patch + explanation/changelog why its added.
> if ((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");
> + ulogd_log(ULOGD_FATAL, "error joining target network namespace\n");
> goto out_ns;
> }
Please leave this as-is.
> diff --git a/output/pcap/ulogd_output_PCAP_NFLOG.c b/output/pcap/ulogd_output_PCAP_NFLOG.c
> new file mode 100644
> index 0000000..297e27e
> --- /dev/null
> +++ b/output/pcap/ulogd_output_PCAP_NFLOG.c
> @@ -0,0 +1,573 @@
> +/*
> + * ulogd_output_PCAP_CT.c - ULOGD plugin to merge packet and flow data
> + *
> + * Enriches NFLOG packets with connection tracking flow information
> + * by querying the NFCT table on each packet.
> + *
> + * Stack configuration:
> + * stack=log42:NFLOG,pcapct42:PCAP_NFLOG
> + */
> +
> +#include <errno.h>
> +#include <endian.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <time.h>
> +#include <sys/time.h>
> +#include <sys/socket.h>
> +#include <netinet/in.h>
> +#include <arpa/inet.h>
> +#include <linux/netfilter.h>
> +#include <linux/netfilter/nfnetlink.h>
> +#include <linux/netfilter/nfnetlink_log.h>
> +#include <libnetfilter_log/libnetfilter_log.h>
> +#include <ulogd/ulogd.h>
> +#include <ulogd/conffile.h>
> +#include <sys/stat.h>
> +
> +#ifdef BUILD_NFCT
> + #include <linux/netfilter/nfnetlink_conntrack.h>
> + #include <libnetfilter_conntrack/libnetfilter_conntrack.h>
> + #include <linux/netfilter/nf_conntrack_common.h>
> +#endif
> +
> +#include <pcap/pcap.h> // for pcap_file_header, PCAP_VERSION_MAJOR/MINOR
> +
> +#define NFLOG_TLV_FLOW_INFO 19 /* Enriched flow metadata */
> +
> +#define LINKTYPE_NFLOG 0xEF
> +#define TCPDUMP_MAGIC 0xa1b2c3d4
> +
> +static struct config_keyset pcap_nflog_kset = {
> + .num_ces = 2,
> + .ces = {
> + [0] = { .key = "file", .type = CONFIG_TYPE_STRING, .options = CONFIG_OPT_NONE, .u.string = "/tmp/ulogd_enriched.pcap" },
> + [1] = { .key = "sync", .type = CONFIG_TYPE_INT, .options = CONFIG_OPT_NONE, .u.value = 1 }
> + }
> +};
> +
> +struct pcap_nflog_data {
> + FILE *output_file;
> +};
> +
> +enum pcap_nflog_input_keys_id {
> + NFLOG_KEY_RAW_MAC = 0,
> + NFLOG_KEY_RAW_PCKT,
> + NFLOG_KEY_RAW_PCKTLEN,
> + NFLOG_KEY_RAW_PCKTCOUNT,
> + NFLOG_KEY_OOB_PREFIX,
> + NFLOG_KEY_OOB_TIME_SEC,
> + NFLOG_KEY_OOB_TIME_USEC,
> + NFLOG_KEY_OOB_MARK,
> + NFLOG_KEY_OOB_IFINDEX_IN,
> + NFLOG_KEY_OOB_IFINDEX_OUT,
> + NFLOG_KEY_OOB_HOOK,
> + NFLOG_KEY_RAW_MAC_LEN,
> + NFLOG_KEY_OOB_SEQ_LOCAL,
> + NFLOG_KEY_OOB_SEQ_GLOBAL,
> + NFLOG_KEY_OOB_FAMILY,
> + NFLOG_KEY_OOB_VERSION,
> + NFLOG_KEY_OOB_RES_ID,
> + NFLOG_KEY_OOB_PROTOCOL,
> + NFLOG_KEY_OOB_UID,
> + NFLOG_KEY_OOB_GID,
> + NFLOG_KEY_RAW_LABEL,
> + NFLOG_KEY_RAW_TYPE,
> + NFLOG_KEY_RAW_MAC_SADDR,
> + NFLOG_KEY_RAW_MAC_ADDRLEN,
> + NFLOG_KEY_RAW,
> + NFLOG_KEY_RAW_CT,
> + NFLOG_KEY_CT_INFO,
> + __PCAP_NFLOG_INPUT_KEY_MAX
> +};
> +
> +static struct ulogd_key pcap_nflog_input_keys[] = {
> + [NFLOG_KEY_RAW_MAC] = { .type = ULOGD_RET_RAW, .name = "raw.mac" },
> + [NFLOG_KEY_RAW_PCKT] = { .type = ULOGD_RET_RAW, .name = "raw.pkt" },
> + [NFLOG_KEY_RAW_PCKTLEN] = { .type = ULOGD_RET_UINT32, .name = "raw.pktlen" },
> + [NFLOG_KEY_RAW_PCKTCOUNT] = { .type = ULOGD_RET_UINT32, .name = "raw.pktcount" },
> + [NFLOG_KEY_OOB_PREFIX] = { .type = ULOGD_RET_STRING, .name = "oob.prefix" },
> + [NFLOG_KEY_OOB_TIME_SEC] = { .type = ULOGD_RET_UINT32, .name = "oob.time.sec" },
> + [NFLOG_KEY_OOB_TIME_USEC] = { .type = ULOGD_RET_UINT32, .name = "oob.time.usec" },
> + [NFLOG_KEY_OOB_MARK] = { .type = ULOGD_RET_UINT32, .name = "oob.mark" },
> + [NFLOG_KEY_OOB_IFINDEX_IN] = { .type = ULOGD_RET_UINT32, .name = "oob.ifindex_in" },
> + [NFLOG_KEY_OOB_IFINDEX_OUT]= { .type = ULOGD_RET_UINT32, .name = "oob.ifindex_out" },
> + [NFLOG_KEY_OOB_HOOK] = { .type = ULOGD_RET_UINT8, .name = "oob.hook" },
> + [NFLOG_KEY_RAW_MAC_LEN] = { .type = ULOGD_RET_UINT16, .name = "raw.mac_len" },
> + [NFLOG_KEY_OOB_SEQ_LOCAL] = { .type = ULOGD_RET_UINT32, .name = "oob.seq.local" },
> + [NFLOG_KEY_OOB_SEQ_GLOBAL] = { .type = ULOGD_RET_UINT32, .name = "oob.seq.global" },
> + [NFLOG_KEY_OOB_FAMILY] = { .type = ULOGD_RET_UINT8, .name = "oob.family" },
> + [NFLOG_KEY_OOB_VERSION] = { .type = ULOGD_RET_UINT8, .name = "oob.version" },
> + [NFLOG_KEY_OOB_RES_ID] = { .type = ULOGD_RET_UINT16, .name = "oob.resid" },
> + [NFLOG_KEY_OOB_PROTOCOL] = { .type = ULOGD_RET_UINT16, .name = "oob.protocol" },
> + [NFLOG_KEY_OOB_UID] = { .type = ULOGD_RET_UINT32, .name = "oob.uid" },
> + [NFLOG_KEY_OOB_GID] = { .type = ULOGD_RET_UINT32, .name = "oob.gid" },
> + [NFLOG_KEY_RAW_LABEL] = { .type = ULOGD_RET_UINT8, .name = "raw.label" },
> + [NFLOG_KEY_RAW_TYPE] = { .type = ULOGD_RET_UINT16, .name = "raw.type" },
> + [NFLOG_KEY_RAW_MAC_SADDR] = { .type = ULOGD_RET_RAW, .name = "raw.mac.saddr" },
> + [NFLOG_KEY_RAW_MAC_ADDRLEN]= { .type = ULOGD_RET_UINT16, .name = "raw.mac.addrlen" },
> + [NFLOG_KEY_RAW] = { .type = ULOGD_RET_RAW, .name = "raw" },
> + [NFLOG_KEY_RAW_CT] = { .type = ULOGD_RET_RAW, .name = "ct" },
> + [NFLOG_KEY_CT_INFO] = { .type = ULOGD_RET_UINT32, .name = "ct_info" },
> +};
> +
> +#define get(proc,key) (pp_is_valid(pi->input.keys, key) ? ikey_get_ ## proc (&pi->input.keys[key]) : 0)
> +
> +#define write_section_proc(section, type, getter) \
> +{ \
> + uint##type##_t temp = getter; \
> + if (write_nflog_tlv(&buf_ptr, buf_end, section, &temp, sizeof(temp)) < 0) { \
> + return -1; \
> + } \
> +}
> +
> +#define write_section(section, key, type) write_section_proc(section, type, get(u##type, key))
> +
> +#define write_u32_section(section, key) write_section(section, key, 32)
> +#define write_u16_section(section, key) write_section(section, key, 16)
> +
> +static int write_pcap_header(FILE *f)
> +{
> + struct pcap_file_header pcfh;
> + pcfh.magic = TCPDUMP_MAGIC;
> + pcfh.version_major = PCAP_VERSION_MAJOR;
> + pcfh.version_minor = PCAP_VERSION_MINOR;
> + pcfh.thiszone = 0;
> + pcfh.sigfigs = 0;
> + pcfh.snaplen = 4096;
> + pcfh.linktype = LINKTYPE_NFLOG;
> +
> + return fwrite(&pcfh, sizeof(pcfh), 1, f) == 1 ? 0 : -1;
> +}
> +
> +static int write_nflog_tlv(char **buf, const char *buf_end, uint16_t type,
> + const void *data, uint16_t data_len)
> +{
> + uint16_t tlv_len = data_len + 4;
> + uint16_t padded_len = (tlv_len + 3) & ~3;
> +
> + int delta = *buf + padded_len - buf_end;
> + if (0 <= delta) {
> + ulogd_log(ULOGD_NOTICE, "Cannot write %u. No space left in buffer (above = %d)\n", type, delta);
> + return -1;
> + }
> +
> + *(uint16_t*)*buf = tlv_len;
> + *(uint16_t*)(*buf + 2) = type;
> + memcpy(*buf + 4, data, data_len);
> +
> + if (padded_len > tlv_len)
> + memset(*buf + tlv_len, 0, padded_len - tlv_len);
> +
> + *buf += padded_len;
> + return padded_len;
> +}
> +
> +static uint64_t u32_to_be64(uint32_t x) {
> + uint64_t v = (uint64_t)x;
> +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
> + v = __builtin_bswap64(v);
> +#endif
> + return v;
> +}
> +
> +#define nfct_get_attr_(ct, attr_id) (const char*)nfct_get_attr(ct, attr_id)
> +
> +#define print_ct(fmt, ...) \
> +{ \
> + int res = snprintf(flow_info + pos, sizeof(flow_info) - pos, fmt "\n", __VA_ARGS__); \
> + if (res < 0) { \
> + ulogd_log(ULOGD_NOTICE, "CT: cannot write to buffer: %m\n"); \
> + goto print_ct_section; \
> + } \
> + pos += res; \
> +}
> +
> +#define print_ct_attribute(attr_id, format, type) \
> +{ \
> + if (nfct_attr_is_set(ct, attr_id)) { \
> + print_ct(format, nfct_get_attr_##type(ct, attr_id)); \
> + } \
> + if (pos >= sizeof(flow_info)) { \
> + goto print_ct_section; \
> + } \
> +}
> +
> +#define print_ct_attribute_pair(attr1, attr2, format, type) \
> +{ \
> + if (nfct_attr_is_set(ct, attr1) && nfct_attr_is_set(ct, attr2)) { \
> + print_ct(format, nfct_get_attr_##type(ct, attr1), nfct_get_attr_##type(ct, attr2)); \
> + } \
> + if (pos >= sizeof(flow_info)) { \
> + goto print_ct_section; \
> + } \
> +}
> +
> +#define print_ct_ip4_pair(attr1, attr2, format) \
> +{ \
> + if (nfct_attr_is_set(ct, attr1) && nfct_attr_is_set(ct, attr2)) { \
> + uint32_t ip1_be = nfct_get_attr_u32(ct, attr1); \
> + uint32_t ip2_be = nfct_get_attr_u32(ct, attr2); \
> + \
> + struct in_addr attr1_ip = { .s_addr = ip1_be }; \
> + struct in_addr attr2_ip = { .s_addr = ip2_be }; \
> + \
> + print_ct(format, inet_ntoa(attr1_ip), inet_ntoa(attr2_ip)); \
> + } \
> + \
> + if (pos >= sizeof(flow_info)) { \
> + goto print_ct_section; \
> + } \
> +}
> +
> +#define print_ct_ip6_pair(attr1, attr2, format) \
> +{ \
> + if (nfct_attr_is_set(ct, attr1) && nfct_attr_is_set(ct, attr2)) { \
> + const void * ip1_raw = nfct_get_attr(ct, attr1); \
> + const void * ip2_raw = nfct_get_attr(ct, attr2); \
> + \
> + char ip1_str[INET6_ADDRSTRLEN]; \
> + char ip2_str[INET6_ADDRSTRLEN]; \
> + \
> + inet_ntop(AF_INET6, ip1_raw, ip1_str, INET6_ADDRSTRLEN); \
> + inet_ntop(AF_INET6, ip2_raw, ip2_str, INET6_ADDRSTRLEN); \
> + \
> + print_ct(format, ip1_str, ip2_str); \
> + } \
> + \
> + if (pos >= sizeof(flow_info)) { \
> + goto print_ct_section; \
> + } \
> +}
> +
> +static int write_pcap_nflog_packet(struct ulogd_pluginstance *pi, FILE *of)
> +{
> + char buffer[4096];
> + char *buf_ptr = buffer;
> + const char *buf_end = buffer + sizeof(buffer);
> +#ifdef BUILD_NFCT
> + struct nf_conntrack *ct = get(ptr, NFLOG_KEY_RAW_CT);
> +#endif
> + struct nflog_data *nflog = get(ptr, NFLOG_KEY_RAW);
> +
> + struct nfgenmsg pkt_hdr = {
> + .nfgen_family=get(u8, NFLOG_KEY_OOB_FAMILY),
> + .version=get(u8, NFLOG_KEY_OOB_VERSION),
> + .res_id=htons(get(u16, NFLOG_KEY_OOB_RES_ID))};
> + memcpy(buf_ptr, &pkt_hdr, sizeof(pkt_hdr));
> + buf_ptr += sizeof(pkt_hdr);
> +
> + // --- NFULA_PACKET_HDR ---
> + { struct nfulnl_msg_packet_hdr pkt_hdr_tlv = {
> + .hw_protocol = htons(get(u16, NFLOG_KEY_OOB_PROTOCOL)),
> + .hook = get(u8, NFLOG_KEY_OOB_HOOK),
> + ._pad = 0
> + };
> + if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_PACKET_HDR,
> + &pkt_hdr_tlv, sizeof(pkt_hdr_tlv)) < 0) {
> + return -1;
> + }
> + }
> +
> + // --- NFULA_MARK ---
> + write_u32_section(NFULA_MARK, NFLOG_KEY_OOB_MARK);
> +
> + // --- NFULA_TIMESTAMP ---
> + {
> + struct nfulnl_msg_packet_timestamp ts = {
> + .sec = u32_to_be64(get(u32, NFLOG_KEY_OOB_TIME_SEC)),
> + .usec = u32_to_be64(get(u32, NFLOG_KEY_OOB_TIME_USEC))
> + };
> +
> + if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_TIMESTAMP, &ts, sizeof(ts)) < 0) {
> + return -1;
> + }
> + }
> +
> + // --- NFULA_IFINDEX_INDEV ---
> + write_u32_section(NFULA_IFINDEX_INDEV, NFLOG_KEY_OOB_IFINDEX_IN);
> +
> + // --- NFULA_IFINDEX_OUTDEV ---
> + write_u32_section(NFULA_IFINDEX_OUTDEV, NFLOG_KEY_OOB_IFINDEX_OUT);
> +
> + // --- NFULA_IFINDEX_PHYSINDEV ---
> + write_section_proc(NFULA_IFINDEX_PHYSINDEV, 32, nflog_get_physindev(nflog));
> +
> + // --- NFULA_IFINDEX_PHYSOUTDEV ---
> + write_section_proc(NFULA_IFINDEX_PHYSOUTDEV, 32, nflog_get_physoutdev(nflog));
> +
> + // --- NFULA_HWADDR ---
> + {
> + uint16_t len = get(u16, NFLOG_KEY_RAW_MAC_ADDRLEN);
> +
> + if (len > 0) {
> + struct nfulnl_msg_packet_hw temp = {
> + .hw_addrlen = htons(len),
> + .hw_addr = {0},
> + };
> + if (pp_is_valid(pi->input.keys, NFLOG_KEY_RAW_MAC_SADDR)) {
> + void* value = ikey_get_ptr(&pi->input.keys[NFLOG_KEY_RAW_MAC_SADDR]);
> + if (value) {
> + memcpy(&temp.hw_addr, value, len);
> + }
> + }
> + if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_HWADDR, &temp, sizeof(temp)) < 0) {
> + return -1;
> + }
> + }
> + }
> +
> + // --- NFULA_PAYLOAD ---
> + {
> + void *pkt_data = get(ptr, NFLOG_KEY_RAW_PCKT);
> + uint32_t pkt_len = get(u32, NFLOG_KEY_RAW_PCKTLEN);
> +
> + if (pkt_data) {
> + if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_PAYLOAD, pkt_data, pkt_len) < 0) {
> + return -1;
> + }
> + }
> + }
> +
> + // --- NFULA_PREFIX ---
> + { char *prefix = (char *)get(ptr, NFLOG_KEY_OOB_PREFIX);
> +
> + if (prefix) {
> + if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_PREFIX, prefix, strlen(prefix)) < 0) {
> + return -1;
> + }
> + }
> + }
> +
> + // --- NFULA_UID ---
> + write_u32_section(NFULA_UID, NFLOG_KEY_OOB_UID);
> +
> + // --- NFULA_SEQ ---
> + write_u32_section(NFULA_SEQ, NFLOG_KEY_OOB_SEQ_LOCAL);
> +
> + // --- NFULA_SEQ_GLOBAL ---
> + write_u32_section(NFULA_SEQ_GLOBAL, NFLOG_KEY_OOB_SEQ_GLOBAL);
> +
> + // --- NFULA_GID ---
> + write_u32_section(NFULA_GID, NFLOG_KEY_OOB_GID);
> +
> + // --- NFULA_HWTYPE ---
> + write_u16_section(NFULA_HWTYPE, NFLOG_KEY_RAW_TYPE);
> + // --- NFULA_HWLEN ---
> + write_u16_section(NFULA_HWLEN, NFLOG_KEY_RAW_MAC_LEN);
> + // --- NFULA_HWHEADER ---
> + {
> + char *data = get(ptr, NFLOG_KEY_RAW_MAC);
> + size_t len = get(u16, NFLOG_KEY_RAW_MAC_LEN);
> +
> + if ((len > 0) && (data != NULL)) {
> + if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_HWHEADER, data, len) < 0) {
> + return -1;
> + }
> + }
> + }
> +#ifdef BUILD_NFCT
> + if (ct != NULL) {
> + char flow_info[2048];
> + size_t pos = 0;
> +
> + // --- NFULA_CT_INFO ---
> + write_u32_section(NFULA_CT_INFO, NFLOG_KEY_CT_INFO);
> +
> + // --- NFULA_CT ---
> + print_ct_attribute(ATTR_ID, "id=%u", u32);
> + print_ct_attribute(ATTR_USE, "use=%u", u32);
> + print_ct_attribute(ATTR_STATUS, "status=0x%08x", u32);
> + print_ct_attribute(ATTR_TIMEOUT, "timeout=%u", u32);
> + print_ct_attribute(ATTR_MARK, "mark=%u", u32);
> + print_ct_attribute(ATTR_SECMARK, "security mark=%u", u32);
> +
> + print_ct_attribute_pair(ATTR_ORIG_COUNTER_PACKETS, ATTR_ORIG_COUNTER_BYTES, "orig packets/bytes=%lu/%lu", u64);
> + print_ct_attribute_pair(ATTR_REPL_COUNTER_PACKETS, ATTR_REPL_COUNTER_BYTES, "repl packets/bytes=%lu/%lu", u64);
> +
> + print_ct_ip4_pair(ATTR_ORIG_IPV4_SRC, ATTR_ORIG_IPV4_DST, "orig ip4 src/dest=%s/%s");
> + print_ct_ip6_pair(ATTR_ORIG_IPV6_SRC, ATTR_ORIG_IPV6_DST, "orig ip6 src/dest=%s/%s");
> + print_ct_attribute_pair(ATTR_ORIG_PORT_SRC, ATTR_ORIG_PORT_DST, "orig port src/dest=%u/%u", u16);
> +
> +
> + print_ct_ip4_pair(ATTR_REPL_IPV4_SRC, ATTR_REPL_IPV4_DST, "repl ip4 src/dest=%s/%s");
> + print_ct_ip6_pair(ATTR_REPL_IPV6_SRC, ATTR_REPL_IPV6_DST, "repl ip6 src/dest=%s/%s");
> + print_ct_attribute_pair(ATTR_REPL_PORT_SRC, ATTR_REPL_PORT_DST, "repl port src/dest=%u/%u", u16);
> +
> + print_ct_attribute_pair(ATTR_ORIG_L3PROTO, ATTR_REPL_L3PROTO, "l3proto orig/repl=%u/%u", u8);
> + print_ct_attribute_pair(ATTR_ORIG_L4PROTO, ATTR_REPL_L4PROTO, "l4proto orig/repl=%u/%u", u8);
> +
> + print_ct_ip4_pair(ATTR_MASTER_IPV4_SRC, ATTR_MASTER_IPV4_DST, "master ip4 src/dest=%s/%s");
> + print_ct_ip6_pair(ATTR_MASTER_IPV6_SRC, ATTR_MASTER_IPV6_DST, "master ip6 src/dest=%s/%s");
> + print_ct_attribute_pair(ATTR_MASTER_PORT_SRC, ATTR_MASTER_PORT_DST, "master port src/dest=%u/%u", u16);
> + print_ct_attribute_pair(ATTR_MASTER_L3PROTO, ATTR_MASTER_L4PROTO, "master proto l3/l4=%u/%u", u8);
> +
> + print_ct_attribute(ATTR_ZONE, "zone=%u", u16);
> + print_ct_attribute_pair(ATTR_ORIG_ZONE, ATTR_REPL_ZONE, "zone orig/repl=%u/%u", u16);
> + // tcp
> + print_ct_attribute(ATTR_TCP_STATE, "tcp_state=%u", u8);
> + print_ct_attribute_pair(ATTR_TCP_FLAGS_ORIG, ATTR_TCP_FLAGS_REPL, "tcp_flags orig/repl=%u/%u", u8);
> + print_ct_attribute_pair(ATTR_TCP_MASK_ORIG, ATTR_TCP_MASK_REPL, "tcp_mask orig/repl=%u/%u", u8);
> + // icmp
> + print_ct_attribute(ATTR_ICMP_TYPE, "icmp_type=%u", u8);
> + print_ct_attribute(ATTR_ICMP_CODE, "icmp_code=%u", u8);
> + print_ct_attribute(ATTR_ICMP_ID, "icmp_id=%u", u16);
> + // SCTP
> + print_ct_attribute(ATTR_SCTP_STATE, "sctp_state=%u", u8);
> + print_ct_attribute_pair(ATTR_SCTP_VTAG_ORIG, ATTR_SCTP_VTAG_REPL, "sctp vtag orig/repl=%u/%u", u8);
> + // DCCP
> + print_ct_attribute(ATTR_DCCP_STATE, "dccp_state=%u", u8);
> + print_ct_attribute(ATTR_DCCP_ROLE, "dccp_role=%u", u8);
> + print_ct_attribute(ATTR_DCCP_HANDSHAKE_SEQ, "dccp_handshake_seq=%lu", u64);
> +
> + print_ct_attribute(ATTR_HELPER_NAME, "helper=%s", );
> + print_ct_attribute(ATTR_HELPER_INFO, "helper info=%s", );
> + print_ct_attribute(ATTR_SECCTX, "sec context=%s", );
> +
> + // NAT
> + print_ct_ip4_pair(ATTR_SNAT_IPV4, ATTR_DNAT_IPV4, "NAT ip4 src/dest=%s/%s");
> + print_ct_ip6_pair(ATTR_SNAT_IPV6, ATTR_DNAT_IPV6, "NAT ip6 src/dest=%s/%s");
> + print_ct_attribute_pair(ATTR_SNAT_PORT, ATTR_DNAT_PORT, "NAT port src/dest=%u/%u", u16);
> +
> + print_ct_attribute_pair(ATTR_ORIG_NAT_SEQ_CORRECTION_POS, ATTR_REPL_NAT_SEQ_CORRECTION_POS, "NAT SEQ Corr Pos orig/repl=%u/%u", u32);
> + print_ct_attribute_pair(ATTR_ORIG_NAT_SEQ_OFFSET_BEFORE, ATTR_REPL_NAT_SEQ_OFFSET_BEFORE, "NAT SEQ Offset Bef orig/repl=%u/%u", u32);
> + print_ct_attribute_pair(ATTR_ORIG_NAT_SEQ_OFFSET_AFTER, ATTR_REPL_NAT_SEQ_OFFSET_AFTER, "NAT SEQ Offset After orig/repl=%u/%u", u32);
> +
> +print_ct_section:
> + if (pos > 0)
> + flow_info[pos - 1] = '\0';
> +
> + if (write_nflog_tlv(&buf_ptr, buf_end, NFULA_CT, flow_info, pos) < 0) {
> + return -1;
> + }
> + }
> +#endif
> + size_t total_len = buf_ptr - buffer;
> + // --- PCAP record header ---
> + struct {
> + uint32_t ts_sec;
> + uint32_t ts_usec;
> + uint32_t caplen;
> + uint32_t len;
> + } ph;
> +
> + ph.ts_sec = get(u32, NFLOG_KEY_OOB_TIME_SEC);
> + ph.ts_usec = get(u32, NFLOG_KEY_OOB_TIME_USEC);
> + ph.caplen = total_len; // length of data written
> + ph.len = total_len; // original length on wire
> +
> + /* Write PCAP record */
> + if ((fwrite(&ph, sizeof(ph), 1, of) != 1) || (fwrite(buffer, total_len, 1, of) != 1)) {
> + ulogd_log(ULOGD_NOTICE, "Cannot write pcap record\n");
> + return -1;
> + }
> +
> + if (pi->config_kset->ces[1].u.value)
> + fflush(of);
> +
> + return 0;
> +}
> +
> +static int pcap_nflog_output(struct ulogd_pluginstance *pi)
> +{
> + struct pcap_nflog_data *data = (struct pcap_nflog_data*)pi->private;
> +
> + if (!pp_is_valid(pi->input.keys, NFLOG_KEY_RAW_PCKT) || !pp_is_valid(pi->input.keys, NFLOG_KEY_RAW_PCKTLEN)) {
> + ulogd_log(ULOGD_NOTICE, "Cannot get payload. Skipping packet\n");
> + return -1;
> + }
> +
> + if (!get(ptr, NFLOG_KEY_RAW)) {
> + ulogd_log(ULOGD_NOTICE, "No nflog pointer present\n");
> + return -1;
> + }
> +
> + return write_pcap_nflog_packet(pi, data->output_file);
> +}
> +
> +static int reopen_file(struct ulogd_pluginstance *pi)
> +{
> + const char *filename = pi->config_kset->ces[0].u.string;
> + struct pcap_nflog_data *data = (struct pcap_nflog_data*)pi->private;
> +
> + if (data->output_file)
> + fclose(data->output_file);
> + data->output_file = NULL;
> +
> + FILE *of = fopen(filename, "a");
> + if (!of) {
> + ulogd_log(ULOGD_ERROR, "Cannot create output file %s: %m\n", filename);
> + return -EPERM;
> + }
> + data->output_file = of;
> +
> + int empty = 1;
> + struct stat st;
> +
> + if (stat(filename, &st) == 0) {
> + empty = st.st_size == 0;
> + }
> +
> + if (!empty) {
> + return 0;
> + } else {
> + ulogd_log(ULOGD_NOTICE, "Output '%s' is empty. To write header\n", filename);
> + }
> +
> + if (write_pcap_header(data->output_file) < 0) {
> + ulogd_log(ULOGD_ERROR, "Cannot write header to file '%s'\n", filename);
> + fclose(data->output_file);
> + data->output_file = NULL;
> + return -ENOSPC;
> + }
> + return 0;
> +}
> +
> +static int pcap_nflog_init(struct ulogd_pluginstance *pi)
> +{
> + struct pcap_nflog_data *data = (struct pcap_nflog_data*)pi->private;
> + memset(data, 0, sizeof(*data));
> +
> + return reopen_file(pi);
> +}
> +
> +static int pcap_nflog_destroy(struct ulogd_pluginstance *pi)
> +{
> + struct pcap_nflog_data *data = (struct pcap_nflog_data*)pi->private;
> +
> + if (data->output_file) {
> + fclose(data->output_file);
> + data->output_file = NULL;
> + }
> + return 0;
> +}
> +
> +static void pcap_nflog_signal(struct ulogd_pluginstance *upi, int signal)
> +{
> + ulogd_log(ULOGD_INFO, "pcap_nflog_signal: Delivered %d \n", signal);
> + if (signal == SIGHUP) {
> + ulogd_log(ULOGD_NOTICE, "reopening capture file\n");
> + reopen_file(upi);
> + }
> +}
> +
> +static struct ulogd_plugin pcap_nflog_plugin = {
> + .name = "PCAP_NFLOG",
> + .input = { .keys = pcap_nflog_input_keys, .num_keys = ARRAY_SIZE(pcap_nflog_input_keys),
> + .type = ULOGD_DTYPE_PACKET | ULOGD_DTYPE_RAW },
> + .output = { .type = ULOGD_DTYPE_SINK },
> + .config_kset = &pcap_nflog_kset,
> + .priv_size = sizeof(struct pcap_nflog_data),
> + .start = pcap_nflog_init,
> + .stop = pcap_nflog_destroy,
> + .signal = &pcap_nflog_signal,
> + .interp = pcap_nflog_output,
> + .version = VERSION,
> +};
> +
> +void __attribute__ ((constructor)) init(void)
> +{
> + ulogd_register_plugin(&pcap_nflog_plugin);
> +}
> \ No newline at end of file
> diff --git a/output/ulogd_output_XML.c b/output/ulogd_output_XML.c
Looks like this file should not be touched at all.
> +++ b/src/ulogd.c
likewise.
So to recap I think there are several changes in this patch
that should not be mixed, if possible:
1. refactoring of configure.ac/makefile.am
2. extensions to the nflog input plugin
3. new output plugin
4. unwanted changes that provide no value (== reformatting).
I don't know if 1) is needed, but having 2 and 3 separate
would help with review.
It also provides opportunity to provide a rationale
for the change(s) in the patch description.
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-11-10 18:47 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-09 9:23 [PATCH] Added netfilter output plugin with ability to write into pcap nflog packets Serhii Ivanov
2025-11-10 18:41 ` Florian Westphal
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).