OK here's what I came up with. There are 3 patches :

  - 0001 : reorganize flags processing
  - 0002 : add support for freebsd
  - 0003 : add support for openbsd

Please review and test if you can. At least it seems OK on linux here.
I have written all the commit messages. Feel free to change them if you
want, as they're made under your name. If you want to provide additional
doc, let's just add a 4th patch on top of this.

The code is not quite beautiful, but that's always the price to pay
when playing with ifdefs, and there are already a large number of them
in the same functions anyway.

Also, if you could provide a real name for the commits, it would be nice!

Thanks!
Willy

>From fa20b9333c0ee547ec1214da73bdc2d7163442dd Mon Sep 17 00:00:00 2001
From: PiBa-NL <[email protected]>
Date: Wed, 8 May 2013 22:49:23 +0200
Subject: REORG: tproxy: prepare the transparent proxy defines for accepting
 other OSes

This patch does not change the logic of the code, it only changes the
way OS-specific defines are tested.

At the moment the transparent proxy code heavily depends on Linux-specific
defines. This first patch introduces a new define "CONFIG_HAP_TRANSPARENT"
which is set every time the defines used by transparent proxy are present.
This also means that with an up-to-date libc, it should not be necessary
anymore to force CONFIG_HAP_LINUX_TPROXY during the build, as the flags
will automatically be detected.

The CTTPROXY flags still remain separate because this older API doesn't
work the same way.

A new line has been added in the version output for haproxy -vv to indicate
what transparent proxy support is available.
---
 include/common/compat.h    |  6 ++++++
 include/types/connection.h |  2 +-
 src/backend.c              |  2 +-
 src/cfgparse.c             | 20 +++++++++----------
 src/haproxy.c              | 16 +++++++++++++++
 src/proto_tcp.c            | 49 ++++++++++++++++++++++++++++++++++------------
 6 files changed, 70 insertions(+), 25 deletions(-)

diff --git a/include/common/compat.h b/include/common/compat.h
index bb2d010..043a56e 100644
--- a/include/common/compat.h
+++ b/include/common/compat.h
@@ -93,6 +93,12 @@
 #endif /* !IPV6_TRANSPARENT */
 #endif /* CONFIG_HAP_LINUX_TPROXY */
 
+#if defined(IP_FREEBIND)       \
+ || defined(IP_TRANSPARENT)    \
+ || defined(IPV6_TRANSPARENT)
+#define CONFIG_HAP_TRANSPARENT
+#endif
+
 /* 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/connection.h b/include/types/connection.h
index 255811c..2c7acd1 100644
--- a/include/types/connection.h
+++ b/include/types/connection.h
@@ -219,7 +219,7 @@ struct conn_src {
        char *iface_name;                    /* bind interface name or NULL */
        struct port_range *sport_range;      /* optional per-server TCP source 
ports */
        struct sockaddr_storage source_addr; /* the address to which we want to 
bind for connect() */
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
        struct sockaddr_storage tproxy_addr; /* non-local address we want to 
bind to for connect() */
        char *bind_hdr_name;                 /* bind to this header name if 
defined */
        int bind_hdr_len;                    /* length of the name of the 
header above */
diff --git a/src/backend.c b/src/backend.c
index 9f4e635..9f23018 100644
--- a/src/backend.c
+++ b/src/backend.c
@@ -884,7 +884,7 @@ int assign_server_and_queue(struct session *s)
  */
 static void assign_tproxy_address(struct session *s)
 {
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
        struct server *srv = objt_server(s->target);
        struct conn_src *src;
 
diff --git a/src/cfgparse.c b/src/cfgparse.c
index 6f2850c..9907bfd 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -1813,7 +1813,7 @@ int cfg_parse_listen(const char *file, int linenum, char 
**args, int kwm)
                                curproxy->conn_src.iface_name = 
strdup(defproxy.conn_src.iface_name);
                        curproxy->conn_src.iface_len = 
defproxy.conn_src.iface_len;
                        curproxy->conn_src.opts = defproxy.conn_src.opts;
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
                        curproxy->conn_src.tproxy_addr = 
defproxy.conn_src.tproxy_addr;
 #endif
                }
@@ -4558,8 +4558,8 @@ stats_error_parsing:
                                cur_arg += 2;
                                while (*(args[cur_arg])) {
                                        if (!strcmp(args[cur_arg], "usesrc")) { 
 /* address to use outside */
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
-#if !defined(CONFIG_HAP_LINUX_TPROXY)
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
+#if !defined(CONFIG_HAP_TRANSPARENT)
                                                if 
(!is_addr(&newsrv->conn_src.source_addr)) {
                                                        Alert("parsing [%s:%d] 
: '%s' requires an explicit '%s' address.\n",
                                                              file, linenum, 
"usesrc", "source");
@@ -4648,7 +4648,7 @@ stats_error_parsing:
                                                        newsrv->conn_src.opts 
|= CO_SRC_TPROXY_ADDR;
                                                }
                                                global.last_checks |= 
LSTCHK_NETADM;
-#if !defined(CONFIG_HAP_LINUX_TPROXY)
+#if !defined(CONFIG_HAP_TRANSPARENT)
                                                global.last_checks |= 
LSTCHK_CTTPROXY;
 #endif
                                                cur_arg += 2;
@@ -4658,7 +4658,7 @@ stats_error_parsing:
                                                      file, linenum, "usesrc");
                                                err_code |= ERR_ALERT | 
ERR_FATAL;
                                                goto out;
-#endif /* defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) */
+#endif /* defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT) */
                                        } /* "usesrc" */
 
                                        if (!strcmp(args[cur_arg], 
"interface")) { /* specifically bind to this interface */
@@ -5046,8 +5046,8 @@ stats_error_parsing:
                cur_arg = 2;
                while (*(args[cur_arg])) {
                        if (!strcmp(args[cur_arg], "usesrc")) {  /* address to 
use outside */
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
-#if !defined(CONFIG_HAP_LINUX_TPROXY)
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
+#if !defined(CONFIG_HAP_TRANSPARENT)
                                if (!is_addr(&curproxy->conn_src.source_addr)) {
                                        Alert("parsing [%s:%d] : '%s' requires 
an explicit 'source' address.\n",
                                              file, linenum, "usesrc");
@@ -5136,7 +5136,7 @@ stats_error_parsing:
                                        curproxy->conn_src.opts |= 
CO_SRC_TPROXY_ADDR;
                                }
                                global.last_checks |= LSTCHK_NETADM;
-#if !defined(CONFIG_HAP_LINUX_TPROXY)
+#if !defined(CONFIG_HAP_TRANSPARENT)
                                global.last_checks |= LSTCHK_CTTPROXY;
 #endif
 #else  /* no TPROXY support */
@@ -6826,7 +6826,7 @@ out_uri_auth_compat:
                                }
                        }
 
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
                        if (curproxy->conn_src.bind_hdr_occ) {
                                curproxy->conn_src.bind_hdr_occ = 0;
                                Warning("config : %s '%s' : ignoring use of 
header %s as source IP in non-HTTP mode.\n",
@@ -6853,7 +6853,7 @@ out_uri_auth_compat:
                                err_code |= ERR_WARN;
                        }
 
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
+#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_TRANSPARENT)
                        if (curproxy->mode != PR_MODE_HTTP && 
newsrv->conn_src.bind_hdr_occ) {
                                newsrv->conn_src.bind_hdr_occ = 0;
                                Warning("config : %s '%s' : server %s cannot 
use header %s as source IP in non-HTTP mode.\n",
diff --git a/src/haproxy.c b/src/haproxy.c
index 6155ea3..d5606fa 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -313,6 +313,22 @@ void display_build_opts()
        printf("Built without PCRE support (using libc's regex instead)\n");
 #endif
 
+#if defined(CONFIG_HAP_TRANSPARENT) || defined(CONFIG_HAP_CTTPROXY)
+       printf("Built with transparent proxy support using:"
+#if defined(CONFIG_HAP_CTTPROXY)
+              " CTTPROXY"
+#endif
+#if defined(IP_TRANSPARENT)
+              " IP_TRANSPARENT"
+#endif
+#if defined(IPV6_TRANSPARENT)
+              " IPV6_TRANSPARENT"
+#endif
+#if defined(IP_FREEBIND)
+              " IP_FREEBIND"
+#endif
+              "\n");
+#endif
        putchar('\n');
 
        list_pollers(stdout);
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 97bc33b..f59c23b 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -122,15 +122,24 @@ int tcp_bind_socket(int fd, int flags, struct 
sockaddr_storage *local, struct so
        struct sockaddr_storage bind_addr;
        int foreign_ok = 0;
        int ret;
-
-#ifdef CONFIG_HAP_LINUX_TPROXY
        static int ip_transp_working = 1;
        static int ip6_transp_working = 1;
+
        switch (local->ss_family) {
        case AF_INET:
                if (flags && ip_transp_working) {
-                       if (setsockopt(fd, SOL_IP, IP_TRANSPARENT, &one, 
sizeof(one)) == 0
-                           || setsockopt(fd, SOL_IP, IP_FREEBIND, &one, 
sizeof(one)) == 0)
+                       /* This deserves some explanation. Some platforms will 
support
+                        * multiple combinations of certain methods, so we try 
the
+                        * supported ones until one succeeds.
+                        */
+                       if (0
+#if defined(IP_TRANSPARENT)
+                           || (setsockopt(fd, SOL_IP, IP_TRANSPARENT, &one, 
sizeof(one)) == 0)
+#endif
+#if defined(IP_FREEBIND)
+                           || (setsockopt(fd, SOL_IP, IP_FREEBIND, &one, 
sizeof(one)) == 0)
+#endif
+                           )
                                foreign_ok = 1;
                        else
                                ip_transp_working = 0;
@@ -138,14 +147,18 @@ int tcp_bind_socket(int fd, int flags, struct 
sockaddr_storage *local, struct so
                break;
        case AF_INET6:
                if (flags && ip6_transp_working) {
-                       if (setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &one, 
sizeof(one)) == 0)
+                       if (0
+#if defined(IPV6_TRANSPARENT)
+                           || (setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, 
&one, sizeof(one)) == 0)
+#endif
+                           )
                                foreign_ok = 1;
                        else
                                ip6_transp_working = 0;
                }
                break;
        }
-#endif
+
        if (flags) {
                memset(&bind_addr, 0, sizeof(bind_addr));
                bind_addr.ss_family = remote->ss_family;
@@ -621,25 +634,35 @@ int tcp_bind_listener(struct listener *listener, char 
*errmsg, int errlen)
        if (!ext)
                setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &one, sizeof(one));
 #endif
-#ifdef CONFIG_HAP_LINUX_TPROXY
+
        if (!ext && (listener->options & LI_O_FOREIGN)) {
                switch (listener->addr.ss_family) {
                case AF_INET:
-                       if ((setsockopt(fd, SOL_IP, IP_TRANSPARENT, &one, 
sizeof(one)) == -1)
-                           && (setsockopt(fd, SOL_IP, IP_FREEBIND, &one, 
sizeof(one)) == -1)) {
+                       if (1
+#if defined(IP_TRANSPARENT)
+                           && (setsockopt(fd, SOL_IP, IP_TRANSPARENT, &one, 
sizeof(one)) == -1)
+#endif
+#if defined(IP_FREEBIND)
+                           && (setsockopt(fd, SOL_IP, IP_FREEBIND, &one, 
sizeof(one)) == -1)
+#endif
+                           ) {
                                msg = "cannot make listening socket 
transparent";
                                err |= ERR_ALERT;
                        }
                break;
                case AF_INET6:
-                       if (setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, &one, 
sizeof(one)) == -1) {
+                       if (1
+#if defined(IPV6_TRANSPARENT)
+                           && (setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, 
&one, sizeof(one)) == -1)
+#endif
+                           ) {
                                msg = "cannot make listening socket 
transparent";
                                err |= ERR_ALERT;
                        }
                break;
                }
        }
-#endif
+
 #ifdef SO_BINDTODEVICE
        /* Note: this might fail if not CAP_NET_RAW */
        if (!ext && listener->interface) {
@@ -1546,7 +1569,7 @@ static int bind_parse_v6only(char **args, int cur_arg, 
struct proxy *px, struct
 }
 #endif
 
-#ifdef CONFIG_HAP_LINUX_TPROXY
+#ifdef CONFIG_HAP_TRANSPARENT
 /* parse the "transparent" bind keyword */
 static int bind_parse_transparent(char **args, int cur_arg, struct proxy *px, 
struct bind_conf *conf, char **err)
 {
@@ -1695,7 +1718,7 @@ static struct bind_kw_list bind_kws = { "TCP", { }, {
 #ifdef TCP_FASTOPEN
        { "tfo",           bind_parse_tfo,          0 }, /* enable TCP_FASTOPEN 
of listening socket */
 #endif
-#ifdef CONFIG_HAP_LINUX_TPROXY
+#ifdef CONFIG_HAP_TRANSPARENT
        { "transparent",   bind_parse_transparent,  0 }, /* transparently bind 
to the specified addresses */
 #endif
 #ifdef IPV6_V6ONLY
-- 
1.7.12.2.21.g234cd45.dirty

>From f5511f3025459eee9f7b0080deab86c00c860202 Mon Sep 17 00:00:00 2001
From: PiBa-NL <[email protected]>
Date: Wed, 8 May 2013 23:22:39 +0200
Subject: MINOR: tproxy: add support for FreeBSD

FreeBSD uses (IPPROTO_IP, IP_BINDANY) and (IPPROTO_IPV6, IPV6_BINDANY)
to enable transparent proxy on a socket.

This patch adds support for the relevant setsockopt() calls.
---
 include/common/compat.h |  2 ++
 src/haproxy.c           |  6 ++++++
 src/proto_tcp.c         | 12 ++++++++++++
 3 files changed, 20 insertions(+)

diff --git a/include/common/compat.h b/include/common/compat.h
index 043a56e..efc2a6e 100644
--- a/include/common/compat.h
+++ b/include/common/compat.h
@@ -94,6 +94,8 @@
 #endif /* CONFIG_HAP_LINUX_TPROXY */
 
 #if defined(IP_FREEBIND)       \
+ || defined(IP_BINDANY)        \
+ || defined(IPV6_BINDANY)      \
  || defined(IP_TRANSPARENT)    \
  || defined(IPV6_TRANSPARENT)
 #define CONFIG_HAP_TRANSPARENT
diff --git a/src/haproxy.c b/src/haproxy.c
index d5606fa..ee0bd65 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -327,6 +327,12 @@ void display_build_opts()
 #if defined(IP_FREEBIND)
               " IP_FREEBIND"
 #endif
+#if defined(IP_BINDANY)
+              " IP_BINDANY"
+#endif
+#if defined(IPV6_BINDANY)
+              " IPV6_BINDANY"
+#endif
               "\n");
 #endif
        putchar('\n');
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index f59c23b..0ae359a 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -139,6 +139,9 @@ int tcp_bind_socket(int fd, int flags, struct 
sockaddr_storage *local, struct so
 #if defined(IP_FREEBIND)
                            || (setsockopt(fd, SOL_IP, IP_FREEBIND, &one, 
sizeof(one)) == 0)
 #endif
+#if defined(IP_BINDANY)
+                           || (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &one, 
sizeof(one)) == 0)
+#endif
                            )
                                foreign_ok = 1;
                        else
@@ -151,6 +154,9 @@ int tcp_bind_socket(int fd, int flags, struct 
sockaddr_storage *local, struct so
 #if defined(IPV6_TRANSPARENT)
                            || (setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, 
&one, sizeof(one)) == 0)
 #endif
+#if defined(IPV6_BINDANY)
+                           || (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, 
&one, sizeof(one)) == 0)
+#endif
                            )
                                foreign_ok = 1;
                        else
@@ -645,6 +651,9 @@ int tcp_bind_listener(struct listener *listener, char 
*errmsg, int errlen)
 #if defined(IP_FREEBIND)
                            && (setsockopt(fd, SOL_IP, IP_FREEBIND, &one, 
sizeof(one)) == -1)
 #endif
+#if defined(IP_BINDANY)
+                           && (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &one, 
sizeof(one)) == -1)
+#endif
                            ) {
                                msg = "cannot make listening socket 
transparent";
                                err |= ERR_ALERT;
@@ -655,6 +664,9 @@ int tcp_bind_listener(struct listener *listener, char 
*errmsg, int errlen)
 #if defined(IPV6_TRANSPARENT)
                            && (setsockopt(fd, SOL_IPV6, IPV6_TRANSPARENT, 
&one, sizeof(one)) == -1)
 #endif
+#if defined(IPV6_BINDANY)
+                           && (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, 
&one, sizeof(one)) == -1)
+#endif
                            ) {
                                msg = "cannot make listening socket 
transparent";
                                err |= ERR_ALERT;
-- 
1.7.12.2.21.g234cd45.dirty

>From b8bed4ab1d1fac894efd5a0b89fe2a5aa25d42ed Mon Sep 17 00:00:00 2001
From: PiBa-NL <[email protected]>
Date: Wed, 8 May 2013 23:30:23 +0200
Subject: MINOR: tproxy: add support for OpenBSD

OpenBSD uses (SOL_SOCKET, SO_BINDANY) to enable transparent
proxy on a socket.

This patch adds support for the relevant setsockopt() calls.
---
 include/common/compat.h |  1 +
 src/haproxy.c           |  3 +++
 src/proto_tcp.c         | 12 ++++++++++++
 3 files changed, 16 insertions(+)

diff --git a/include/common/compat.h b/include/common/compat.h
index efc2a6e..0085a3a 100644
--- a/include/common/compat.h
+++ b/include/common/compat.h
@@ -96,6 +96,7 @@
 #if defined(IP_FREEBIND)       \
  || defined(IP_BINDANY)        \
  || defined(IPV6_BINDANY)      \
+ || defined(SO_BINDANY)        \
  || defined(IP_TRANSPARENT)    \
  || defined(IPV6_TRANSPARENT)
 #define CONFIG_HAP_TRANSPARENT
diff --git a/src/haproxy.c b/src/haproxy.c
index ee0bd65..5b5bea4 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -333,6 +333,9 @@ void display_build_opts()
 #if defined(IPV6_BINDANY)
               " IPV6_BINDANY"
 #endif
+#if defined(SO_BINDANY)
+              " SO_BINDANY"
+#endif
               "\n");
 #endif
        putchar('\n');
diff --git a/src/proto_tcp.c b/src/proto_tcp.c
index 0ae359a..5638257 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -142,6 +142,9 @@ int tcp_bind_socket(int fd, int flags, struct 
sockaddr_storage *local, struct so
 #if defined(IP_BINDANY)
                            || (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &one, 
sizeof(one)) == 0)
 #endif
+#if defined(SO_BINDANY)
+                           || (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &one, 
sizeof(one)) == 0)
+#endif
                            )
                                foreign_ok = 1;
                        else
@@ -157,6 +160,9 @@ int tcp_bind_socket(int fd, int flags, struct 
sockaddr_storage *local, struct so
 #if defined(IPV6_BINDANY)
                            || (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, 
&one, sizeof(one)) == 0)
 #endif
+#if defined(SO_BINDANY)
+                           || (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &one, 
sizeof(one)) == 0)
+#endif
                            )
                                foreign_ok = 1;
                        else
@@ -654,6 +660,9 @@ int tcp_bind_listener(struct listener *listener, char 
*errmsg, int errlen)
 #if defined(IP_BINDANY)
                            && (setsockopt(fd, IPPROTO_IP, IP_BINDANY, &one, 
sizeof(one)) == -1)
 #endif
+#if defined(SO_BINDANY)
+                           && (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &one, 
sizeof(one)) == -1)
+#endif
                            ) {
                                msg = "cannot make listening socket 
transparent";
                                err |= ERR_ALERT;
@@ -667,6 +676,9 @@ int tcp_bind_listener(struct listener *listener, char 
*errmsg, int errlen)
 #if defined(IPV6_BINDANY)
                            && (setsockopt(fd, IPPROTO_IPV6, IPV6_BINDANY, 
&one, sizeof(one)) == -1)
 #endif
+#if defined(SO_BINDANY)
+                           && (setsockopt(fd, SOL_SOCKET, SO_BINDANY, &one, 
sizeof(one)) == -1)
+#endif
                            ) {
                                msg = "cannot make listening socket 
transparent";
                                err |= ERR_ALERT;
-- 
1.7.12.2.21.g234cd45.dirty

Reply via email to