[MINOR] add support for bind interface name
By appending "interface <name>" 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 b2cb48f..59a8322 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -853,6 +853,7 @@
bind [<address>]:<port> [, ...]
+bind [<address>]:<port> [, ...] interface <interface>
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
@@ -868,6 +869,16 @@
mandatory. Note that in the case of an IPv6 address, the port
is always the number after the last colon (':').
+ <interface> 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 4c64551..5c3b608 100644
--- a/include/types/protocols.h
+++ b/include/types/protocols.h
@@ -96,6 +96,7 @@
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 416e67c..10f7b2a 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -782,6 +782,8 @@
/* 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 @@
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 94907ec..78f63fc 100644
--- a/src/proto_tcp.c
+++ b/src/proto_tcp.c
@@ -241,6 +241,16 @@
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;
msg = "cannot bind socket";