]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] add support for source interface binding
authorWilly Tarreau <w@1wt.eu>
Wed, 4 Feb 2009 17:46:54 +0000 (18:46 +0100)
committerWilly Tarreau <w@1wt.eu>
Wed, 4 Feb 2009 17:46:54 +0000 (18:46 +0100)
Specifying "interface <name>" after the "source" statement allows
one to bind to a specific interface for proxy<->server traffic.

This makes it possible to use multiple links to reach multiple
servers, and to force traffic to pass via an interface different
from the one the system would have chosen based on the routing
table.

doc/configuration.txt
include/types/proxy.h
src/backend.c
src/cfgparse.c
src/checks.c

index 59a8322866fa1365d1ac20d25204f9e311d2c930..a804d23bd9e12714b91855e3121dc865a83c1ddc 100644 (file)
@@ -2839,6 +2839,7 @@ server <name> <address>[:port] [param*]
 
 
 source <addr>[:<port>] [usesrc { <addr2>[:<port2>] | client | clientip } ]
+source <addr>[:<port>] [interface <name>]
   Set the source address for outgoing connections
   May be used in sections :   defaults | frontend | listen | backend
                                  yes   |    no    |   yes  |   yes
@@ -2864,6 +2865,13 @@ source <addr>[:<port>] [usesrc { <addr2>[:<port2>] | client | clientip } ]
               The default value of zero means the system will select a free
               port.
 
+    <name>    is an optional interface name to which to bind to for outgoing
+              traffic. On systems supporting this features (currently, only
+              Linux), this allows one to bind all traffic to the server to
+              this interface even if it is not the one the system would select
+              based on routing tables. This should be used with extreme care.
+              Note that using this option requires root privileges.
+
   The "source" keyword is useful in complex environments where a specific
   address only is allowed to connect to the servers. It may be needed when a
   private address must be used through a public gateway for instance, and it is
index 5d0869cb5aee8b73dc17ecc85db7fbf8f362e19c..cc5069007afc8a9c6259be8cc621133e5969766e 100644 (file)
@@ -233,6 +233,8 @@ struct proxy {
 #if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
        struct sockaddr_in tproxy_addr;         /* non-local address we want to bind to for connect() */
 #endif
+       int iface_len;                          /* bind interface name length */
+       char *iface_name;                       /* bind interface name or NULL */
        struct proxy *next;
        struct logsrv logsrv1, logsrv2;         /* 2 syslog servers */
        signed char logfac1, logfac2;           /* log facility for both servers. -1 = disabled */
index be3dcf6a84936dbfc96956524b9835e4f25f5b2e..a6a0351af98e6ed78f3b1ca0b60a1ec16ad5d166 100644 (file)
@@ -1757,6 +1757,11 @@ int connect_server(struct session *s)
                        remote = (struct sockaddr_in *)&s->cli_addr;
                        break;
                }
+#endif
+#ifdef SO_BINDTODEVICE
+               /* Note: this might fail if not CAP_NET_RAW */
+               if (s->be->iface_name)
+                       setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, s->be->iface_name, s->be->iface_len);
 #endif
                ret = tcpv4_bind_socket(fd, flags, &s->be->source_addr, remote);
                if (ret) {
index 10f7b2accf3d9e60b9fd3dfb69e9649df68e5305..6507bff09d57937445a7e124d5903401e4c8f297 100644 (file)
@@ -705,6 +705,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                        if (defproxy.url_param_name)
                                curproxy->url_param_name = strdup(defproxy.url_param_name);
                        curproxy->url_param_len = defproxy.url_param_len;
+
+                       if (defproxy.iface_name)
+                               curproxy->iface_name = strdup(defproxy.iface_name);
+                       curproxy->iface_len  = defproxy.iface_len;
                }
 
                if (curproxy->cap & PR_CAP_RS) {
@@ -761,6 +765,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                free(defproxy.capture_name);
                free(defproxy.monitor_uri);
                free(defproxy.defbe.name);
+               free(defproxy.iface_name);
                free(defproxy.fwdfor_hdr_name);
                defproxy.fwdfor_hdr_len = 0;
 
@@ -2123,54 +2128,82 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv)
                }
        }
        else if (!strcmp(args[0], "source")) {  /* address to which we bind when connecting */
+               int cur_arg;
+
                if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL))
                        return 0;
 
                if (!*args[1]) {
-#if defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY)
-                       Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], and optional '%s' <addr> as argument.\n",
-                             file, linenum, "source", "usesrc");
-#else
-                       Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>] as argument.\n",
-                             file, linenum, "source");
-#endif
+                       Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], and optionally '%s' <addr>, and '%s' <name>.\n",
+                             file, linenum, "source", "usesrc", "interface");
                        return -1;
                }
        
                curproxy->source_addr = *str2sa(args[1]);
                curproxy->options |= PR_O_BIND_SRC;
-               if (!strcmp(args[2], "usesrc")) {  /* address to use outside */
+
+               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 (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 (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;
+                               }
+#endif
+                               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")) {
+                                       curproxy->options |= PR_O_TPXY_CLI;
+                               } else if (!strcmp(args[cur_arg + 1], "clientip")) {
+                                       curproxy->options |= PR_O_TPXY_CIP;
+                               } else {
+                                       curproxy->options |= PR_O_TPXY_ADDR;
+                                       curproxy->tproxy_addr = *str2sa(args[cur_arg + 1]);
+                               }
+                               global.last_checks |= LSTCHK_NETADM;
+#if !defined(CONFIG_HAP_LINUX_TPROXY)
+                               global.last_checks |= LSTCHK_CTTPROXY;
 #endif
-                       if (!*args[3]) {
-                               Alert("parsing [%s:%d] : '%s' expects <addr>[:<port>], 'client', or 'clientip' as argument.\n",
+#else  /* no TPROXY support */
+                               Alert("parsing [%s:%d] : '%s' not allowed here because support for TPROXY was not compiled in.\n",
                                      file, linenum, "usesrc");
                                return -1;
+#endif
+                               cur_arg += 2;
+                               continue;
                        }
 
-                       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_NETADM;
-#if !defined(CONFIG_HAP_LINUX_TPROXY)
-                       global.last_checks |= LSTCHK_CTTPROXY;
+                       if (!strcmp(args[cur_arg], "interface")) { /* specifically bind to this interface */
+#ifdef SO_BINDTODEVICE
+                               if (!*args[cur_arg + 1]) {
+                                       Alert("parsing [%s:%d] : '%s' : missing interface name.\n",
+                                             file, linenum, args[0]);
+                                       return -1;
+                               }
+                               if (curproxy->iface_name)
+                                       free(curproxy->iface_name);
+
+                               curproxy->iface_name = strdup(args[cur_arg + 1]);
+                               curproxy->iface_len  = strlen(curproxy->iface_name);
+                               global.last_checks |= LSTCHK_NETADM;
+#else
+                               Alert("parsing [%s:%d] : '%s' : '%s' option not implemented.\n",
+                                     file, linenum, args[0], args[cur_arg]);
+                               return -1;
 #endif
-#else  /* no TPROXY support */
-                       Alert("parsing [%s:%d] : '%s' not allowed here because support for TPROXY was not compiled in.\n",
-                             file, linenum, "usesrc");
+                               cur_arg += 2;
+                               continue;
+                       }
+                       Alert("parsing [%s:%d] : '%s' only supports optional keywords '%s' and '%s'.\n",
+                             file, linenum, args[0], "inteface", "usesrc");
                        return -1;
-#endif
                }
        }
        else if (!strcmp(args[0], "usesrc")) {  /* address to use outside: needs "source" first */
index d6ce3355e16ff2c0a9506386a644b247b2a51bb0..aad1643891ebad658b228ee3665f4b89ba4f4285 100644 (file)
@@ -614,6 +614,12 @@ void process_chk(struct task *t, int *next)
                                                remote = (struct sockaddr_in *)&s->proxy->tproxy_addr;
                                                flags  = 3;
                                        }
+#endif
+#ifdef SO_BINDTODEVICE
+                                       /* Note: this might fail if not CAP_NET_RAW */
+                                       if (s->proxy->iface_name)
+                                               setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE,
+                                                          s->proxy->iface_name, s->proxy->iface_len);
 #endif
                                        ret = tcpv4_bind_socket(fd, flags, &s->proxy->source_addr, remote);
                                        if (ret) {