]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Complete Interception multiple NAT support
authorAmos Jeffries <squid3@treenet.co.nz>
Thu, 9 Apr 2009 12:53:37 +0000 (00:53 +1200)
committerAmos Jeffries <squid3@treenet.co.nz>
Thu, 9 Apr 2009 12:53:37 +0000 (00:53 +1200)
Bug 2618: fix ipfilter transparent proxy
Thanks to John Wehle <john@feith.com> for providing the code fixes.

Also:
  - cleans up some obscurity over data source and sink for me/client IPs.
  - cleans up existing debugs
  - adds new debugs to show NAT inputs and results at level-5
    (non-result is common and left at level-9)
  - adds new debugs to show TPROXY result at level-5 like NAT results
  - move IPF lookup into it's own function
  - move PF interception into its own function

With this all of the transparent build options are independent, and may be
used in any combination. Squid is no longer bound to the single-firewall
interception support.

NP: one small note; the PF lookup is slightly weird due to its altering
the local client address from the NAT information.
Not sure if this is needed, leaving it alone for now.

src/ip/IpIntercept.cc
src/ip/IpIntercept.h

index 8120c4ff1165ea78cb267d4f972db712e386ed77..a4fa9ad18eff0d76616bb1cc09839436f3243d4a 100644 (file)
@@ -139,7 +139,7 @@ IpIntercept::NetfilterInterception(int fd, const IpAddress &me, IpAddress &dst,
     dst.FreeAddrInfo(lookup);
 
     if (me != dst) {
-        debugs(89, 5, HERE << "address: " << dst);
+        debugs(89, 5, HERE << "address NAT: me= " << me << ", dst= " << dst);
         return 0;
     }
 
@@ -158,8 +158,11 @@ IpIntercept::NetfilterTransparent(int fd, const IpAddress &me, IpAddress &client
      */
     if (fd_table[fd].flags.transparent) {
         client.SetPort(0); // allow random outgoing port to prevent address clashes
+        debugs(89, 5, HERE << "address TPROXY: me= " << me << ", client= " << client);
         return 0;
     }
+
+    debugs(89, 9, HERE << "address: me= " << me << ", client= " << client);
 #endif
     return -1;
 }
@@ -186,7 +189,7 @@ IpIntercept::IpfwInterception(int fd, const IpAddress &me, IpAddress &dst, int s
     dst.FreeAddrInfo(lookup);
 
     if (me != dst) {
-        debugs(89, 5, HERE << "address: " << dst);
+        debugs(89, 5, HERE << "address NAT: me= " << me << ", dst= " << dst);
         return 0;
     }
 
@@ -196,22 +199,15 @@ IpIntercept::IpfwInterception(int fd, const IpAddress &me, IpAddress &dst, int s
 }
 
 int
-IpIntercept::NatLookup(int fd, const IpAddress &me, const IpAddress &peer, IpAddress &client, IpAddress &dst)
+IpIntercept::IpfInterception(int fd, const IpAddress &me, IpAddress &client, IpAddress &dst, int silent)
 {
 #if IPF_TRANSPARENT  /* --enable-ipf-transparent */
-    client = me;
-    if ( !me.IsIPv4() ) return -1;
-    if ( !peer.IsIPv4() ) return -1;
 
 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
-
     struct ipfobj obj;
 #else
-
     static int siocgnatl_cmd = SIOCGNATL & 0xff;
-
 #endif
-
     struct natlookup natLookup;
     static int natfd = -1;
     int x;
@@ -226,30 +222,27 @@ IpIntercept::NatLookup(int fd, const IpAddress &me, const IpAddress &peer, IpAdd
 #endif
 
     natLookup.nl_inport = htons(me.GetPort());
-    natLookup.nl_outport = htons(peer.GetPort());
+    natLookup.nl_outport = htons(dst.GetPort());
     me.GetInAddr(natLookup.nl_inip);
-    peer.GetInAddr(natLookup.nl_outip);
+    dst.GetInAddr(natLookup.nl_outip);
     natLookup.nl_flags = IPN_TCP;
 
     if (natfd < 0) {
         int save_errno;
         enter_suid();
 #ifdef IPNAT_NAME
-
         natfd = open(IPNAT_NAME, O_RDONLY, 0);
 #else
-
         natfd = open(IPL_NAT, O_RDONLY, 0);
 #endif
-
         save_errno = errno;
         leave_suid();
         errno = save_errno;
     }
 
     if (natfd < 0) {
-        if (squid_curtime - last_reported > 60) {
-            debugs(89, 1, "clientNatLookup: NAT open failed: " << xstrerror());
+        if (!silent) {
+            debugs(89, DBG_IMPORTANT, HERE << "NAT open failed: " << xstrerror());
             last_reported = squid_curtime;
             return -1;
         }
@@ -257,7 +250,6 @@ IpIntercept::NatLookup(int fd, const IpAddress &me, const IpAddress &peer, IpAdd
 
 #if defined(IPFILTER_VERSION) && (IPFILTER_VERSION >= 4000027)
     x = ioctl(natfd, SIOCGNATL, &obj);
-
 #else
     /*
     * IP-Filter changed the type for SIOCGNATL between
@@ -267,7 +259,6 @@ IpIntercept::NatLookup(int fd, const IpAddress &me, const IpAddress &peer, IpAdd
     * this seems simpler.
     */
     if (63 == siocgnatl_cmd) {
-
         struct natlookup *nlp = &natLookup;
         x = ioctl(natfd, SIOCGNATL, &nlp);
     } else {
@@ -277,8 +268,8 @@ IpIntercept::NatLookup(int fd, const IpAddress &me, const IpAddress &peer, IpAdd
 #endif
     if (x < 0) {
         if (errno != ESRCH) {
-            if (squid_curtime - last_reported > 60) {
-                debugs(89, 1, "clientNatLookup: NAT lookup failed: ioctl(SIOCGNATL)");
+            if (!silent) {
+                debugs(89, DBG_IMPORTANT, HERE << "NAT lookup failed: ioctl(SIOCGNATL)");
                 last_reported = squid_curtime;
             }
 
@@ -288,74 +279,44 @@ IpIntercept::NatLookup(int fd, const IpAddress &me, const IpAddress &peer, IpAdd
 
         return -1;
     } else {
-        if (me != natLookup.nl_realip) {
+        if (client != natLookup.nl_realip) {
             client = natLookup.nl_realip;
-
             client.SetPort(ntohs(natLookup.nl_realport));
         }
         // else. we already copied it.
 
+        debugs(89, 5, HERE << "address NAT: me= " << me << ", client= " << client << ", dst= " << dst);
         return 0;
     }
 
+    debugs(89, 9, HERE << "address: me= " << me << ", client= " << client << ", dst= " << dst);
 
-
-#elif LINUX_NETFILTER || IPFW_TRANSPARENT /* --enable-linux-netfilter OR --enable-ipfw-transparent */
-
-    /* Netfilter and IPFW share almost identical lookup methods for their NAT tables.
-     * This allows us to perform a nice clean failover sequence for them.
-     */
-
-    client = me;
-    dst = peer;
-
-    if ( !me.IsIPv4()   ) return -1;
-    if ( !peer.IsIPv4() ) return -1;
-
-#if 0
-    // Crop interception errors down to one per minute.
-    int silent = (squid_curtime - last_reported > 60 ? 0 : 1);
-#else
-    // Show all interception errors.
-    int silent = 0;
-#endif
-
-    if (intercept_active) {
-        if ( NetfilterInterception(fd, me, client, silent) == 0) return 0;
-        if ( IpfwInterception(fd, me, client, silent) == 0) return 0;
-    }
-    if (transparent_active) {
-        if ( NetfilterTransparent(fd, me, dst, silent) == 0) return 0;
-    }
-
+#endif /* --enable-ipf-transparent */
     return -1;
+}
 
-#elif PF_TRANSPARENT  /* --enable-pf-transparent */
+int
+IpIntercept::PfInterception(int fd, const IpAddress &me, IpAddress &client, IpAddress &dst, int silent)
+{
+#if PF_TRANSPARENT  /* --enable-pf-transparent */
 
     struct pfioc_natlook nl;
     static int pffd = -1;
 
-    if ( !me.IsIPv4() ) return -1;
-    if ( !peer.IsIPv4() ) return -1;
-
     if (pffd < 0)
         pffd = open("/dev/pf", O_RDONLY);
 
     if (pffd < 0) {
-        if (squid_curtime - last_reported > 60) {
-            debugs(89, 1, "clientNatLookup: PF open failed: " << xstrerror());
+        if (!silent) {
+            debugs(89, DBG_IMPORTANT, HERE << "PF open failed: " << xstrerror());
             last_reported = squid_curtime;
         }
-
         return -1;
-
     }
 
-    client.SetEmpty();
-
     memset(&nl, 0, sizeof(struct pfioc_natlook));
-    peer.GetInAddr(nl.saddr.v4);
-    nl.sport = htons(peer.GetPort());
+    dst.GetInAddr(nl.saddr.v4);
+    nl.sport = htons(dst.GetPort());
 
     me.GetInAddr(nl.daddr.v4);
     nl.dport = htons(me.GetPort());
@@ -366,35 +327,75 @@ IpIntercept::NatLookup(int fd, const IpAddress &me, const IpAddress &peer, IpAdd
 
     if (ioctl(pffd, DIOCNATLOOK, &nl)) {
         if (errno != ENOENT) {
-            if (squid_curtime - last_reported > 60) {
-                debugs(89, 1, "clientNatLookup: PF lookup failed: ioctl(DIOCNATLOOK)");
+            if (!silent) {
+                debugs(89, DBG_IMPORTANT, HERE << "PF lookup failed: ioctl(DIOCNATLOOK)");
                 last_reported = squid_curtime;
             }
-
             close(pffd);
             pffd = -1;
         }
-
-        return -1;
     } else {
-        int natted = (me != nl.rdaddr.v4);
+        int natted = (client != nl.rdaddr.v4);
         client = nl.rdaddr.v4;
         client.SetPort(ntohs(nl.rdport));
 
-        if (natted)
+        if (natted) {
+            debugs(89, 5, HERE << "address NAT: me= " << me << ", client= " << client << ", dst= " << dst);
             return 0;
-        else
-            return -1;
+        }
     }
 
+    debugs(89, 9, HERE << "address: me= " << me << ", client= " << client << ", dst= " << dst);
 
-#else /* none of the transparent options configured */
-
-    debugs(89, 1, "WARNING: transparent proxying not supported");
+#endif /* --enable-pf-transparent */
     return -1;
+}
+
+
+int
+IpIntercept::NatLookup(int fd, const IpAddress &me, const IpAddress &peer, IpAddress &client, IpAddress &dst)
+{
+    /* --enable-linux-netfilter    */
+    /* --enable-ipfw-transparent   */
+    /* --enable-ipf-transparent    */
+    /* --enable-pf-transparent     */
+#if IPF_TRANSPARENT || LINUX_NETFILTER || IPFW_TRANSPARENT || PF_TRANSPARENT
+
+    client = me;
+    dst = peer;
+
+    if ( !me.IsIPv4()   ) return -1;
+    if ( !peer.IsIPv4() ) return -1;
+
+#if 0
+    // Crop interception errors down to one per minute.
+    int silent = (squid_curtime - last_reported > 60 ? 0 : 1);
+#else
+    // Show all interception errors.
+    int silent = 0;
+#endif
+
+    debugs(89, 5, HERE << "address BEGIN: me= " << me << ", client= " << client <<
+           ", dst= " << dst << ", peer= " << peer);
 
+    if (intercept_active) {
+        /* NAT methods that use sock-opts to return client address */
+        if ( NetfilterInterception(fd, me, client, silent) == 0) return 0;
+        if ( IpfwInterception(fd, me, client, silent) == 0) return 0;
+
+        /* NAT methods that use ioctl to return client address AND destination address */
+        if ( PfInterception(fd, me, client, dst, silent) == 0) return 0;
+        if ( IpfInterception(fd, me, client, dst, silent) == 0) return 0;
+    }
+    if (transparent_active) {
+        if ( NetfilterTransparent(fd, me, dst, silent) == 0) return 0;
+    }
+
+#else /* none of the transparent options configured */
+    debugs(89, DBG_IMPORTANT, "WARNING: transparent proxying not supported");
 #endif
 
+    return -1;
 }
 
 #if LINUX_TPROXY2
index dd3ec9cb782065e0f29fe88021f5a65d5320ff30..0ab24e23aab9bdb55d07616f2b964f29eec59d6c 100644 (file)
@@ -122,6 +122,24 @@ private:
      */
     int IpfwInterception(int fd, const IpAddress &me, IpAddress &client, int silent);
 
+    /**
+     * perform Lookups on IPF interception.
+     *
+     \param silent[in]   0 if errors are to be displayed. 1 if errors are to be hidden.
+     \retval 0     Successfuly located the new address.
+     \retval -1    An error occured during NAT lookups.
+     */
+    int IpfInterception(int fd, const IpAddress &me, IpAddress &client, IpAddress &dst, int silent);
+
+    /**
+     * perform Lookups on PF interception.
+     *
+     \param silent[in]   0 if errors are to be displayed. 1 if errors are to be hidden.
+     \retval 0     Successfuly located the new address.
+     \retval -1    An error occured during NAT lookups.
+     */
+    int PfInterception(int fd, const IpAddress &me, IpAddress &client, IpAddress &dst, int silent);
+
     int transparent_active;
     int intercept_active;
     time_t last_reported; /**< Time of last error report. Throttles NAT error display to 1 per minute */