Fix bind_interface option.

Closes #1999. Thanks to Joerg55.
This commit is contained in:
Roger Light 2021-01-07 22:47:26 +00:00
parent 28c28fe707
commit 886ee6cd0c
4 changed files with 59 additions and 29 deletions

View File

@ -8,6 +8,7 @@ Broker:
- Fix apparmor incorrectly denying access to
/var/lib/mosquitto/mosquitto.db.new. Closes #1978.
- Fix potential intermittent initial bridge connections when using poll().
- Fix `bind_interface` option. Closes #1999.
Apps:
- Disallow control characters in mosquitto_passwd usernames.

View File

@ -1035,21 +1035,13 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S
<option>bind_address</option> option but is useful
when an interface has multiple addresses or the
address may change.</para>
<para>It is valid to use this option together with
<para>If used at the same time as the
<option>bind_address</option> for the default
listener, or the <replaceable>bind
address/host</replaceable> part of the
<option>listener</option> definition. Care should
be taken to ensure that the address being bound to
is on the interface being bound to. If you set the
<option>bind_interface</option> to be
<replaceable>eth0</replaceable>, and
<option>bind_address</option> to
<replaceable>127.0.0.1</replaceable>, then the
broker will start correctly but you will be unable
to connect.</para>
<para>This option is currently only available on
Linux, and requires elevated privileges.</para>
address/host</replaceable> part of the
<option>listener</option>, then <option>bind_interface</option>
will take priority.</para>
<para>This option is not available on Windows.</para>
<para>Not reloaded on reload signal.</para>
</listitem>
</varlistentry>

View File

@ -229,11 +229,10 @@
# Bind the listener to a specific interface. This is similar to
# the [ip address/host name] part of the listener definition, but is useful
# when an interface has multiple addresses or the address may change. It is
# valid to use this with the [ip address/host name] part of the listener
# definition, but take care that the interface you are binding to contains the
# address you are binding to, otherwise you will not be able to connect.
# Only available on Linux and requires elevated privileges.
# when an interface has multiple addresses or the address may change. If used
# with the [ip address/host name] part of the listener definition, then the
# bind_interface option will take priority.
# Not available on Windows.
#
# Example: bind_interface eth0
#bind_interface

View File

@ -24,7 +24,7 @@ Contributors:
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/tcp.h>
#include <net/if.h>
#include <ifaddrs.h>
#else
#include <winsock2.h>
#include <ws2tcpip.h>
@ -607,6 +607,52 @@ int net__tls_load_verify(struct mosquitto__listener *listener)
}
#ifndef WIN32
static int net__bind_interface(struct mosquitto__listener *listener, mosq_sock_t sock, struct addrinfo *rp)
{
/*
* This binds the listener sock to a network interface.
* The use of SO_BINDTODEVICE requires root access, which we don't have, so instead
* use getifaddrs to find the interface addresses, and attempt to bind to
* the IP of the matching interface.
*/
struct ifaddrs *ifaddr, *ifa;
if(getifaddrs(&ifaddr) < 0){
net__print_error(MOSQ_LOG_ERR, "Error: %s");
return MOSQ_ERR_ERRNO;
}
for(ifa=ifaddr; ifa!=NULL; ifa=ifa->ifa_next){
if(ifa->ifa_addr == NULL){
continue;
}
if(!strcasecmp(listener->bind_interface, ifa->ifa_name)
&& ifa->ifa_addr->sa_family == rp->ai_addr->sa_family){
if(rp->ai_addr->sa_family == AF_INET){
memcpy(&((struct sockaddr_in *)rp->ai_addr)->sin_addr,
&((struct sockaddr_in *)ifa->ifa_addr)->sin_addr,
sizeof(struct in_addr));
freeifaddrs(ifaddr);
return MOSQ_ERR_SUCCESS;
}else if(rp->ai_addr->sa_family == AF_INET6){
memcpy(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr,
&((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr,
sizeof(struct in6_addr));
freeifaddrs(ifaddr);
return MOSQ_ERR_SUCCESS;
}
}
}
freeifaddrs(ifaddr);
log__printf(NULL, MOSQ_LOG_ERR, "Error: Interface %s not found.", listener->bind_interface);
return MOSQ_ERR_NOT_FOUND;
}
#endif
static int net__socket_listen_tcp(struct mosquitto__listener *listener)
{
mosq_sock_t sock = INVALID_SOCKET;
@ -615,9 +661,6 @@ static int net__socket_listen_tcp(struct mosquitto__listener *listener)
char service[10];
int rc;
int ss_opt = 1;
#ifdef SO_BINDTODEVICE
struct ifreq ifr;
#endif
if(!listener) return MOSQ_ERR_INVAL;
@ -680,14 +723,9 @@ static int net__socket_listen_tcp(struct mosquitto__listener *listener)
return 1;
}
#ifdef SO_BINDTODEVICE
#ifndef WIN32
if(listener->bind_interface){
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, listener->bind_interface, sizeof(ifr.ifr_name)-1);
ifr.ifr_name[sizeof(ifr.ifr_name)-1] = '\0';
log__printf(NULL, MOSQ_LOG_INFO, "Binding listener to interface \"%s\".", ifr.ifr_name);
if(setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, (void *)&ifr, sizeof(ifr)) < 0) {
net__print_error(MOSQ_LOG_ERR, "Error: %s");
if(net__bind_interface(listener, sock, rp)){
COMPAT_CLOSE(sock);
freeaddrinfo(ainfo);
mosquitto__free(listener->socks);