[PATCH nf 1/1] netfilter: h323: keep NAT mangling aligned with parsed transport

Ren Wei <[email protected]> Tue, 28 Jul 2026 01:58:23 +0800
Newsgroups gmane.comp.security.firewalls.netfilter.devel
Message-ID <68faab5bf406aad612233e76994c649fbc547b8d.1785133455.git.zhilinz@nebusec.ai>
From: Zhiling Zou <[email protected]>

nf_nat_h323's set_addr() chooses the TCP or UDP NAT mangler from the
packet's current IPv4 protocol byte. H.323 conntrack helpers run later
from the conntrack helper path, so a packet can be accepted and assigned
to a UDP RAS or TCP signaling helper before another hook rewrites the IP
protocol byte.

When the current IP protocol no longer matches the helper's transport,
the parser and NAT mangler use different transport-header bases. In the
RAS case, ras_help() parses addresses relative to the UDP payload, while
set_addr() can call the TCP mangler and use a forged TCP data offset.
That can make the NAT match offset extend past the skb tail.

Select the mangler from nf_ct_protonum(ct), the stable L4 protocol stored
in the conntrack tuple, instead of the live IP header. H.245 parsing is
TPKT/TCP based even though the registered H.245 helper can be bound to a
UDP conntrack from userspace; ignore those non-TCP bindings before the
TCP parser feeds offsets to NAT.

Fixes: 5e35941d9901 ("[NETFILTER]: Add H.323 conntrack/NAT helper")
Cc: [email protected]
Reported-by: Vega <[email protected]>
Assisted-by: Codex:gpt-5.4
Signed-off-by: Zhiling Zou <[email protected]>
Signed-off-by: Ren Wei <[email protected]>
---
 net/ipv4/netfilter/nf_nat_h323.c       | 9 +++++++--
 net/netfilter/nf_conntrack_h323_main.c | 4 ++++
 2 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c
index 183e8a3ff2bab..98d50b3a6be2c 100644
--- a/net/ipv4/netfilter/nf_nat_h323.c
+++ b/net/ipv4/netfilter/nf_nat_h323.c
@@ -37,7 +37,8 @@ static int set_addr(struct sk_buff *skb, unsigned int protoff,
 	buf.port = port;
 	addroff += dataoff;
 
-	if (ip_hdr(skb)->protocol == IPPROTO_TCP) {
+	switch (nf_ct_protonum(ct)) {
+	case IPPROTO_TCP:
 		if (!nf_nat_mangle_tcp_packet(skb, ct, ctinfo,
 					      protoff, addroff, sizeof(buf),
 					      (char *) &buf, sizeof(buf))) {
@@ -51,7 +52,8 @@ static int set_addr(struct sk_buff *skb, unsigned int protoff,
 		if (th == NULL)
 			return -1;
 		*data = skb->data + ip_hdrlen(skb) + th->doff * 4 + dataoff;
-	} else {
+		break;
+	case IPPROTO_UDP:
 		if (!nf_nat_mangle_udp_packet(skb, ct, ctinfo,
 					      protoff, addroff, sizeof(buf),
 					      (char *) &buf, sizeof(buf))) {
@@ -62,6 +64,9 @@ static int set_addr(struct sk_buff *skb, unsigned int protoff,
 		 * or pull everything in a linear buffer, so we can safely
 		 * use the skb pointers now */
 		*data = skb->data + ip_hdrlen(skb) + sizeof(struct udphdr);
+		break;
+	default:
+		return -1;
 	}
 
 	return 0;
diff --git a/net/netfilter/nf_conntrack_h323_main.c b/net/netfilter/nf_conntrack_h323_main.c
index 24931e379985b..9cac3e5ab39ef 100644
--- a/net/netfilter/nf_conntrack_h323_main.c
+++ b/net/netfilter/nf_conntrack_h323_main.c
@@ -539,6 +539,10 @@ static int h245_help(struct sk_buff *skb, unsigned int protoff,
 	if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY)
 		return NF_ACCEPT;
 
+	/* H.245 is TPKT/TCP; ignore manual bindings to other L4 protocols. */
+	if (nf_ct_protonum(ct) != IPPROTO_TCP)
+		return NF_ACCEPT;
+
 	pr_debug("nf_ct_h245: skblen = %u\n", skb->len);
 
 	spin_lock_bh(&nf_h323_lock);
-- 
2.43.0