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,6 +1354,7 @@ 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 */
@@ -1361,13 +1362,21 @@ static void check_dns_listeners(fd_set *set, time_t now)
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;
} }

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

@@ -171,6 +171,39 @@ 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 */
@@ -190,16 +191,20 @@ void tftp_request(struct listener *listen, time_t now)
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 */