]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Support IPv6 NAT interception on Linux
authorAlexis Robert <alexis.robert@gmail.com>
Tue, 9 Jul 2013 10:04:39 +0000 (22:04 +1200)
committerAmos Jeffries <squid3@treenet.co.nz>
Tue, 9 Jul 2013 10:04:39 +0000 (22:04 +1200)
NAT support has been included for IPv6 in Linux 3.7 (along with
REDIRECT/DNAT rules), as well as IP6T_SO_ORIGINAL_DST in Linux 3.8.
Add support for transparent proxies over IPv6.

There is a bug in linux/netfilter_ipv6/ip6_tables.h on C++ compilers,
the bug report and patch to fix it can be found at
  https://lkml.org/lkml/2012/9/30/146.
It is only used for the constant IP6T_SO_ORIGINAL_DST. We attempt to use
the official header whenever possible but if it is detected missing or
broken we define our own version of the option.

IPv6 is now permitted on any http_port or https_port in squid.conf
however on older Linux systems and Unix systems without the required NAT
support Squid will fail when accepting the traffic.

Also, this removes the blocker checks preventing BSD systems using NAT
interception on IPv6 ports. Several version of PF have long since
supported IPv6 NAT operations although it was discouraged, such support
is not easily detected though so results WILL vary by operating system.

configure.ac
src/cache_cf.cc
src/ip/Intercept.cc

index 6ab2691e97c626d07d82164a192f149b099d7a69..3635c26c56fb610cd7f9d1db75be1598a6b0dc50 100644 (file)
@@ -2258,8 +2258,9 @@ AC_CHECK_HEADERS( \
   wchar.h
 )
 
-AC_CHECK_HEADERS(
-  linux/netfilter_ipv4.h
+AC_CHECK_HEADERS( \
+  linux/netfilter_ipv4.h \
+  linux/netfilter_ipv6/ip6_tables.h \
 ,,,
 SQUID_DEFAULT_INCLUDES
 #if HAVE_LIMITS_H
index e9c3e1803211f19e88b6520e59a9e3faa4640267..cd2233f62bbc611645c3696e0bd468718182da31 100644 (file)
@@ -3616,14 +3616,6 @@ parse_port_option(AnyP::PortCfg * s, char *token)
         /* Log information regarding the port modes under interception. */
         debugs(3, DBG_IMPORTANT, "Starting Authentication on port " << s->s);
         debugs(3, DBG_IMPORTANT, "Disabling Authentication on port " << s->s << " (interception enabled)");
-
-        /* INET6: until transparent REDIRECT works on IPv6 SOCKET, force wildcard to IPv4 */
-        if (Ip::EnableIpv6)
-            debugs(3, DBG_IMPORTANT, "Disabling IPv6 on port " << s->s << " (interception enabled)");
-        if ( !s->s.setIPv4() ) {
-            debugs(3, DBG_CRITICAL, "FATAL: http(s)_port: IPv6 addresses cannot NAT intercept (protocol does not provide NAT)" << s->s );
-            self_destruct();
-        }
     } else if (strcmp(token, "tproxy") == 0) {
         if (s->flags.natIntercept || s->flags.accelSurrogate) {
             debugs(3,DBG_CRITICAL, "FATAL: http(s)_port: TPROXY option requires its own interception port. It cannot be shared with other modes.");
index 205ce1d40bd78183b6754e056100631b893b3010..45de8d82f8865ac72c845d2e6a478d3063cd7640 100644 (file)
 /* must be before including netfilter_ipv4.h */
 #include <limits.h>
 #endif
+#include <linux/if.h>
 #include <linux/netfilter_ipv4.h>
+#if HAVE_LINUX_NETFILTER_IPV6_IP6_TABLES_H
+/* 2013-07-01: Pablo the Netfilter maintainer is rejecting patches
+ * which will enable C++ compilers to build the Netfilter public headers.
+ * We can auto-detect its presence and attempt to use in case he ever
+ * changes his mind or things get cleaned up some other way.
+ * But until then are usually forced to hard-code the getsockopt() code
+ * for IPv6 NAT lookups.
+ */
+#include <linux/netfilter_ipv6/ip6_tables.h>
+#endif
+#if !defined(IP6T_SO_ORIGINAL_DST)
+#define IP6T_SO_ORIGINAL_DST   80      // stolen with prejudice from the above file.
+#endif
 #endif /* LINUX_NETFILTER required headers */
 
 // single global instance for access by other components.
@@ -124,22 +138,26 @@ bool
 Ip::Intercept::NetfilterInterception(const Comm::ConnectionPointer &newConn, int silent)
 {
 #if LINUX_NETFILTER
-    struct sockaddr_in lookup;
-    socklen_t len = sizeof(struct sockaddr_in);
-    newConn->local.getSockAddr(lookup);
+    struct sockaddr_storage lookup;
+    socklen_t len = newConn->local.isIPv6() ? sizeof(sockaddr_in6) : sizeof(sockaddr_in);
+    newConn->local.getSockAddr(lookup, AF_UNSPEC);
 
     /** \par
      * Try NAT lookup for REDIRECT or DNAT targets. */
-    if ( getsockopt(newConn->fd, IPPROTO_IP, SO_ORIGINAL_DST, &lookup, &len) != 0) {
+    if ( getsockopt(newConn->fd,
+                newConn->local.isIPv6() ? IPPROTO_IPV6 : IPPROTO_IP,
+                newConn->local.isIPv6() ? IP6T_SO_ORIGINAL_DST : SO_ORIGINAL_DST,
+                &lookup,
+                &len) != 0) {
         if (!silent) {
-            debugs(89, DBG_IMPORTANT, HERE << " NF getsockopt(SO_ORIGINAL_DST) failed on " << newConn << ": " << xstrerror());
+            debugs(89, DBG_IMPORTANT, "ERROR: NF getsockopt(ORIGINAL_DST) failed on " << newConn << ": " << xstrerror());
             lastReported_ = squid_curtime;
         }
-        debugs(89, 9, HERE << "address: " << newConn);
+        debugs(89, 9, "address: " << newConn);
         return false;
     } else {
         newConn->local = lookup;
-        debugs(89, 5, HERE << "address NAT: " << newConn);
+        debugs(89, 5, "address NAT: " << newConn);
         return true;
     }
 #endif
@@ -359,10 +377,6 @@ Ip::Intercept::Lookup(const Comm::ConnectionPointer &newConn, const Comm::Connec
         if (TproxyTransparent(newConn, silent)) return true;
     }
 
-    /* NAT is only available in IPv4 */
-    if ( !newConn->local.isIPv4()  ) return false;
-    if ( !newConn->remote.isIPv4() ) return false;
-
     if (interceptActive_ && listenConn->flags&COMM_INTERCEPTION) {
         /* NAT methods that use sock-opts to return client address */
         if (NetfilterInterception(newConn, silent)) return true;