[MEDIUM] support fully transparent proxy on Linux (USE_LINUX_TPROXY)

Using some Linux kernel patches, it is possible to redirect non-local
traffic to local sockets when IP forwarding is enabled. In order to
enable this option, we introduce the "transparent" option keyword on
the "bind" command line. It will make the socket reachable by remote
sources even if the destination address does not belong to the machine.
diff --git a/Makefile b/Makefile
index 17d9dd1..43c05fd 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,7 @@
 #   USE_STATIC_PCRE      : enable static libpcre. Recommended.
 #   USE_TCPSPLICE        : enable tcp_splice() on Linux (needs kernel patch).
 #   USE_TPROXY           : enable transparent proxy. Automatic.
+#   USE_LINUX_TPROXY     : enable full transparent proxy (need kernel patch).
 #
 # Options can be forced by specifying "USE_xxx=1" or can be disabled by using
 # "USE_xxx=" (empty string).
@@ -291,6 +292,11 @@
 BUILD_OPTIONS  += $(call ignore_implicit,USE_TPROXY)
 endif
 
+ifneq ($(USE_LINUX_TPROXY),)
+OPTIONS_CFLAGS += -DCONFIG_HAP_LINUX_TPROXY
+BUILD_OPTIONS  += $(call ignore_implicit,USE_LINUX_TPROXY)
+endif
+
 ifneq ($(USE_POLL),)
 OPTIONS_CFLAGS += -DENABLE_POLL
 OPTIONS_OBJS   += src/ev_poll.o
diff --git a/doc/configuration.txt b/doc/configuration.txt
index f2ebd8f..6df3f0e 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2,9 +2,9 @@
                                  HAProxy
                           Configuration Manual
                          ----------------------
-                            version 1.3.14.2
+                            version 1.3.15
                              willy tarreau
-                               2008/01/05
+                               2008/01/13
 
 
 This document covers the configuration language as implemented in the version
@@ -758,19 +758,30 @@
 
 
 bind [<address>]:<port> [, ...]
+bind [<address>]:<port> [, ...] transparent
   Define one or several listening addresses and/or ports in a frontend.
   May be used in sections :   defaults | frontend | listen | backend
                                   no   |    yes   |   yes  |   no
   Arguments :
-    <address> is optional and can be a host name, an IPv4 address, an IPv6
-              address, or '*'. It designates the address the frontend will
-              listen on. If unset, all IPv4 addresses of the system will be
-              listened on. The same will apply for '*' or the system's special
-              address "0.0.0.0".
+    <address>     is optional and can be a host name, an IPv4 address, an IPv6
+                  address, or '*'. It designates the address the frontend will
+                  listen on. If unset, all IPv4 addresses of the system will be
+                  listened on. The same will apply for '*' or the system's
+                  special address "0.0.0.0".
+
+    <port>        is the TCP port number the proxy will listen on. The port is
+                  mandatory. Note that in the case of an IPv6 address, the port
+                  is always the number after the last colon (':').
 
-    <port>    is the TCP port number the proxy will listen on. The port is
-              mandatory. Note that in the case of an IPv6 address, the port is
-              always the number after the last colon (':').
+    transparent   is an optional keyword which is supported only on certain
+                  Linux kernels. It indicates that the addresses will be bound
+                  even if they do not belong to the local machine. Any packet
+                  targetting any of these addresses will be caught just as if
+                  the address was locally configured. This normally requires
+                  that IP forwarding is enabled. Caution! do not use this with
+                  the default address '*', as it would redirect any traffic for
+                  the specified port. This keyword is available only when
+                  HAProxy is built with USE_LINUX_TPROXY=1.
 
   It is possible to specify a list of address:port combinations delimited by
   commas. The frontend will then listen on all of these addresses. There is no
diff --git a/include/common/compat.h b/include/common/compat.h
index 774f9b4..3c51fb2 100644
--- a/include/common/compat.h
+++ b/include/common/compat.h
@@ -2,7 +2,7 @@
   include/common/compat.h
   Operating system compatibility interface.
 
-  Copyright (C) 2000-2006 Willy Tarreau - w@1wt.eu
+  Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -66,6 +66,13 @@
 #include <linux/netfilter_ipv4.h>
 #endif
 
+/* On Linux, IP_TRANSPARENT generally requires a kernel patch */
+#if defined(CONFIG_HAP_LINUX_TPROXY)
+#if !defined(IP_TRANSPARENT)
+#define IP_TRANSPARENT 19
+#endif /* !IP_TRANSPARENT */
+#endif /* CONFIG_HAP_LINUX_TPROXY */
+
 /* We'll try to enable SO_REUSEPORT on Linux 2.4 and 2.6 if not defined.
  * There are two families of values depending on the architecture. Those
  * are at least valid on Linux 2.4 and 2.6, reason why we'll rely on the
diff --git a/include/types/protocols.h b/include/types/protocols.h
index f360746..971a2a6 100644
--- a/include/types/protocols.h
+++ b/include/types/protocols.h
@@ -2,7 +2,7 @@
   include/types/protocols.h
   This file defines the structures used by generic network protocols.
 
-  Copyright (C) 2000-2007 Willy Tarreau - w@1wt.eu
+  Copyright (C) 2000-2008 Willy Tarreau - w@1wt.eu
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
@@ -65,6 +65,7 @@
 /* listener socket options */
 #define LI_O_NONE	0x0000
 #define LI_O_NOLINGER	0x0001	/* disable linger on this socket */
+#define LI_O_FOREIGN	0x0002	/* permit listening on foreing addresses */
 
 /* The listener will be directly referenced by the fdtab[] which holds its
  * socket. The listener provides the protocol-specific accept() function to
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 0c89c5b..b2bc451 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -728,6 +728,7 @@
 
 	/* Now let's parse the proxy-specific keywords */
 	if (!strcmp(args[0], "bind")) {  /* new listen addresses */
+		struct listener *last_listen;
 		if (curproxy == &defproxy) {
 			Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]);
 			return -1;
@@ -740,9 +741,30 @@
 			      file, linenum, args[0]);
 			return -1;
 		}
-		curproxy->listen = str2listener(args[1], curproxy->listen);
+
+		last_listen = curproxy->listen;
+		curproxy->listen = str2listener(args[1], last_listen);
 		if (!curproxy->listen)
 			return -1;
+		if (*args[2]) {
+#ifdef CONFIG_HAP_LINUX_TPROXY
+			if (!strcmp(args[2], "transparent")) { /* transparently bind to these addresses */
+				struct listener *l;
+
+				for (l = curproxy->listen; l != last_listen; l = l->next)
+					l->options |= LI_O_FOREIGN;
+			}
+			else {
+				Alert("parsing [%s:%d] : '%s' only supports the 'transparent' option.\n",
+				      file, linenum, args[0]);
+				return -1;
+			}
+#else
+			Alert("parsing [%s:%d] : '%s' supports no option after the address list.\n",
+			      file, linenum, args[0]);
+			return -1;
+#endif
+		}
 		global.maxsock++;
 		return 0;
 	}
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 2da34db..78d9367 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -154,6 +154,13 @@
 	 */
 	setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (char *) &one, sizeof(one));
 #endif
+#ifdef CONFIG_HAP_LINUX_TPROXY
+	if ((listener->options & LI_O_FOREIGN) 
+	    && (setsockopt(fd, SOL_IP, IP_TRANSPARENT, (char *) &one, sizeof(one)) == -1)) {
+		msg = "cannot make listening socket transparent";
+		err |= ERR_ALERT;
+	}
+#endif
 	if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) {
 		err |= ERR_RETRYABLE | ERR_ALERT;
 		msg = "cannot bind socket";