From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Florian Westphal <fw@strlen.de>
Subject: [PATCH nf 3/4] netfilter: nfnetlink_log: fix up dev_parse_header usage
Date: Fri, 30 Oct 2026 16:44:32 +0100 [thread overview]
Message-ID: <20261030154433.13601-4-fw@strlen.de> (raw)
In-Reply-To: <20261030154433.13601-1-fw@strlen.de>
dev_parse_header() grabs the source hw address from the skb and stores
it in the given buffer.
e.g.: eth_header_parse() is simply:
int eth_header_parse(const struct sk_buff *skb,
const struct net_device *dev,
unsigned char *haddr)
{
memcpy(haddr, eth_hdr(skb->h_source, ETH_ALEN);
return ETH_ALEN;
}
Problem is that the compound structure used by the UABI in nfnetlink_log
and nfnetlink_queue has a fixed 8-byte size.
Enough for ETH_ALEN, but not necessarily enough for other address types.
Move this to a helper and allow sharing it between the copypasted users.
The struct layout of the two container structures have same layout:
add BUILD_BUG_ON checks to assert this.
Due to the nature of the dev_parse_header() helpers:
1. skb MUST have a valid mac header, else we read garbage
2. users MUST NOT use this when ingress device and skb->dev
have diverged (we can't know we would decode something
meaningful anymore).
3. Because container structure only has space for 8 octets,
we have to clamp the result.
Do all of this in the common helper.
This is an LLM finding, no bug was ever reported about this.
Fixes: 0597f2680d66 ("[NETFILTER]: Add new "nfnetlink_log" userspace packet logging facility")
Fixes: 7af4cc3fa158 ("[NETFILTER]: Add "nfnetlink_queue" netfilter queue handler over nfnetlink")
Signed-off-by: Florian Westphal <fw@strlen.de>
---
net/netfilter/nfnetlink_log.c | 18 ++---
net/netfilter/nfnetlink_log_common.h | 99 ++++++++++++++++++++++++++++
net/netfilter/nfnetlink_queue.c | 18 ++---
3 files changed, 107 insertions(+), 28 deletions(-)
create mode 100644 net/netfilter/nfnetlink_log_common.h
diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c
index 0b590c117f4c..29530b753928 100644
--- a/net/netfilter/nfnetlink_log.c
+++ b/net/netfilter/nfnetlink_log.c
@@ -47,6 +47,8 @@
#include <net/netfilter/nf_conntrack.h>
#endif
+#include "nfnetlink_log_common.h"
+
#define NFULNL_COPY_DISABLED 0xff
#define NFULNL_NLBUFSIZ_DEFAULT NLMSG_GOODSIZE
#define NFULNL_TIMEOUT_DEFAULT 100 /* every second */
@@ -587,20 +589,8 @@ __build_packet_message(struct nfnl_log_net *log,
nla_put_be32(inst->skb, NFULA_MARK, htonl(skb->mark)))
goto nla_put_failure;
- if (indev && skb->dev &&
- skb_mac_header_was_set(skb) &&
- skb_mac_header_len(skb) != 0) {
- struct nfulnl_msg_packet_hw phw;
- int len;
-
- memset(&phw, 0, sizeof(phw));
- len = dev_parse_header(skb, phw.hw_addr);
- if (len > 0) {
- phw.hw_addrlen = htons(len);
- if (nla_put(inst->skb, NFULA_HWADDR, sizeof(phw), &phw))
- goto nla_put_failure;
- }
- }
+ if (nfnetlink_put_hwaddr(inst->skb, skb, indev, NFULA_HWADDR))
+ goto nla_put_failure;
if (indev && skb_mac_header_was_set(skb)) {
if (nla_put_be16(inst->skb, NFULA_HWTYPE, htons(skb->dev->type)) ||
diff --git a/net/netfilter/nfnetlink_log_common.h b/net/netfilter/nfnetlink_log_common.h
new file mode 100644
index 000000000000..755a3364c8a0
--- /dev/null
+++ b/net/netfilter/nfnetlink_log_common.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _NF_NETLINK_LOG_COMMON_H
+#define _NF_NETLINK_LOG_COMMON_H
+
+#include <linux/netfilter/nfnetlink_queue.h>
+#include <linux/netfilter/nfnetlink_log.h>
+
+/* nfnetlink_log and nfnetlink_queue suffer from copypaste; avoid adding
+ * more and place shared code here.
+ */
+static __always_inline void assert_nfqnl_nfunl_hdrs(void)
+{
+ struct nfulnl_msg_packet_hw phwu;
+ struct nfqnl_msg_packet_hw phwq;
+
+ BUILD_BUG_ON(sizeof(phwu) != sizeof(phwq));
+
+#define X(memb) offsetof(struct nfulnl_msg_packet_hw, memb) != \
+ offsetof(struct nfqnl_msg_packet_hw, memb)
+
+ BUILD_BUG_ON(X(hw_addrlen));
+ BUILD_BUG_ON(X(hw_addr));
+#undef X
+#define X(memb) sizeof(phwu.memb) != sizeof(phwq.memb)
+ BUILD_BUG_ON(X(hw_addrlen));
+#undef X
+}
+
+/**
+ * nfnetlink_put_hwaddr - Serialize the L2 hardware address into a netlink attribute.
+ * @nlskb: netlink message to append the attribute to.
+ * @skb: the packet whose hardware address is being serialized.
+ * @indev: the input device; must equal @skb->dev.
+ * @attr: netlink attribute type identifier.
+ *
+ * Extracts the MAC header from @skb using dev_parse_header() and appends
+ * it as a netlink attribute of type @attr to @nlskb.
+ *
+ * UAPI NOTE: The attribute is serialized in a legacy container structure
+ * (nfnl_msg_packet_hw) rather than a plain nla_put(hwaddr, len). The
+ * hw_addrlen member in that structure is redundant -- userspace already
+ * knows the size from the netlink attribute length -- but is retained
+ * for ABI compatibility. nfnetlink_queue duplicates this layout; both
+ * structs are layout-identical, so this one helper serves both.
+ *
+ * The maximum address length is 8 octets (MAX_ADDR_LEN / ETH_ALEN upper
+ * bound), even though netlink itself supports longer addresses. This is a
+ * limitation of the compound-structure ABI, not of netlink.
+ *
+ * This helper checks:
+ * - @skb->dev is non-NULL.
+ * - @indev is equal @skb->dev; dev_parse_header() dereferences
+ * dev->header_ops on skb->dev, so a mismatched device (e.g. egress
+ * device on an egress path) would parse the wrong L2 header.
+ * - L2 header is present.
+ *
+ * Return: -EMSGSIZE if the tailroom of the skb is insufficient to store
+ * the attribute header and payload.
+ */
+static inline int
+nfnetlink_put_hwaddr(struct sk_buff *nlskb,
+ const struct sk_buff *skb,
+ const struct net_device *indev,
+ u16 attr)
+{
+ char hw_addr[MAX_ADDR_LEN];
+ unsigned int mac_len;
+ int len;
+
+ if (!skb->dev ||
+ indev != skb->dev ||
+ !skb_mac_header_was_set(skb))
+ return 0;
+
+ mac_len = skb_mac_header_len(skb);
+ if (mac_len == 0)
+ return 0;
+
+ len = dev_parse_header(skb, hw_addr);
+ if (len > 0) {
+ struct nfulnl_msg_packet_hw phw;
+ const int max_hwlen = (int)sizeof(phw.hw_addr);
+
+ assert_nfqnl_nfunl_hdrs();
+
+ /* UAPI limitation, must clamp. */
+ if (len > max_hwlen)
+ len = max_hwlen;
+
+ memset(&phw, 0, sizeof(phw));
+ memcpy(phw.hw_addr, hw_addr, len);
+ phw.hw_addrlen = htons(len);
+
+ return nla_put(nlskb, attr, sizeof(phw), &phw);
+ }
+
+ return 0;
+}
+#endif
diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c
index 90d887546f7d..7c0fb4777852 100644
--- a/net/netfilter/nfnetlink_queue.c
+++ b/net/netfilter/nfnetlink_queue.c
@@ -56,6 +56,8 @@
#include <net/netfilter/nf_conntrack.h>
#endif
+#include "nfnetlink_log_common.h"
+
#define NFQNL_QMAX_DEFAULT 1024
#define NFQNL_HASH_MIN 8
#define NFQNL_HASH_MAX 32768
@@ -906,20 +908,8 @@ nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
nla_put_be32(skb, NFQA_PRIORITY, htonl(entskb->priority)))
goto nla_put_failure;
- if (indev && entskb->dev &&
- skb_mac_header_was_set(entskb) &&
- skb_mac_header_len(entskb) != 0) {
- struct nfqnl_msg_packet_hw phw;
- int len;
-
- memset(&phw, 0, sizeof(phw));
- len = dev_parse_header(entskb, phw.hw_addr);
- if (len) {
- phw.hw_addrlen = htons(len);
- if (nla_put(skb, NFQA_HWADDR, sizeof(phw), &phw))
- goto nla_put_failure;
- }
- }
+ if (nfnetlink_put_hwaddr(skb, entskb, indev, NFQA_HWADDR))
+ goto nla_put_failure;
if (nfqnl_put_bridge(entry, skb) < 0)
goto nla_put_failure;
--
2.56.0
next prev parent reply other threads:[~2026-10-30 15:44 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-30 15:44 [PATCH nf 0/4] netfilter: logging cleanups Florian Westphal
2026-10-30 15:44 ` [PATCH nf 1/4] netfilter: nf_log: remove dead ARPHRD_SIT handling from MAC/HW header logging Florian Westphal
2026-10-30 15:44 ` [PATCH nf 2/4] netfilter: nf_log_syslog: fix MAC header dump to use real header length Florian Westphal
2026-10-30 15:44 ` Florian Westphal [this message]
2026-10-30 15:44 ` [PATCH nf 4/4] netfilter: nfnetlink_log: rework mac header logging Florian Westphal
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=20261030154433.13601-4-fw@strlen.de \
--to=fw@strlen.de \
--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.