Skip to content

Commit

Permalink
Fix bind_interface option.
Browse files Browse the repository at this point in the history
Closes #1999. Thanks to Joerg55.
  • Loading branch information
ralight committed Jan 7, 2021
1 parent 28c28fe commit 886ee6c
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 29 deletions.
1 change: 1 addition & 0 deletions ChangeLog.txt
Expand Up @@ -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.
Expand Down
18 changes: 5 additions & 13 deletions man/mosquitto.conf.5.xml
Expand Up @@ -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>
Expand Down
9 changes: 4 additions & 5 deletions mosquitto.conf
Expand Up @@ -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
Expand Down
60 changes: 49 additions & 11 deletions src/net.c
Expand Up @@ -24,7 +24,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0
#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>
Expand Down Expand Up @@ -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;
Expand All @@ -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;

Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 886ee6c

Please sign in to comment.