diff --git a/src/dnsmasq.c b/src/dnsmasq.c index 01df3d2..4893262 100644 --- a/src/dnsmasq.c +++ b/src/dnsmasq.c @@ -1354,20 +1354,29 @@ static void check_dns_listeners(fd_set *set, time_t now) else { int if_index; - + char intr_name[IF_NAMESIZE]; + /* In full wildcard mode, need to refresh interface list. This happens automagically in CLEVERBIND */ - if (!option_bool(OPT_CLEVERBIND)) - enumerate_interfaces(); - - /* 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 (!option_bool(OPT_CLEVERBIND)) + enumerate_interfaces(); + + /* 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 && + 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) if (iface->index == if_index) break; - if (!iface) + if (!iface && !loopback_exception(listener->tcpfd, tcp_addr.sa.sa_family, &addr, intr_name)) client_ok = 0; } @@ -1375,10 +1384,10 @@ static void check_dns_listeners(fd_set *set, time_t now) iface = listener->iface; /* May be NULL */ else { - /* Check for allowed interfaces when binding the wildcard address: - 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 - an allowed interface. As a side effect, we get the netmask of the + /* Check for allowed interfaces when binding the wildcard address: + 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 + an allowed interface. As a side effect, we get the netmask of the interface too, for localisation. */ for (iface = daemon->interfaces; iface; iface = iface->next) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 58bf12c..795c68c 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -1002,6 +1002,7 @@ void create_wildcard_listeners(void); void create_bound_listeners(int die); int is_dad_listeners(void); 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 tcp_interface(int fd, int af); struct in_addr get_ifaddr(char *intr); diff --git a/src/forward.c b/src/forward.c index 3454848..408a2f0 100644 --- a/src/forward.c +++ b/src/forward.c @@ -763,10 +763,17 @@ void receive_query(struct listener *listen, time_t now) /* enforce available interface configuration */ - if (!indextoname(listen->fd, if_index, ifr.ifr_name) || - !iface_check(listen->family, &dst_addr, ifr.ifr_name, &auth_dns)) + if (!indextoname(listen->fd, if_index, ifr.ifr_name)) 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)) { struct irec *iface; @@ -780,7 +787,7 @@ void receive_query(struct listener *listen, time_t now) break; /* interface may be new */ - if (!iface) + if (!iface && !option_bool(OPT_CLEVERBIND)) enumerate_interfaces(); for (iface = daemon->interfaces; iface; iface = iface->next) diff --git a/src/network.c b/src/network.c index db1e01a..a5c3b29 100644 --- a/src/network.c +++ b/src/network.c @@ -170,7 +170,40 @@ int iface_check(int family, struct all_addr *addr, char *name, int *auth) 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, union mysockaddr *addr, struct in_addr netmask, int dad) { diff --git a/src/tftp.c b/src/tftp.c index 4ce10b3..c2385fd 100644 --- a/src/tftp.c +++ b/src/tftp.c @@ -61,6 +61,7 @@ void tftp_request(struct listener *listen, time_t now) char *name = NULL; char *prefix = daemon->tftp_prefix; struct tftp_prefix *pref; + struct all_addr addra; union { struct cmsghdr align; /* this ensures alignment */ @@ -189,18 +190,22 @@ void tftp_request(struct listener *listen, time_t now) return; name = namebuff; + + addra.addr.addr4 = addr.in.sin_addr; #ifdef HAVE_IPV6 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; } - else -#endif - if (!iface_check(AF_INET, (struct all_addr *)&addr.in.sin_addr, name, NULL)) - return; - + #ifdef HAVE_DHCP /* allowed interfaces are the same as for DHCP */ for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)