From: Florian Westphal <fw@strlen.de>
To: Serhii Ivanov <icegood1980@gmail.com>
Cc: netfilter-devel@vger.kernel.org
Subject: Re: [PATCH] Added netfilter output plugin with ability to write into pcap nflog packets
Date: Mon, 10 Nov 2025 19:41:58 +0100 [thread overview]
Message-ID: <aRIx9qRHpZGQqi6T@strlen.de> (raw)
In-Reply-To: <20251109092304.1279619-2-icegood1980@gmail.com>
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.
next prev parent reply other threads:[~2025-11-10 18:47 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
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 message]
-- strict thread matches above, loose matches on Subject: below --
2025-11-30 17:00 Serhii Ivanov
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aRIx9qRHpZGQqi6T@strlen.de \
--to=fw@strlen.de \
--cc=icegood1980@gmail.com \
--cc=netfilter-devel@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.