Linux Netfilter development
 help / color / mirror / Atom feed
From: Florian Westphal <fw@strlen.de>
To: <netfilter-devel@vger.kernel.org>
Cc: Florian Westphal <fw@strlen.de>
Subject: [PATCH v3 nf 5/6] netfilter: nf_conntrack: harden helper invocation with tuple revalidation
Date: Fri, 18 Sep 2026 16:58:08 +0200	[thread overview]
Message-ID: <20260918145809.12938-6-fw@strlen.de> (raw)
In-Reply-To: <20260918145809.12938-1-fw@strlen.de>

Complete hardening of the connection tracking helper invocation sequence
initiated in the previous refactoring commits.

1. Tuple Verification: Extracts the *current* tuple from the packet and
   verify it matches the connection tracking entry's tuple.
   This prevents helpers from being invoked on packets that may have been
   modified after the initial connection lookup, e.g. via act_ct ->
   pedit.  This also prevents the helper from operating on ICMP(v6) PMTU
   errors.

2. TCP Header Sanity: For TCP traffic, the patch introduces a check to
   ensure a full and sane TCP header is present.

Also adds missing 'nhoff' to ipv6_skip_exthdr(), this would be required
for conntrack over bridges.

Fixes: 9fb9cbb1082d ("[NETFILTER]: Add nf_conntrack subsystem.")
Signed-off-by: Florian Westphal <fw@strlen.de>
---
 v3: also add nhoff in ipv6.  LLM reports this even though its unrelated
 and we never got bug reports about this. Also add nf_ct_invert_tuple
 return value check and refuse huge protoff sooner.

 net/netfilter/nf_conntrack_proto.c | 43 ++++++++++++++++++++++++++++--
 1 file changed, 41 insertions(+), 2 deletions(-)

diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c
index 18125aa29e0d..43bf3a71dbbf 100644
--- a/net/netfilter/nf_conntrack_proto.c
+++ b/net/netfilter/nf_conntrack_proto.c
@@ -136,6 +136,9 @@ static bool nf_confirm_get_protoff(struct sk_buff *skb, struct net *net,
 				   enum ip_conntrack_info ctinfo,
 				   unsigned int *protoffp, u8 *pnum)
 {
+	unsigned int nhoff = skb_network_offset(skb);
+	struct nf_conntrack_tuple tuple, invert;
+	enum ip_conntrack_dir dir;
 	unsigned int protoff;
 	__be16 frag_off;
 	int start;
@@ -144,12 +147,13 @@ static bool nf_confirm_get_protoff(struct sk_buff *skb, struct net *net,
 	case NFPROTO_IPV4:
 		if (ip_is_fragment(ip_hdr(skb)))
 			return false;
-		protoff = skb_network_offset(skb) + ip_hdrlen(skb);
+		protoff = nhoff + ip_hdrlen(skb);
 		*pnum = ip_hdr(skb)->protocol;
 		break;
 	case NFPROTO_IPV6:
 		*pnum = ipv6_hdr(skb)->nexthdr;
-		start = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), pnum, &frag_off);
+		start = ipv6_skip_exthdr(skb, nhoff + sizeof(struct ipv6hdr),
+					 pnum, &frag_off);
 		if (start < 0 || frag_off)
 			return false;
 
@@ -160,6 +164,41 @@ static bool nf_confirm_get_protoff(struct sk_buff *skb, struct net *net,
 		return false;
 	}
 
+	if (protoff > skb->len)
+		return false;
+
+	if (!nf_ct_get_tuplepr(skb, nhoff, nf_ct_l3num(ct), net, &tuple))
+		return false;
+
+	dir = CTINFO2DIR(ctinfo);
+	if (!nf_ct_invert_tuple(&invert, &tuple))
+		return false;
+
+	/* This is called after L3/L4 headers have been mangled by NAT:
+	 * Packet in original direction has been subject to SNAT, i.e.
+	 * inverted reply dir.
+	 * Packet in reply direction has been subject to DNAT, i.e.
+	 * inverted original direction.
+	 */
+	if (!nf_ct_tuple_equal(&invert, nf_ct_tuple(ct, !dir)))
+		return false;
+
+	/* Validate that a full, sane TCP header (including options) is
+	 * present at protoff before helpers/seqadj are allowed to touch it.
+	 */
+	if (tuple.dst.protonum == IPPROTO_TCP) {
+		unsigned int tcplen = skb->len - protoff;
+		const struct tcphdr *th;
+		struct tcphdr _tcph;
+
+		th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
+		if (!th)
+			return false;
+
+		if (th->doff * 4 < sizeof(*th) || tcplen < th->doff * 4)
+			return false;
+	}
+
 	*protoffp = protoff;
 	return true;
 }
-- 
2.55.0


  parent reply	other threads:[~2026-09-18 15:40 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-18 14:58 [PATCH nf v3 0/6] netfilter: harden conntrack vs ingress pipeline rewrites Florian Westphal
2026-09-18 14:58 ` [PATCH v3 nf 1/6] netfilter: nf_conntrack: validate skb->_nfct and packet headers Florian Westphal
2026-09-18 14:58 ` [PATCH v3 nf 2/6] netfilter: nf_conntrack: refactor helper call logic in nf_confirm() Florian Westphal
2026-09-18 14:58 ` [PATCH v3 nf 3/6] netfilter: nf_conntrack: verify L4 protocol before calling helper Florian Westphal
2026-09-18 14:58 ` [PATCH v3 nf 4/6] netfilter: conntrack: replace open-coded helper invocation Florian Westphal
2026-09-18 14:58 ` Florian Westphal [this message]
2026-09-18 14:58 ` [PATCH v3 nf 6/6] netfilter: nft_ct: validate timeout object protocol 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=20260918145809.12938-6-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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox