]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MAJOR] support for source binding via cttproxy
authorWilly Tarreau <w@1wt.eu>
Sun, 12 Nov 2006 22:57:19 +0000 (23:57 +0100)
committerWilly Tarreau <w@1wt.eu>
Sun, 12 Nov 2006 22:57:19 +0000 (23:57 +0100)
Using the cttproxy kernel patch, it's possible to bind to any source
address. It is highly recommended to use the 03-natdel patch with the
other ones.

A new keyword appears as a complement to the "source" keyword : "usesrc".
The source address is mandatory and must be valid on the interface which
will see the packets. The "usesrc" option supports "client" (for full
client_ip:client_port spoofing), "client_ip" (for client_ip spoofing)
and any 'IP[:port]' combination to pretend to be another machine.

Right now, the source binding is missing from server health-checks if
set to another address. It must be implemented (think restricted firewalls).
The doc is still missing too.

Makefile
include/types/backend.h
include/types/global.h
include/types/proxy.h
include/types/server.h
src/backend.c
src/cfgparse.c

index 50be7d0c70da010129bce2eed36a51f38847e68d..1eef960aef93f98ef0b668950cf4431a4c00c3ad 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -92,14 +92,60 @@ ADDLIB =
 # set some defines when needed.
 # Known ones are -DENABLE_POLL, -DENABLE_EPOLL, and -DUSE_MY_EPOLL
 # - use -DTPROXY to compile with transparent proxy support.
+# - use -DCONFIG_HAP_CTTPROXY to enable full transparent proxy support
 DEFINE = -DTPROXY
 
+
+#### build options
+
+# do not change this one, enable USE_* variables instead.
+OPTIONS =
+
+ifneq ($(USE_CTTPROXY),)
+OPTIONS += -DCONFIG_HAP_CTTPROXY
+endif
+
+ifneq ($(USE_TPROXY),)
+OPTIONS += -DTPROXY
+endif
+
+ifneq ($(USE_POLL),)
+OPTIONS += -DENABLE_POLL
+endif
+
+ifneq ($(USE_EPOLL),)
+OPTIONS += -DENABLE_EPOLL
+endif
+
+ifneq ($(USE_MY_EPOLL),)
+OPTIONS += -DUSE_MY_EPOLL
+endif
+
+ifneq ($(USE_NETFILTER),)
+OPTIONS += -DNETFILTER
+endif
+
+ifneq ($(USE_EPOLL_WORKAROUND),)
+OPTIONS += -DEPOLL_CTL_MOD_WORKAROUND
+endif
+
+ifneq ($(USE_GETSOCKNAME),)
+OPTIONS += -DUSE_GETSOCKNAME
+endif
+
+ifneq ($(USE_REGPARM),)
+OPTIONS += -DCONFIG_HAP_USE_REGPARM
+endif
+
+#### end of build options
+
+
 # global options
 TARGET_OPTS=$(COPTS.$(TARGET))
 REGEX_OPTS=$(COPTS.$(REGEX))
 CPU_OPTS=$(COPTS.$(CPU))
 
-COPTS=-Iinclude $(ADDINC) $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) $(DEFINE)
+COPTS=-Iinclude $(ADDINC) $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) $(DEFINE) $(OPTIONS)
 LIBS=$(LIBS.$(TARGET)) $(LIBS.$(REGEX)) $(ADDLIB)
 
 CFLAGS = -Wall $(COPTS) $(DEBUG)
index a6a393a51d8ea0db2bd4db3879401090c2f3d7a8..f70a3707cf8d40953fed8f88cf74192e9c116ebf 100644 (file)
 #define PR_O_ABRT_CLOSE 0x00800000      /* immediately abort request when client closes */
 #define PR_O_SSL3_CHK   0x01000000      /* use SSLv3 CLIENT_HELLO packets for server health */
 
+#define        PR_O_TPXY_ADDR  0x02000000      /* bind to this non-local address when connect()ing */
+#define        PR_O_TPXY_CIP   0x04000000      /* bind to the client's IP address when connect()ing */
+#define        PR_O_TPXY_CLI   0x06000000      /* bind to the client's IP+port when connect()ing */
+#define        PR_O_TPXY_MASK  0x06000000      /* bind to a non-local address when connect()ing */
+
+
 #endif /* _TYPES_BACKEND_H */
 
 /*
index 681ac05820d4563e3681b1978071095f8e6f5895..40c6c997ab6b59e05f80ee9a29b1f2bbea7c12b6 100644 (file)
 #define        MODE_STARTING   128
 #define        MODE_FOREGROUND 256
 
+/* list of last checks to perform, depending on config options */
+#define LSTCHK_CAP_BIND        0x00000001      /* check that we can bind to any port */
+#define LSTCHK_CTTPROXY        0x00000002      /* check that tproxy is enabled */
+#define LSTCHK_NETADM  0x00000004      /* check that we have CAP_NET_ADMIN */
 
 /* FIXME : this will have to be redefined correctly */
 struct global {
-    int uid;
-    int gid;
-    int nbproc;
-    int maxconn;
-    int maxsock;               /* max # of sockets */
-    int rlimit_nofile;         /* default ulimit-n value : 0=unset */
-    int rlimit_memmax;         /* default ulimit-d in megs value : 0=unset */
-    int mode;
-    char *chroot;
-    char *pidfile;
-    int logfac1, logfac2;
-    int loglev1, loglev2;
-    struct sockaddr_in logsrv1, logsrv2;
+       int uid;
+       int gid;
+       int nbproc;
+       int maxconn;
+       int maxsock;            /* max # of sockets */
+       int rlimit_nofile;      /* default ulimit-n value : 0=unset */
+       int rlimit_memmax;      /* default ulimit-d in megs value : 0=unset */
+       int mode;
+       int last_checks;
+       char *chroot;
+       char *pidfile;
+       int logfac1, logfac2;
+       int loglev1, loglev2;
+       struct sockaddr_in logsrv1, logsrv2;
 };
 
 extern struct global global;
index f3304e63b609839f15be94dc6792188c75c7768d..8523a694a4c9fd705aae674e4b449e77c80d83bf 100644 (file)
@@ -102,6 +102,9 @@ struct proxy {
        int options;                            /* PR_O_REDISP, PR_O_TRANSP, ... */
        int mode;                               /* mode = PR_MODE_TCP, PR_MODE_HTTP or PR_MODE_HEALTH */
        struct sockaddr_in source_addr;         /* the address to which we want to bind for connect() */
+#ifdef CONFIG_HAP_CTTPROXY
+       struct sockaddr_in tproxy_addr;         /* non-local address we want to bind to for connect() */
+#endif
        struct proxy *next;
        struct sockaddr_in logsrv1, logsrv2;    /* 2 syslog servers */
        signed char logfac1, logfac2;           /* log facility for both servers. -1 = disabled */
index 37b8af15f11fe213719c6f4e77a52d8552e9996d..70e1f8fdb6398ff6ad068de4f59f808a5389d134 100644 (file)
 
 
 /* server flags */
-#define SRV_RUNNING    1       /* the server is UP */
-#define SRV_BACKUP     2       /* this server is a backup server */
-#define        SRV_MAPPORTS    4       /* this server uses mapped ports */
-#define        SRV_BIND_SRC    8       /* this server uses a specific source address */
-#define        SRV_CHECKED     16      /* this server needs to be checked */
+#define SRV_RUNNING    0x0001  /* the server is UP */
+#define SRV_BACKUP     0x0002  /* this server is a backup server */
+#define SRV_MAPPORTS   0x0004  /* this server uses mapped ports */
+#define SRV_BIND_SRC   0x0008  /* this server uses a specific source address */
+#define SRV_CHECKED    0x0010  /* this server needs to be checked */
+
+#define SRV_TPROXY_ADDR        0x0020  /* bind to this non-local address to reach this server */
+#define SRV_TPROXY_CIP 0x0040  /* bind to the client's IP address to reach this server */
+#define SRV_TPROXY_CLI 0x0060  /* bind to the client's IP+port to reach this server */
+#define SRV_TPROXY_MASK        0x0060  /* bind to a non-local address to reach this server */
 
 /* function which act on servers need to return various errors */
 #define SRV_STATUS_OK       0   /* everything is OK. */
@@ -60,6 +65,9 @@ struct server {
        struct task *queue_mgt;         /* the task associated to the queue processing */
        struct sockaddr_in addr;                /* the address to connect to */
        struct sockaddr_in source_addr; /* the address to which we want to bind for connect() */
+#ifdef CONFIG_HAP_CTTPROXY
+       struct sockaddr_in tproxy_addr; /* non-local address we want to bind to for connect() */
+#endif
        short check_port;                       /* the port to use for the health checks */
        int health;                             /* 0->rise-1 = bad; rise->rise+fall-1 = good */
        int rise, fall;                 /* time in iterations */
index 2884ea539f553c8a2999ead3be6213f4df492c5f..1813d34dee81480f90ef8def55cd188cd50bf256 100644 (file)
@@ -35,6 +35,9 @@
 #include <proto/stream_sock.h>
 #include <proto/task.h>
 
+#ifdef CONFIG_HAP_CTTPROXY
+#include <import/ip_tproxy.h>
+#endif
 
 /*
  * This function recounts the number of usable active and backup servers for
@@ -384,6 +387,42 @@ int connect_server(struct session *s)
                                 s->proxy->id, s->srv->id);
                        return SN_ERR_RESOURCE;
                }
+#ifdef CONFIG_HAP_CTTPROXY
+               if (s->srv->state & SRV_TPROXY_MASK) {
+                       struct in_tproxy itp1, itp2;
+                       memset(&itp1, 0, sizeof(itp1));
+
+                       itp1.op = TPROXY_ASSIGN;
+                       switch (s->srv->state & SRV_TPROXY_MASK) {
+                       case SRV_TPROXY_ADDR:
+                               itp1.v.addr.faddr = s->srv->tproxy_addr.sin_addr;
+                               itp1.v.addr.fport = s->srv->tproxy_addr.sin_port;
+                               break;
+                       case SRV_TPROXY_CLI:
+                               itp1.v.addr.fport = ((struct sockaddr_in *)&s->cli_addr)->sin_port;
+                               /* fall through */
+                       case SRV_TPROXY_CIP:
+                               /* FIXME: what can we do if the client connects in IPv6 ? */
+                               itp1.v.addr.faddr = ((struct sockaddr_in *)&s->cli_addr)->sin_addr;
+                               break;
+                       }
+
+                       /* set connect flag on socket */
+                       itp2.op = TPROXY_FLAGS;
+                       itp2.v.flags = ITP_CONNECT | ITP_ONCE;
+
+                       if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp1, sizeof(itp1)) == -1 ||
+                           setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
+                               Alert("Cannot bind to tproxy source address before connect() for server %s/%s. Aborting.\n",
+                                     s->proxy->id, s->srv->id);
+                               close(fd);
+                               send_log(s->proxy, LOG_EMERG,
+                                        "Cannot bind to tproxy source address before connect() for server %s/%s.\n",
+                                        s->proxy->id, s->srv->id);
+                               return SN_ERR_RESOURCE;
+                       }
+               }
+#endif
        }
        else if (s->proxy->options & PR_O_BIND_SRC) {
                setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(one));
@@ -395,6 +434,42 @@ int connect_server(struct session *s)
                                 s->proxy->id, s->srv->id);
                        return SN_ERR_RESOURCE;
                }
+#ifdef CONFIG_HAP_CTTPROXY
+               if (s->proxy->options & PR_O_TPXY_MASK) {
+                       struct in_tproxy itp1, itp2;
+                       memset(&itp1, 0, sizeof(itp1));
+
+                       itp1.op = TPROXY_ASSIGN;
+                       switch (s->proxy->options & PR_O_TPXY_MASK) {
+                       case PR_O_TPXY_ADDR:
+                               itp1.v.addr.faddr = s->srv->tproxy_addr.sin_addr;
+                               itp1.v.addr.fport = s->srv->tproxy_addr.sin_port;
+                               break;
+                       case PR_O_TPXY_CLI:
+                               itp1.v.addr.fport = ((struct sockaddr_in *)&s->cli_addr)->sin_port;
+                               /* fall through */
+                       case PR_O_TPXY_CIP:
+                               /* FIXME: what can we do if the client connects in IPv6 ? */
+                               itp1.v.addr.faddr = ((struct sockaddr_in *)&s->cli_addr)->sin_addr;
+                               break;
+                       }
+
+                       /* set connect flag on socket */
+                       itp2.op = TPROXY_FLAGS;
+                       itp2.v.flags = ITP_CONNECT | ITP_ONCE;
+
+                       if (setsockopt(fd, SOL_IP, IP_TPROXY, &itp1, sizeof(itp1)) == -1 ||
+                           setsockopt(fd, SOL_IP, IP_TPROXY, &itp2, sizeof(itp2)) == -1) {
+                               Alert("Cannot bind to tproxy source address before connect() for proxy %s. Aborting.\n",
+                                     s->proxy->id);
+                               close(fd);
+                               send_log(s->proxy, LOG_EMERG,
+                                        "Cannot bind to tproxy source address before connect() for server %s/%s.\n",
+                                        s->proxy->id, s->srv->id);
+                               return SN_ERR_RESOURCE;
+                       }
+               }
+#endif
        }
        
        if ((connect(fd, (struct sockaddr *)&s->srv_addr, sizeof(s->srv_addr)) == -1) &&
index 034a2f11bf5293c51cb147e192b3a89b8c5995ea..95922476125169f5079c9ddf562961d1c89aa92d 100644 (file)
@@ -1153,6 +1153,30 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
                                newsrv->state |= SRV_BIND_SRC;
                                newsrv->source_addr = *str2sa(args[cur_arg + 1]);
                                cur_arg += 2;
+#ifdef CONFIG_HAP_CTTPROXY
+                               if (!strcmp(args[cur_arg], "usesrc")) {  /* address to use outside */
+                                       if (newsrv->source_addr.sin_addr.s_addr == INADDR_ANY) {
+                                               Alert("parsing [%s:%d] : '%s' requires an explicit 'source' address.\n",
+                                                     file, linenum, "usesrc");
+                                               return -1;
+                                       }
+                                       if (!*args[cur_arg + 1]) {
+                                               Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], 'client', or 'clientip' as argument.\n",
+                                                     file, linenum, "usesrc");
+                                               return -1;
+                                       }
+                                       if (!strcmp(args[cur_arg + 1], "client")) {
+                                               newsrv->state |= SRV_TPROXY_CLI;
+                                       } else if (!strcmp(args[cur_arg + 1], "clientip")) {
+                                               newsrv->state |= SRV_TPROXY_CIP;
+                                       } else {
+                                               newsrv->state |= SRV_TPROXY_ADDR;
+                                               newsrv->tproxy_addr = *str2sa(args[cur_arg + 1]);
+                                       }
+                                       global.last_checks |= LSTCHK_CTTPROXY | LSTCHK_NETADM;
+                                       cur_arg += 2;
+                               }
+#endif
                        }
                        else {
                                Alert("parsing [%s:%d] : server %s only supports options 'backup', 'cookie', 'check', 'inter', 'rise', 'fall', 'port', 'source', 'minconn', 'maxconn' and 'weight'.\n",
@@ -1241,6 +1265,30 @@ int cfg_parse_listen(const char *file, int linenum, char **args)
        
                curproxy->source_addr = *str2sa(args[1]);
                curproxy->options |= PR_O_BIND_SRC;
+#ifdef CONFIG_HAP_CTTPROXY
+               if (!strcmp(args[2], "usesrc")) {  /* address to use outside */
+                       if (curproxy->source_addr.sin_addr.s_addr == INADDR_ANY) {
+                               Alert("parsing [%s:%d] : '%s' requires an explicit 'source' address.\n",
+                                     file, linenum, "usesrc");
+                               return -1;
+                       }
+                       if (!*args[3]) {
+                               Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], 'client', or 'clientip' as argument.\n",
+                                     file, linenum, "usesrc");
+                               return -1;
+                       }
+
+                       if (!strcmp(args[3], "client")) {
+                               curproxy->options |= PR_O_TPXY_CLI;
+                       } else if (!strcmp(args[3], "clientip")) {
+                               curproxy->options |= PR_O_TPXY_CIP;
+                       } else {
+                               curproxy->options |= PR_O_TPXY_ADDR;
+                               curproxy->tproxy_addr = *str2sa(args[3]);
+                       }
+                       global.last_checks |= LSTCHK_CTTPROXY | LSTCHK_NETADM;
+               }
+#endif
        }
        else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) {  /* replace request header from a regex */
                regex_t *preg;