[PATCH] fix for netfilter/nat/pppoe crashes (hopefully)

Darrell Dieringer netfilter@darrelldieringer.com
Tue, 31 Jul 2001 12:04:28 -0500


Hello,

Allow me to ask a linux newbie question... I've seen patches posted many
times, but I don't know how to apply them.  How does one apply a posted
patch?  There seems to be some amount of implied wisdom necessary to deal
with them, and I just don't know what it is.

Please, for the benefit of the great number of subscribers who probably have
a similar question, please keep the flames low.  I love reading how-to's and
searching for info, but sometimes a person needs to learn A, B, & C before
they can even begin attacking D, and it gets pretty frustrating.

Thanks,

D

-----Original Message-----
From: netfilter-admin@lists.samba.org
[mailto:netfilter-admin@lists.samba.org]On Behalf Of Marc Boucher
Sent: Tuesday, July 31, 2001 11:43 AM
To: netfilter@lists.samba.org; netfilter-devel@lists.samba.org;
netdev@oss.sgi.com
Cc: mostrows@us.ibm.com; mostrows@speakeasy.net
Subject: [PATCH] fix for netfilter/nat/pppoe crashes (hopefully)


Hi folks,

Enclosed is a patch which should eliminate the crashes involving
netfilter/iptables/nat (and often pppoe) that several people have been
experiencing under kernels >= 2.4.4.

Basically the nat manip_pkt handlers were corrupting
skb_shinfo(skb)->frag_list
(thus causing a crash in skb_drop_fraglist()) by stupidly writing beyond
skb->end when attempting to update fields (like tcp->check) in truncated
inner-ICMP headers.

Cheers
Marc

--- linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_core.c	2001/07/31 15:31:51	1.1
+++ linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_core.c	2001/07/31 16:12:45
@@ -824,6 +824,16 @@
 			       ? "DST" : "SRC",
 			       NIPQUAD(info->manips[i].manip.ip),
 			       ntohs(info->manips[i].manip.u.udp.port));
+
+			if((skb->len <= ((void *)inner - (void *)iph)) ||
+				(skb->len < ((void *)(
+					(u_int32_t *)inner + inner->ihl
+					) - (void *)iph))) {
+				DEBUGP("icmp_reply_translation: skb too small for inner IP header\n");
+				READ_UNLOCK(&ip_nat_lock);
+				return NF_DROP;
+			}
+
 			manip_pkt(inner->protocol, inner,
 				  skb->len - ((void *)inner - (void *)iph),
 				  &info->manips[i].manip,
--- linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_icmp.c	2001/07/31
15:37:44	1.1
+++ linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_icmp.c	2001/07/31
15:55:15
@@ -9,6 +9,12 @@
 #include <linux/netfilter_ipv4/ip_nat_rule.h>
 #include <linux/netfilter_ipv4/ip_nat_protocol.h>

+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
 static int
 icmp_in_range(const struct ip_conntrack_tuple *tuple,
 	      enum ip_nat_manip_type maniptype,
@@ -48,6 +54,11 @@
 	       enum ip_nat_manip_type maniptype)
 {
 	struct icmphdr *hdr = (struct icmphdr *)((u_int32_t *)iph + iph->ihl);
+
+	if(((void *)(hdr+1) - (void *)iph) < len) {
+		DEBUGP("icmp_manip_pkt: too small\n");
+		return;
+	}

 	hdr->checksum = ip_nat_cheat_check(hdr->un.echo.id ^ 0xFFFF,
 					   manip->u.icmp.id,
--- linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_tcp.c	2001/07/31 15:37:45
1.1
+++ linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_tcp.c	2001/07/31 16:19:40
@@ -9,6 +9,12 @@
 #include <linux/netfilter_ipv4/ip_nat_rule.h>
 #include <linux/netfilter_ipv4/ip_nat_protocol.h>

+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
 static int
 tcp_in_range(const struct ip_conntrack_tuple *tuple,
 	     enum ip_nat_manip_type maniptype,
@@ -83,6 +89,12 @@
 	u_int32_t oldip;
 	u_int16_t *portptr;

+
+	if(((void *)&hdr->dest + sizeof(hdr->dest) - (void *)iph) < len) {
+		DEBUGP("tcp_manip_pkt: too small\n");
+		return;
+	}
+
 	if (maniptype == IP_NAT_MANIP_SRC) {
 		/* Get rid of src ip and src pt */
 		oldip = iph->saddr;
@@ -92,10 +104,17 @@
 		oldip = iph->daddr;
 		portptr = &hdr->dest;
 	}
-	hdr->check = ip_nat_cheat_check(~oldip, manip->ip,
+
+	/* this could be a inner header returned in icmp packet; in such
+	   cases we cannot update the checksum field since it is outside of
+	   the 64 bits of transport layer headers typically included */
+	if(((void *)&hdr->check + sizeof(hdr->check) - (void *)iph) >= len) {
+		hdr->check = ip_nat_cheat_check(~oldip, manip->ip,
 					ip_nat_cheat_check(*portptr ^ 0xFFFF,
 							   manip->u.tcp.port,
 							   hdr->check));
+	}
+
 	*portptr = manip->u.tcp.port;
 }

--- linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_udp.c	2001/07/31 15:37:45
1.1
+++ linux-2.4.7-mb/net/ipv4/netfilter/ip_nat_proto_udp.c	2001/07/31 16:19:23
@@ -9,6 +9,12 @@
 #include <linux/netfilter_ipv4/ip_nat_rule.h>
 #include <linux/netfilter_ipv4/ip_nat_protocol.h>

+#if 0
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
 static int
 udp_in_range(const struct ip_conntrack_tuple *tuple,
 	     enum ip_nat_manip_type maniptype,
@@ -80,6 +86,11 @@
 	struct udphdr *hdr = (struct udphdr *)((u_int32_t *)iph + iph->ihl);
 	u_int32_t oldip;
 	u_int16_t *portptr;
+
+	if(((void *)&hdr->check + sizeof(hdr->check) - (void *)iph) < len) {
+		DEBUGP("udp_manip_pkt: too small\n");
+		return;
+	}

 	if (maniptype == IP_NAT_MANIP_SRC) {
 		/* Get rid of src ip and src pt */