From: Alexis Robert Date: Tue, 9 Jul 2013 10:04:39 +0000 (+1200) Subject: Support IPv6 NAT interception on Linux X-Git-Tag: SQUID_3_4_0_1~30 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=ee80a91989a64b42e02e7bc8939c427afc66049e;p=thirdparty%2Fsquid.git Support IPv6 NAT interception on Linux 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. --- diff --git a/configure.ac b/configure.ac index 6ab2691e97..3635c26c56 100644 --- a/configure.ac +++ b/configure.ac @@ -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 diff --git a/src/cache_cf.cc b/src/cache_cf.cc index e9c3e18032..cd2233f62b 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -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."); diff --git a/src/ip/Intercept.cc b/src/ip/Intercept.cc index 205ce1d40b..45de8d82f8 100644 --- a/src/ip/Intercept.cc +++ b/src/ip/Intercept.cc @@ -96,7 +96,21 @@ /* must be before including netfilter_ipv4.h */ #include #endif +#include #include +#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 +#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;