Added support to for SO_BINDTODEVICE on listening socket

This commit is contained in:
Mario Klebsch 2023-05-15 10:25:23 +02:00
parent ef60434b39
commit 2532ba0989
2 changed files with 103 additions and 1 deletions

View File

@ -64,6 +64,7 @@
#define ALNUM "([-a-z0-9._]+)" #define ALNUM "([-a-z0-9._]+)"
#define USERNAME "([^:]*)" #define USERNAME "([^:]*)"
#define PASSWORD "([^@]*)" #define PASSWORD "([^@]*)"
#define INTERFACE "[^ \t\\/]{1,16}"
#define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})" #define IP "((([0-9]{1,3})\\.){3}[0-9]{1,3})"
#define IPMASK "(" IP "(/" DIGIT "+)?)" #define IPMASK "(" IP "(/" DIGIT "+)?)"
#define IPV6 "(" \ #define IPV6 "(" \
@ -217,7 +218,11 @@ struct {
STDCONF (user, ALNUM, handle_user), STDCONF (user, ALNUM, handle_user),
STDCONF (group, ALNUM, handle_group), STDCONF (group, ALNUM, handle_group),
/* ip arguments */ /* ip arguments */
STDCONF (listen, "(" IP "|" IPV6 ")", handle_listen), STDCONF (listen, "(" IP "|" IPV6
#ifdef SO_BINDTODEVICE
"|" INTERFACE
#endif
")", handle_listen),
STDCONF (allow, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", STDCONF (allow, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")",
handle_allow), handle_allow),
STDCONF (deny, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")", STDCONF (deny, "(" "(" IPMASK "|" IPV6MASK ")" "|" ALNUM ")",

View File

@ -35,6 +35,10 @@
#include "conf.h" #include "conf.h"
#include "loop.h" #include "loop.h"
#include "sblist.h" #include "sblist.h"
#ifdef SO_BINDTODEVICE
#include <net/if.h>
#endif
/* /*
* Return a human readable error for getaddrinfo() and getnameinfo(). * Return a human readable error for getaddrinfo() and getnameinfo().
@ -310,6 +314,88 @@ static int listen_on_one_socket(struct addrinfo *ad)
return listenfd; return listenfd;
} }
#ifdef SO_BINDTODEVICE
static int listen_on_interface(const char *interface, uint16_t port)
{
int listenfd;
int ret;
int ipv6=1;
const int on = 1;
const int off = 0;
struct sockaddr_storage addr;
struct ifreq ifr;
listenfd = socket(PF_INET6, SOCK_STREAM, 0);
if (listenfd == -1) {
listenfd = socket(PF_INET, SOCK_STREAM, 0);
if (listenfd == -1) {
log_message(LOG_ERR, "socket() failed: %s", strerror(errno));
return -1;
}
ipv6=0;
}
ret = setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (ret != 0) {
log_message(LOG_ERR,
"setsockopt failed to set SO_REUSEADDR: %s",
strerror(errno));
close(listenfd);
return -1;
}
strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
if (setsockopt(listenfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) < 0) {
log_message(LOG_ERR, "setsockopt(SO_BINDTODEVICE,%s) failed: %s", interface, strerror (errno));
close(listenfd);
return -1;
}
if (ipv6) {
struct sockaddr_in6 *addr6;
ret = setsockopt(listenfd, IPPROTO_IPV6, IPV6_V6ONLY, &off,
sizeof(off));
if (ret != 0) {
log_message(LOG_ERR,
"setsockopt failed to clear IPV6_V6ONLY: %s",
strerror(errno));
close(listenfd);
return -1;
}
addr6 = (struct sockaddr_in6*)&addr;
memset(addr6, 0, sizeof(*addr6));
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(port);
addr6->sin6_addr = in6addr_any;
} else {
struct sockaddr_in *addr4;
addr4 = (struct sockaddr_in*)&addr;
memset(addr4, 0, sizeof(*addr4));
addr4->sin_family = AF_INET;
addr4->sin_port = htons(port);
addr4->sin_addr.s_addr = htonl(INADDR_ANY);
}
ret = bind(listenfd, (struct sockaddr*)&addr, sizeof(addr));
if (ret != 0) {
log_message(LOG_ERR, "bind failed: %s", strerror (errno));
close(listenfd);
return -1;
}
ret = listen(listenfd, MAXLISTEN);
if (ret != 0) {
log_message(LOG_ERR, "listen failed: %s", strerror(errno));
close(listenfd);
return -1;
}
log_message(LOG_INFO, "listening on fd [%d]", listenfd);
return listenfd;
}
#endif
/* /*
* Start listening on a socket. Create a socket with the selected port. * Start listening on a socket. Create a socket with the selected port.
* If the provided address is NULL, we may listen on multiple sockets, * If the provided address is NULL, we may listen on multiple sockets,
@ -333,6 +419,17 @@ int listen_sock (const char *addr, uint16_t port, sblist* listen_fds)
log_message(LOG_INFO, "listen_sock called with addr = '%s'", log_message(LOG_INFO, "listen_sock called with addr = '%s'",
addr == NULL ? "(NULL)" : addr); addr == NULL ? "(NULL)" : addr);
#ifdef SO_BINDTODEVICE
if (addr && if_nametoindex(addr)) {
int listenfd;
listenfd = listen_on_interface(addr, port);
if (listenfd >= 0) {
sblist_add (listen_fds, &listenfd);
return 0;
}
}
#endif
memset (&hints, 0, sizeof (struct addrinfo)); memset (&hints, 0, sizeof (struct addrinfo));
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM; hints.ai_socktype = SOCK_STREAM;