diff --git a/ChangeLog.txt b/ChangeLog.txt index b90624c88e..2324897401 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -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. diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index db3bef1e43..6ea2b10fb7 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -1035,21 +1035,13 @@ log_timestamp_format %Y-%m-%dT%H:%M:%S option but is useful when an interface has multiple addresses or the address may change. - It is valid to use this option together with + If used at the same time as the for the default listener, or the bind - address/host part of the - definition. Care should - be taken to ensure that the address being bound to - is on the interface being bound to. If you set the - to be - eth0, and - to - 127.0.0.1, then the - broker will start correctly but you will be unable - to connect. - This option is currently only available on - Linux, and requires elevated privileges. + address/host part of the + , then + will take priority. + This option is not available on Windows. Not reloaded on reload signal. diff --git a/mosquitto.conf b/mosquitto.conf index c1f17c1b30..4e407417bd 100644 --- a/mosquitto.conf +++ b/mosquitto.conf @@ -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 diff --git a/src/net.c b/src/net.c index b274458233..b18a401bbd 100644 --- a/src/net.c +++ b/src/net.c @@ -24,7 +24,7 @@ SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 #include #include #include -#include +#include #else #include #include @@ -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);