kern/123858: stf(4) not usable behind a NAT

Lapo Luchini lapo at lapo.it
Wed May 21 12:40:03 UTC 2008


>Number:         123858
>Category:       kern
>Synopsis:       stf(4) not usable behind a NAT
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Wed May 21 12:40:02 UTC 2008
>Closed-Date:
>Last-Modified:
>Originator:     Lapo Luchini
>Release:        FreeBSD 6.3-PRERELEASE amd64
>Organization:
>Environment:
System: FreeBSD deepie.home.lapo.it 7.0-STABLE FreeBSD 7.0-STABLE #7: Thu Apr  3 08:55:02 CEST 2008     root at deepie.home.lapo.it:/usr/obj/usr/src/sys/DEEPIE  amd64

>Description:

The stf(4) interface does proper filtering on the incoming 6to4 packets, 
but checking the destination address avoids it to work behind a NAT.

For those of us with a modem/router not capable enough to support IPv6 
but capable to redirect protocol 41 traffic (or maybe just all of it) to 
a NAT-ed FreeBSD box, this patch means easy and working access to IPv6 
world; ok, I'm absent-minded, but I already compiled a kernel forgetting 
to re-apply the patch three times in a row ;-)

>How-To-Repeat:

ping6(8) some IPv6 website, watch the ping packets go (and correctly 
reach destination) and never see the answer.

>Fix:

This is ume's patch as in Message-ID: <ygeacqp2y0f.wl%ume at mahoroba.org>, 
applied on latest 6-STABLE.

Has been working perfectly for me in the past year and some more 
(tracking 6-STABLE on both i386 and amd64).

--- stf.no_addr4check.diff begins here ---
--- share/man/man4/stf.4.orig	2005-02-09 19:07:16.000000000 +0100
+++ share/man/man4/stf.4	2008-04-01 08:34:57.381344242 +0200
@@ -179,6 +179,17 @@
 Note, however, there are other security risks exist.
 If you wish to use the configuration,
 you must not advertise your 6to4 address to others.
+.Pp
+You can configure to use 6to4 from behind NAT by setting the
+.Xr sysctl 8
+variable
+.Va net.link.stf.no_addr4check
+to 1 with support of your NAT box.  In this case, make sure to use a
+6to4 address which is worked out from an IPv4 global address of your
+NAT box.  If you are directly connected to the Internet, you shouldn't
+chenge the value of
+.Va net.link.stf.no_addr4check .
+This is only hack to use 6to4 from within a NAT.
 .\"
 .Sh EXAMPLES
 Note that
--- sys/net/if_stf.c.orig	2007-09-23 19:50:17.000000000 +0200
+++ sys/net/if_stf.c	2008-04-01 08:34:57.667320642 +0200
@@ -88,6 +88,7 @@
 #include <sys/module.h>
 #include <sys/protosw.h>
 #include <sys/queue.h>
+#include <sys/sysctl.h>
 #include <machine/cpu.h>
 
 #include <sys/malloc.h>
@@ -181,6 +182,13 @@
 struct if_clone stf_cloner = IFC_CLONE_INITIALIZER(STFNAME, NULL, 0,
     NULL, stf_clone_match, stf_clone_create, stf_clone_destroy);
 
+SYSCTL_DECL(_net_link);
+SYSCTL_NODE(_net_link, IFT_STF, stf, CTLFLAG_RW, 0, "6to4 Interface");
+
+static int no_addr4check = 0;
+SYSCTL_INT(_net_link_stf, OID_AUTO, no_addr4check, CTLFLAG_RW,
+    &no_addr4check, 0, "Skip checking outer IPv4 address");
+
 static int
 stf_clone_match(struct if_clone *ifc, const char *name)
 {
@@ -334,9 +342,17 @@
 	 * local 6to4 address.
 	 * success on: dst = 10.1.1.1, ia6->ia_addr = 2002:0a01:0101:...
 	 */
-	if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst,
-	    sizeof(ip.ip_dst)) != 0)
-		return 0;
+	if (no_addr4check) {
+		struct ifnet *tif;
+
+		INADDR_TO_IFP(ip.ip_dst, tif);
+		if (!tif)
+			return 0;
+	} else {
+		if (bcmp(GET_V4(&ia6->ia_addr.sin6_addr), &ip.ip_dst,
+		    sizeof(ip.ip_dst)) != 0)
+			return 0;
+	}
 
 	/*
 	 * check if IPv4 src matches the IPv4 address derived from the
@@ -373,12 +389,14 @@
 		if (!IN6_IS_ADDR_6TO4(&sin6->sin6_addr))
 			continue;
 
-		bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in));
-		LIST_FOREACH(ia4, INADDR_HASH(in.s_addr), ia_hash)
-			if (ia4->ia_addr.sin_addr.s_addr == in.s_addr)
-				break;
-		if (ia4 == NULL)
-			continue;
+		if (!no_addr4check) {
+			bcopy(GET_V4(&sin6->sin6_addr), &in, sizeof(in));
+			LIST_FOREACH(ia4, INADDR_HASH(in.s_addr), ia_hash)
+				if (ia4->ia_addr.sin_addr.s_addr == in.s_addr)
+					break;
+			if (ia4 == NULL)
+				continue;
+		}
 
 		return (struct in6_ifaddr *)ia;
 	}
@@ -493,8 +511,10 @@
 
 	bzero(ip, sizeof(*ip));
 
-	bcopy(GET_V4(&((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr),
-	    &ip->ip_src, sizeof(ip->ip_src));
+	if (!no_addr4check)
+		bcopy(GET_V4(
+		    &((struct sockaddr_in6 *)&ia6->ia_addr)->sin6_addr),
+		    &ip->ip_src, sizeof(ip->ip_src));
 	bcopy(&in4, &ip->ip_dst, sizeof(ip->ip_dst));
 	ip->ip_p = IPPROTO_IPV6;
 	ip->ip_ttl = ip_stf_ttl;
@@ -569,13 +589,6 @@
 	}
 
 	/*
-	 * reject packets with private address range.
-	 * (requirement from RFC3056 section 2 1st paragraph)
-	 */
-	if (isrfc1918addr(in))
-		return -1;
-
-	/*
 	 * reject packets with broadcast
 	 */
 	for (ia4 = TAILQ_FIRST(&in_ifaddrhead);
@@ -627,7 +640,16 @@
 	 */
 	if (IN6_IS_ADDR_6TO4(in6)) {
 		struct in_addr in4;
+
 		bcopy(GET_V4(in6), &in4, sizeof(in4));
+
+		/*
+		 * reject packets with private address range.
+		 * (requirement from RFC3056 section 2 1st paragraph)
+		 */
+		if (isrfc1918addr(&in4))
+			return -1;
+
 		return stf_checkaddr4(sc, &in4, inifp);
 	}
 
@@ -678,6 +700,18 @@
 #endif
 
 	/*
+	 * Skip RFC1918 check against dest address to allowincoming
+	 * packets with private address for dest.  Though it may
+	 * breasks the requirement from RFC3056 section 2 1st
+	 * paragraph, it helps for 6to4 over NAT.
+	 */
+	if ((!no_addr4check && isrfc1918addr(&ip->ip_dst)) ||
+	    isrfc1918addr(&ip->ip_src)) {
+		m_freem(m);
+		return;
+	}
+
+	/*
 	 * perform sanity check against outer src/dst.
 	 * for source, perform ingress filter as well.
 	 */
--- stf.no_addr4check.diff ends here ---
>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list