Commit e25db1f2 authored by Simon Kelley's avatar Simon Kelley

Handle wrong interface for locally-routed packets.

parent 79cb46c0
...@@ -1354,20 +1354,29 @@ static void check_dns_listeners(fd_set *set, time_t now) ...@@ -1354,20 +1354,29 @@ static void check_dns_listeners(fd_set *set, time_t now)
else else
{ {
int if_index; int if_index;
char intr_name[IF_NAMESIZE];
/* In full wildcard mode, need to refresh interface list. /* In full wildcard mode, need to refresh interface list.
This happens automagically in CLEVERBIND */ This happens automagically in CLEVERBIND */
if (!option_bool(OPT_CLEVERBIND)) if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces(); enumerate_interfaces();
/* if we can find the arrival interface, check it's one that's allowed */ /* if we can find the arrival interface, check it's one that's allowed */
if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0) if ((if_index = tcp_interface(confd, tcp_addr.sa.sa_family)) != 0 &&
indextoname(listener->tcpfd, if_index, intr_name))
{ {
struct all_addr addr;
addr.addr.addr4 = tcp_addr.in.sin_addr;
#ifdef HAVE_IPV6
if (tcp_addr.sa.sa_family == AF_INET6)
addr.addr.addr6 = tcp_addr.in6.sin6_addr;
#endif
for (iface = daemon->interfaces; iface; iface = iface->next) for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->index == if_index) if (iface->index == if_index)
break; break;
if (!iface) if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name))
client_ok = 0; client_ok = 0;
} }
...@@ -1375,10 +1384,10 @@ static void check_dns_listeners(fd_set *set, time_t now) ...@@ -1375,10 +1384,10 @@ static void check_dns_listeners(fd_set *set, time_t now)
iface = listener->iface; /* May be NULL */ iface = listener->iface; /* May be NULL */
else else
{ {
/* Check for allowed interfaces when binding the wildcard address: /* Check for allowed interfaces when binding the wildcard address:
we do this by looking for an interface with the same address as we do this by looking for an interface with the same address as
the local address of the TCP connection, then looking to see if that's the local address of the TCP connection, then looking to see if that's
an allowed interface. As a side effect, we get the netmask of the an allowed interface. As a side effect, we get the netmask of the
interface too, for localisation. */ interface too, for localisation. */
for (iface = daemon->interfaces; iface; iface = iface->next) for (iface = daemon->interfaces; iface; iface = iface->next)
......
...@@ -1002,6 +1002,7 @@ void create_wildcard_listeners(void); ...@@ -1002,6 +1002,7 @@ void create_wildcard_listeners(void);
void create_bound_listeners(int die); void create_bound_listeners(int die);
int is_dad_listeners(void); int is_dad_listeners(void);
int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns); int iface_check(int family, struct all_addr *addr, char *name, int *auth_dns);
int loopback_exception(int fd, int family, struct all_addr *addr, char *name);
int fix_fd(int fd); int fix_fd(int fd);
int tcp_interface(int fd, int af); int tcp_interface(int fd, int af);
struct in_addr get_ifaddr(char *intr); struct in_addr get_ifaddr(char *intr);
......
...@@ -763,10 +763,17 @@ void receive_query(struct listener *listen, time_t now) ...@@ -763,10 +763,17 @@ void receive_query(struct listener *listen, time_t now)
/* enforce available interface configuration */ /* enforce available interface configuration */
if (!indextoname(listen->fd, if_index, ifr.ifr_name) || if (!indextoname(listen->fd, if_index, ifr.ifr_name))
!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
return; return;
if (!iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns))
{
if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
if (!loopback_exception(listen->fd, listen->family, &dst_addr, ifr.ifr_name))
return;
}
if (listen->family == AF_INET && option_bool(OPT_LOCALISE)) if (listen->family == AF_INET && option_bool(OPT_LOCALISE))
{ {
struct irec *iface; struct irec *iface;
...@@ -780,7 +787,7 @@ void receive_query(struct listener *listen, time_t now) ...@@ -780,7 +787,7 @@ void receive_query(struct listener *listen, time_t now)
break; break;
/* interface may be new */ /* interface may be new */
if (!iface) if (!iface && !option_bool(OPT_CLEVERBIND))
enumerate_interfaces(); enumerate_interfaces();
for (iface = daemon->interfaces; iface; iface = iface->next) for (iface = daemon->interfaces; iface; iface = iface->next)
......
...@@ -170,7 +170,40 @@ int iface_check(int family, struct all_addr *addr, char *name, int *auth) ...@@ -170,7 +170,40 @@ int iface_check(int family, struct all_addr *addr, char *name, int *auth)
return ret; return ret;
} }
/* Fix for problem that the kernel sometimes reports the loopback inerface as the
arrival interface when a packet originates locally, even when sent to address of
an interface other than the loopback. Accept packet if it arrived via a loopback
interface, even when we're not accepting packets that way, as long as the destination
address is one we're believing. Interface list must be up-to-date before calling. */
int loopback_exception(int fd, int family, struct all_addr *addr, char *name)
{
struct ifreq ifr;
struct irec *iface;
strncpy(ifr.ifr_name, name, IF_NAMESIZE);
if (ioctl(fd, SIOCGIFFLAGS, &ifr) != -1 &&
ifr.ifr_flags & IFF_LOOPBACK)
{
for (iface = daemon->interfaces; iface; iface = iface->next)
if (iface->addr.sa.sa_family == family)
{
if (family == AF_INET)
{
if (iface->addr.in.sin_addr.s_addr == addr->addr.addr4.s_addr)
return 1;
}
#ifdef HAVE_IPV6
else if (IN6_ARE_ADDR_EQUAL(&iface->addr.in6.sin6_addr, &addr->addr.addr6))
return 1;
#endif
}
}
return 0;
}
static int iface_allowed(struct irec **irecp, int if_index, static int iface_allowed(struct irec **irecp, int if_index,
union mysockaddr *addr, struct in_addr netmask, int dad) union mysockaddr *addr, struct in_addr netmask, int dad)
{ {
......
...@@ -61,6 +61,7 @@ void tftp_request(struct listener *listen, time_t now) ...@@ -61,6 +61,7 @@ void tftp_request(struct listener *listen, time_t now)
char *name = NULL; char *name = NULL;
char *prefix = daemon->tftp_prefix; char *prefix = daemon->tftp_prefix;
struct tftp_prefix *pref; struct tftp_prefix *pref;
struct all_addr addra;
union { union {
struct cmsghdr align; /* this ensures alignment */ struct cmsghdr align; /* this ensures alignment */
...@@ -189,18 +190,22 @@ void tftp_request(struct listener *listen, time_t now) ...@@ -189,18 +190,22 @@ void tftp_request(struct listener *listen, time_t now)
return; return;
name = namebuff; name = namebuff;
addra.addr.addr4 = addr.in.sin_addr;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (listen->family == AF_INET6) if (listen->family == AF_INET6)
addra.addr.addr6 = addr.in6.sin6_addr;
#endif
if (!iface_check(listen->family, &addra, name, NULL))
{ {
if (!iface_check(AF_INET6, (struct all_addr *)&addr.in6.sin6_addr, name, NULL)) if (!option_bool(OPT_CLEVERBIND))
enumerate_interfaces();
if (!loopback_exception(listen->tftpfd, listen->family, &addra, name))
return; return;
} }
else
#endif
if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, NULL))
return;
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
/* allowed interfaces are the same as for DHCP */ /* allowed interfaces are the same as for DHCP */
for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next) for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment