]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Handle wrong interface for locally-routed packets.
authorSimon Kelley <simon@thekelleys.org.uk>
Tue, 29 Jan 2013 22:10:26 +0000 (22:10 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Tue, 29 Jan 2013 22:10:26 +0000 (22:10 +0000)
src/dnsmasq.c
src/dnsmasq.h
src/forward.c
src/network.c
src/tftp.c

index 01df3d24a5e540affe238a185c2f098b890c8d25..4893262842b27f14615798c859a2343775b9943e 100644 (file)
@@ -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)
index 58bf12c507fd760acf4887833a9f08c1940b2021..795c68c691fb3af1c87ea32a6eb5dac74f838f68 100644 (file)
@@ -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);
index 34548481d3a8c46f0cf74d7d84e278480cdfe465..408a2f00e41eb9fa1977adcbb19e46d4820ff9cc 100644 (file)
@@ -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)
index db1e01aa4e977ded7fe5ec88d14766450582fdef..a5c3b2936f9b07999c73a886ceded66e4aa0f3c1 100644 (file)
@@ -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) 
 {
index 4ce10b3eb1cb947bd59d7bde31687a951d1c3ee5..c2385fd20403a1616028a2c2462e99a4623efa55 100644 (file)
@@ -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)