mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 10:18:25 +00:00
Use IP[V6]_UNICAST_IF socket option instead of SO_BINDTODEVICE for DNS.
dnsmasq allows to specify a interface for each name server passed with the -S option or pushed through D-Bus; when an interface is set, queries to the server will be forced via that interface. Currently dnsmasq uses SO_BINDTODEVICE to enforce that traffic goes through the given interface; SO_BINDTODEVICE also guarantees that any response coming from other interfaces is ignored. This can cause problems in some scenarios: consider the case where eth0 and eth1 are in the same subnet and eth0 has a name server ns0 associated. There is no guarantee that the response to a query sent via eth0 to ns0 will be received on eth0 because the local router may have in the ARP table the MAC address of eth1 for the IP of eth0. This can happen because Linux sends ARP responses for all the IPs of the machine through all interfaces. The response packet on the wrong interface will be dropped because of SO_BINDTODEVICE and the resolution will fail. To avoid this situation, dnsmasq should only restrict queries, but not responses, to the given interface. A way to do this on Linux is with the IP_UNICAST_IF and IPV6_UNICAST_IF socket options which were added in kernel 3.4 and, respectively, glibc versions 2.16 and 2.26. Reported-by: Hector Martin <marcan@marcan.st> Signed-off-by: Beniamino Galvani <bgalvani@redhat.com>
This commit is contained in:
@@ -2,6 +2,10 @@ version 2.79
|
||||
Fix parsing of CNAME arguments, which are confused by extra spaces.
|
||||
Thanks to Diego Aguirre for spotting the bug.
|
||||
|
||||
Where available, use IP_UNICAST_IF or IPV6_UNICAST_IF to bind
|
||||
upstream servers to an interface, rather than SO_BINDTODEVICE.
|
||||
Thanks to Beniamino Galvani for the patch.
|
||||
|
||||
|
||||
version 2.78
|
||||
Fix logic of appending ".<layer>" to PXE basename. Thanks to Chris
|
||||
|
||||
@@ -1254,7 +1254,7 @@ void free_rfd(struct randfd *rfd);
|
||||
|
||||
/* network.c */
|
||||
int indextoname(int fd, int index, char *name);
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp);
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp);
|
||||
int random_sock(int family);
|
||||
void pre_allocate_sfds(void);
|
||||
int reload_servers(char *fname);
|
||||
|
||||
@@ -1551,7 +1551,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
|
||||
setsockopt(server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||
#endif
|
||||
|
||||
if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 1) ||
|
||||
if (!local_bind(server->tcpfd, &server->source_addr, server->interface, 0, 1) ||
|
||||
connect(server->tcpfd, &server->addr.sa, sa_len(&server->addr)) == -1)
|
||||
{
|
||||
close(server->tcpfd);
|
||||
@@ -1847,7 +1847,7 @@ unsigned char *tcp_request(int confd, time_t now,
|
||||
setsockopt(last_server->tcpfd, SOL_SOCKET, SO_MARK, &mark, sizeof(unsigned int));
|
||||
#endif
|
||||
|
||||
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 1) ||
|
||||
if ((!local_bind(last_server->tcpfd, &last_server->source_addr, last_server->interface, 0, 1) ||
|
||||
connect(last_server->tcpfd, &last_server->addr.sa, sa_len(&last_server->addr)) == -1))
|
||||
{
|
||||
close(last_server->tcpfd);
|
||||
|
||||
@@ -1187,7 +1187,7 @@ int random_sock(int family)
|
||||
}
|
||||
|
||||
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
|
||||
int local_bind(int fd, union mysockaddr *addr, char *intname, unsigned int ifindex, int is_tcp)
|
||||
{
|
||||
union mysockaddr addr_copy = *addr;
|
||||
|
||||
@@ -1204,7 +1204,25 @@ int local_bind(int fd, union mysockaddr *addr, char *intname, int is_tcp)
|
||||
|
||||
if (bind(fd, (struct sockaddr *)&addr_copy, sa_len(&addr_copy)) == -1)
|
||||
return 0;
|
||||
|
||||
|
||||
if (!is_tcp && ifindex > 0)
|
||||
{
|
||||
#if defined(IP_UNICAST_IF)
|
||||
if (addr_copy.sa.sa_family == AF_INET)
|
||||
{
|
||||
uint32_t ifindex_opt = htonl(ifindex);
|
||||
return setsockopt(fd, IPPROTO_IP, IP_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0;
|
||||
}
|
||||
#endif
|
||||
#if defined(HAVE_IPV6) && defined (IPV6_UNICAST_IF)
|
||||
if (addr_copy.sa.sa_family == AF_INET6)
|
||||
{
|
||||
uint32_t ifindex_opt = htonl(ifindex);
|
||||
return setsockopt(fd, IPPROTO_IPV6, IPV6_UNICAST_IF, &ifindex_opt, sizeof(ifindex_opt)) == 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(SO_BINDTODEVICE)
|
||||
if (intname[0] != 0 &&
|
||||
setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, intname, IF_NAMESIZE) == -1)
|
||||
@@ -1260,7 +1278,7 @@ static struct serverfd *allocate_sfd(union mysockaddr *addr, char *intname)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!local_bind(sfd->fd, addr, intname, 0) || !fix_fd(sfd->fd))
|
||||
if (!local_bind(sfd->fd, addr, intname, ifindex, 0) || !fix_fd(sfd->fd))
|
||||
{
|
||||
errsave = errno; /* save error from bind. */
|
||||
close(sfd->fd);
|
||||
|
||||
Reference in New Issue
Block a user