#include "squid.h"
#include "base/AsyncCall.h"
+#include "cbdata.h"
#include "comm.h"
#include "ClientInfo.h"
#include "CommCalls.h"
#include "SquidTime.h"
#include "StatCounters.h"
#include "StoreIOBuffer.h"
+#include "tools.h"
+
#if USE_SSL
#include "ssl/support.h"
#endif
-#include "cbdata.h"
#if _SQUID_CYGWIN_
#include <sys/ioctl.h>
#endif
}
/**
- * Set the socket IP_TRANSPARENT option for Linux TPROXY v4 support.
+ * Set the socket IP_TRANSPARENT option for Linux TPROXY v4 support,
+ * or set the socket SO_BINDANY option for BSD divert-to support.
*/
void
comm_set_transparent(int fd)
{
-#if defined(IP_TRANSPARENT)
+#if _SQUID_LINUX_ && defined(IP_TRANSPARENT)
int tos = 1;
if (setsockopt(fd, SOL_IP, IP_TRANSPARENT, (char *) &tos, sizeof(int)) < 0) {
debugs(50, DBG_IMPORTANT, "comm_open: setsockopt(IP_TRANSPARENT) on FD " << fd << ": " << xstrerror());
/* mark the socket as having transparent options */
fd_table[fd].flags.transparent = 1;
}
+
+#elif defined(SO_BINDANY)
+ int tos = 1;
+ enter_suid();
+ if (setsockopt(fd, SOL_SOCKET, SO_BINDANY, (char *) &tos, sizeof(int)) < 0) {
+ debugs(50, DBG_IMPORTANT, "comm_open: setsockopt(SO_BINDANY) on FD " << fd << ": " << xstrerror());
+ } else {
+ /* mark the socket as having transparent options */
+ fd_table[fd].flags.transparent = true;
+ }
+ leave_suid();
+
#else
debugs(50, DBG_CRITICAL, "WARNING: comm_open: setsockopt(IP_TRANSPARENT) not supported on this platform");
#endif /* sockopt */
return false;
}
+bool
+Ip::Intercept::PfTransparent(const Comm::ConnectionPointer &newConn, int silent)
+{
+#if PF_TRANSPARENT && defined(SO_BINDANY)
+ /* Trust the user configured properly. If not no harm done.
+ * We will simply attempt a bind outgoing on our own IP.
+ */
+ newConn->remote.SetPort(0); // allow random outgoing port to prevent address clashes
+ debugs(89, 5, HERE << "address DIVERT: " << newConn);
+ return true;
+#else
+ return false;
+#endif
+}
+
bool
Ip::Intercept::PfInterception(const Comm::ConnectionPointer &newConn, int silent)
{
/* NP: try TPROXY first, its much quieter than NAT when non-matching */
if (transparentActive_ && listenConn->flags&COMM_TRANSPARENT) {
if (NetfilterTransparent(newConn, silent)) return true;
+ if (PfTransparent(newConn, silent)) return true;
}
/* NAT is only available in IPv4 */
bool
Ip::Intercept::ProbeForTproxy(Ip::Address &test)
{
- debugs(3, 3, "Detect TPROXY support on port " << test);
-
#if defined(IP_TRANSPARENT)
+ debugs(3, 3, "Detect TPROXY support on port " << test);
int tos = 1;
int tmp_sock = -1;
}
}
-#else /* undefined IP_TRANSPARENT */
- debugs(3, 3, "setsockopt(IP_TRANSPARENT) not supported on this platform. Disabling TPROXYv4.");
+#elif defined(SO_BINDANY)
+ debugs(3, 3, "Detect BINDANY support on port " << test);
+
+ int tos = 1;
+ int tmp_sock = -1;
+
+ if (test.IsIPv6()) {
+ debugs(3, 3, "...Probing for IPv6 SO_BINDANY support.");
+
+ struct sockaddr_in6 tmp_ip6;
+ Ip::Address tmp = "::2";
+ tmp.SetPort(0);
+ tmp.GetSockAddr(tmp_ip6);
+
+ if ((tmp_sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP)) >=0 &&
+ (setsockopt(tmp_sock, SOL_SOCKET, SO_BINDANY, (char *)&tos,
+ sizeof(tos)) == 0) &&
+ (bind(tmp_sock, (struct sockaddr*)&tmp_ip6, sizeof(struct sockaddr_in6)) == 0)) {
+ debugs(3, 3, "IPv6 BINDANY support detected. Using.");
+ close(tmp_sock);
+ return true;
+ }
+ }
+
+ if (test.IsIPv4()) {
+ debugs(3, 3, "...Probing for IPv4 SO_BINDANY support.");
+
+ struct sockaddr_in tmp_ip4;
+ Ip::Address tmp = "127.0.0.2";
+ tmp.SetPort(0);
+ tmp.GetSockAddr(tmp_ip4);
+
+ if ((tmp_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) >=0 &&
+ (setsockopt(tmp_sock, SOL_SOCKET, SO_BINDANY, (char *)&tos,
+ sizeof(tos)) == 0) &&
+ (bind(tmp_sock, (struct sockaddr*)&tmp_ip4, sizeof(struct sockaddr_in)) == 0)) {
+ debugs(3, 3, "IPv4 BINDANY support detected. Using.");
+ close(tmp_sock);
+ return true;
+ }
+ }
+
+#else
+ debugs(3, 3, "TPROXY setsockopt() not supported on this platform. Disabling TPROXY.");
+
#endif
return false;
}
bool IpfInterception(const Comm::ConnectionPointer &newConn, int silent);
/**
- * perform Lookups on PF interception.
+ * perform Lookups on PF interception target (REDIRECT).
*
* \param silent 0 if errors are to be displayed. 1 if errors are to be hidden.
* \param newConn Details known, to be updated where relevant.
*/
bool PfInterception(const Comm::ConnectionPointer &newConn, int silent);
+ /**
+ * perform Lookups on PF fully-transparent interception target (DIVERT).
+ *
+ * \param silent 0 if errors are to be displayed. 1 if errors are to be hidden.
+ * \param newConn Details known, to be updated where relevant.
+ * \return Whether successfuly located the new address.
+ */
+ bool PfTransparent(const Comm::ConnectionPointer &newConn, int silent);
+
int transparentActive_;
int interceptActive_;
time_t lastReported_; /**< Time of last error report. Throttles NAT error display to 1 per minute */