diff --git a/doc/configuration.txt b/doc/configuration.txt index a804d23bd..fd62ef1e8 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -4294,6 +4294,7 @@ slowstart seen as failed. source [:] [usesrc { [:] | client | clientip } ] +source [:] [interface ] ... The "source" parameter sets the source address which will be used when connecting to the server. It follows the exact same parameters and principle as the backend "source" keyword, except that it only applies to the server diff --git a/include/types/server.h b/include/types/server.h index 09eb1bade..5c0186a88 100644 --- a/include/types/server.h +++ b/include/types/server.h @@ -91,6 +91,8 @@ struct server { #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 server *tracknext, *tracked; /* next server in a tracking list, tracked server */ char *trackit; /* temporary variable to make assignment deferrable */ diff --git a/src/backend.c b/src/backend.c index a6a0351af..e1911c5e3 100644 --- a/src/backend.c +++ b/src/backend.c @@ -1718,6 +1718,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->srv->iface_name) + setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, s->srv->iface_name, s->srv->iface_len); #endif ret = tcpv4_bind_socket(fd, flags, &s->srv->source_addr, remote); if (ret) { diff --git a/src/cfgparse.c b/src/cfgparse.c index 6507bff09..5136e9471 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1976,39 +1976,66 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int inv) newsrv->state |= SRV_BIND_SRC; newsrv->source_addr = *str2sa(args[cur_arg + 1]); cur_arg += 2; - if (!strcmp(args[cur_arg], "usesrc")) { /* address to use outside */ + 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 (newsrv->source_addr.sin_addr.s_addr == INADDR_ANY) { - Alert("parsing [%s:%d] : '%s' requires an explicit '%s' address.\n", - file, linenum, "usesrc", "source"); - return -1; - } + if (newsrv->source_addr.sin_addr.s_addr == INADDR_ANY) { + Alert("parsing [%s:%d] : '%s' requires an explicit '%s' address.\n", + file, linenum, "usesrc", "source"); + return -1; + } #endif - if (!*args[cur_arg + 1]) { - Alert("parsing [%s:%d] : '%s' expects [:], '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_NETADM; + if (!*args[cur_arg + 1]) { + Alert("parsing [%s:%d] : '%s' expects [:], '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_NETADM; #if !defined(CONFIG_HAP_LINUX_TPROXY) - global.last_checks |= LSTCHK_CTTPROXY; + global.last_checks |= LSTCHK_CTTPROXY; #endif - cur_arg += 2; + cur_arg += 2; + continue; #else /* no TPROXY support */ - Alert("parsing [%s:%d] : '%s' not allowed here because support for TPROXY was not compiled in.\n", + Alert("parsing [%s:%d] : '%s' not allowed here because support for TPROXY was not compiled in.\n", file, linenum, "usesrc"); return -1; +#endif /* defined(CONFIG_HAP_CTTPROXY) || defined(CONFIG_HAP_LINUX_TPROXY) */ + } /* "usesrc" */ + + 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 (newsrv->iface_name) + free(newsrv->iface_name); + + newsrv->iface_name = strdup(args[cur_arg + 1]); + newsrv->iface_len = strlen(newsrv->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 - } + cur_arg += 2; + continue; + } + /* this keyword in not an option of "source" */ + break; + } /* while */ } else if (!strcmp(args[cur_arg], "usesrc")) { /* address to use outside: needs "source" first */ Alert("parsing [%s:%d] : '%s' only allowed after a '%s' statement.\n", diff --git a/src/checks.c b/src/checks.c index aad164389..eb316cbb6 100644 --- a/src/checks.c +++ b/src/checks.c @@ -589,6 +589,12 @@ void process_chk(struct task *t, int *next) remote = (struct sockaddr_in *)&s->tproxy_addr; flags = 3; } +#endif +#ifdef SO_BINDTODEVICE + /* Note: this might fail if not CAP_NET_RAW */ + if (s->iface_name) + setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, + s->iface_name, s->iface_len); #endif ret = tcpv4_bind_socket(fd, flags, &s->source_addr, remote); if (ret) {