From: Willy Tarreau Date: Wed, 4 Feb 2009 16:19:29 +0000 (+0100) Subject: [MINOR] add support for bind interface name X-Git-Tag: v1.3.16-rc1~53 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5e6e204d1c0f74308fcd011eb175b22ec0fb6a3b;p=thirdparty%2Fhaproxy.git [MINOR] add support for bind interface name By appending "interface " to a "bind" line, it is now possible to specifically bind to a physical interface name. Note that this currently only works on Linux and requires root privileges. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index b2cb48fed6..59a8322866 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -853,6 +853,7 @@ balance url_param [check_post []] bind [
]: [, ...] +bind [
]: [, ...] interface bind [
]: [, ...] transparent Define one or several listening addresses and/or ports in a frontend. May be used in sections : defaults | frontend | listen | backend @@ -868,6 +869,16 @@ bind [
]: [, ...] transparent mandatory. Note that in the case of an IPv6 address, the port is always the number after the last colon (':'). + is an optional physical interface name. This is currently + only supported on Linux. The interface must be a physical + interface, not an aliased interface. When specified, all + addresses on the same line will only be accepted if the + incoming packet physically come through the designated + interface. It is also possible to bind multiple frontends to + the same address if they are bound to different interfaces. + Note that binding to a physical interface requires root + privileges. + 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 diff --git a/include/types/protocols.h b/include/types/protocols.h index 4c64551bb9..5c3b60847e 100644 --- a/include/types/protocols.h +++ b/include/types/protocols.h @@ -96,6 +96,7 @@ struct listener { mode_t mode; /* 0 to leave unchanged */ } ux; } perm; + char *interface; /* interface name or NULL */ }; /* This structure contains all information needed to easily handle a protocol. diff --git a/src/cfgparse.c b/src/cfgparse.c index 416e67ca25..10f7b2accf 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -782,6 +782,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv) /* Now let's parse the proxy-specific keywords */ if (!strcmp(args[0], "bind")) { /* new listen addresses */ struct listener *last_listen; + int cur_arg; + if (curproxy == &defproxy) { Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); return -1; @@ -799,24 +801,50 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv) curproxy->listen = str2listener(args[1], last_listen); if (!curproxy->listen) return -1; - if (*args[2]) { + + cur_arg = 2; + while (*(args[cur_arg])) { + if (!strcmp(args[cur_arg], "interface")) { /* specifically bind to this interface */ +#ifdef SO_BINDTODEVICE + struct listener *l; + + if (!*args[cur_arg + 1]) { + Alert("parsing [%s:%d] : '%s' : missing interface name.\n", + file, linenum, args[0]); + return -1; + } + + for (l = curproxy->listen; l != last_listen; l = l->next) + l->interface = strdup(args[cur_arg + 1]); + + global.last_checks |= LSTCHK_NETADM; + + cur_arg += 2; + continue; +#else + Alert("parsing [%s:%d] : '%s' : '%s' option not implemented.\n", + file, linenum, args[0], args[cur_arg]); + return -1; +#endif + } + if (!strcmp(args[cur_arg], "transparent")) { /* transparently bind to these addresses */ #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]); + + cur_arg ++; + continue; +#else + Alert("parsing [%s:%d] : '%s' : '%s' option not implemented.\n", + file, linenum, args[0], args[cur_arg]); return -1; +#endif } -#else - Alert("parsing [%s:%d] : '%s' supports no option after the address list.\n", + Alert("parsing [%s:%d] : '%s' only supports the 'transparent' and 'interface' options.\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 94907ec762..78f63fcea4 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -240,6 +240,16 @@ int tcp_bind_listener(struct listener *listener, char *errmsg, int errlen) msg = "cannot make listening socket transparent"; err |= ERR_ALERT; } +#endif +#ifdef SO_BINDTODEVICE + /* Note: this might fail if not CAP_NET_RAW */ + if (listener->interface) { + if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, + listener->interface, strlen(listener->interface)) == -1) { + msg = "cannot bind listener to device"; + err |= ERR_WARN; + } + } #endif if (bind(fd, (struct sockaddr *)&listener->addr, listener->proto->sock_addrlen) == -1) { err |= ERR_RETRYABLE | ERR_ALERT;