Handle wrong interface for locally-routed packets.

This commit is contained in:
Simon Kelley
2013-01-29 22:10:26 +00:00
parent 79cb46c0e9
commit e25db1f273
5 changed files with 76 additions and 21 deletions

View File

@@ -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)
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)

View File

@@ -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);

View File

@@ -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)
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)

View File

@@ -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)
{ {

View File

@@ -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)
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)