]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Fix hang at startup when DHCPv6 enabled on a complex network
authorSimon Kelley <simon@thekelleys.org.uk>
Sat, 18 Feb 2012 17:08:50 +0000 (17:08 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Sat, 18 Feb 2012 17:08:50 +0000 (17:08 +0000)
configuration - we have to read all the MAC addresses from netlink,
not bail when we find a suitable one.

Fix thinko in dhcp_update_configs - thanks to Hartmut for spotting
that.

Get a sensible address for the default DNS server even when using a
relay.

src/dhcp-common.c
src/dhcp6.c
src/dnsmasq.h
src/netlink.c
src/rfc3315.c

index 6de8e61ab6d787d89a7c3184bf8a8c6194e478e4..8b8bb67b1ccde78c78d329ab32d5bdb55283ad01 100644 (file)
@@ -332,9 +332,9 @@ void dhcp_update_configs(struct dhcp_config *configs)
              }
 
 #ifdef HAVE_DHCP6
-           if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 129, 0))
+           if (prot == AF_INET6 && !config_find_by_address6(configs, &crec->addr.addr.addr.addr6, 128, 0))
              {
-               memcpy(config->hwaddr, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
+               memcpy(&config->addr6, &crec->addr.addr.addr.addr6, IN6ADDRSZ);
                config->flags |= CONFIG_ADDR6 | CONFIG_ADDR_HOSTS;
                continue;
              }
index ba61ae2f54d446525c555bc01da888e7a674053a..45a9bfd6bcd76b3346b95a435cac292926b24a60 100644 (file)
@@ -20,6 +20,7 @@
 
 struct iface_param {
   struct dhcp_context *current;
+  struct in6_addr fallback;
   int ind;
 };
 
@@ -200,10 +201,14 @@ void dhcp6_packet(time_t now)
 
   /* unlinked contexts are marked by context->current == context */
   for (context = daemon->dhcp6; context; context = context->next)
-    context->current = context;
-  
+    {
+      context->current = context;
+      memset(&context->local6, 0, IN6ADDRSZ);
+    }
+
   parm.current = NULL;
   parm.ind = if_index;
+  memset(&parm.fallback, 0, IN6ADDRSZ);
   
   if (!iface_enumerate(AF_INET6, &parm, complete_context6))
     return;
@@ -211,7 +216,7 @@ void dhcp6_packet(time_t now)
   lease_prune(NULL, now); /* lose any expired leases */
 
   msg.msg_iov =  &daemon->dhcp_packet;
-  sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, sz, IN6_IS_ADDR_MULTICAST(&from.in6.sin6_addr), now);
+  sz = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, sz, IN6_IS_ADDR_MULTICAST(&from.in6.sin6_addr), now);
   
   lease_update_file(now);
   lease_update_dns();
@@ -229,23 +234,32 @@ static int complete_context6(struct in6_addr *local,  int prefix,
 
   (void)scope; /* warning */
   (void)dad;
-
-  for (context = daemon->dhcp6; context; context = context->next)
+  
+  if (if_index == param->ind &&
+      !IN6_IS_ADDR_LOOPBACK(local) &&
+      !IN6_IS_ADDR_LINKLOCAL(local) &&
+      !IN6_IS_ADDR_MULTICAST(local))
     {
-      if (prefix == context->prefix &&
-         !IN6_IS_ADDR_LOOPBACK(local) &&
-         !IN6_IS_ADDR_LINKLOCAL(local) &&
-         !IN6_IS_ADDR_MULTICAST(local) &&
-         is_same_net6(local, &context->start6, prefix) &&
-          is_same_net6(local, &context->end6, prefix))
-        {
-          /* link it onto the current chain if we've not seen it before */
-          if (if_index == param->ind && context->current == context)
-            {
-              context->current = param->current;
-              param->current = context;
-             context->local6 = *local;
-            }
+      /* Determine a globally address on the arrival interface, even
+        if we have no matching dhcp-context, because we're only
+        allocating on remote subnets via relays. This
+        is used as a default for the DNS server option. */
+      memcpy(&param->fallback, &local, IN6ADDRSZ);
+      
+      for (context = daemon->dhcp6; context; context = context->next)
+       {
+         if (prefix == context->prefix &&
+             is_same_net6(local, &context->start6, prefix) &&
+             is_same_net6(local, &context->end6, prefix))
+           {
+             /* link it onto the current chain if we've not seen it before */
+             if (context->current == context)
+               {
+                 context->current = param->current;
+                 param->current = context;
+                 context->local6 = *local;
+               }
+           }
        }
     }          
   return 1;
@@ -305,7 +319,7 @@ int address6_allocate(struct dhcp_context *context,  unsigned char *clid, int cl
          do {
            /* eliminate addresses in use by the server. */
            for (d = context; d; d = d->current)
-             if (addr == addr6part(&d->router6))
+             if (addr == addr6part(&d->local6))
                break;
 
            if (!d &&
index b1750925c387a2873f0892330c445e83b75aa920..575d9a74b127b2d2f7928b0f6f59da83ff3bb0d4 100644 (file)
@@ -603,7 +603,7 @@ struct dhcp_context {
   struct in_addr start, end; /* range of available addresses */
 #ifdef HAVE_DHCP6
   struct in6_addr start6, end6; /* range of available addresses */
-  struct in6_addr local6, router6;
+  struct in6_addr local6;
   int prefix;
 #endif
   int flags;
@@ -1036,7 +1036,8 @@ void make_duid(time_t now);
 
 /* rfc3315.c */
 #ifdef HAVE_DHCP6
-size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, size_t sz, int is_multicast, time_t now);
+size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,  
+                  struct in6_addr *fallback, size_t sz, int is_multicast, time_t now);
 #endif
 
 /* dhcp-common.c */
index f4e94025156f605f04716488f4df293909b145f5..a7d8fb2008f172255ff8f146b941da656b0758be 100644 (file)
@@ -136,6 +136,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
   struct nlmsghdr *h;
   ssize_t len;
   static unsigned int seq = 0;
+  int callback_ok = 1;
 
   struct {
     struct nlmsghdr nlh;
@@ -186,7 +187,7 @@ int iface_enumerate(int family, void *parm, int (*callback)())
        else if (h->nlmsg_type == NLMSG_ERROR)
          nl_err(h);
        else if (h->nlmsg_type == NLMSG_DONE)
-         return 1;
+         return callback_ok;
        else if (h->nlmsg_type == RTM_NEWADDR && family != AF_UNSPEC && family != AF_LOCAL)
          {
            struct ifaddrmsg *ifa = NLMSG_DATA(h);  
@@ -213,9 +214,9 @@ int iface_enumerate(int family, void *parm, int (*callback)())
                        rta = RTA_NEXT(rta, len1);
                      }
                    
-                   if (addr.s_addr)
+                   if (addr.s_addr && callback_ok)
                      if (!((*callback)(addr, ifa->ifa_index, netmask, broadcast, parm)))
-                       return 0;
+                       callback_ok = 0;
                  }
 #ifdef HAVE_IPV6
                else if (ifa->ifa_family == AF_INET6)
@@ -229,10 +230,10 @@ int iface_enumerate(int family, void *parm, int (*callback)())
                        rta = RTA_NEXT(rta, len1);
                      }
                    
-                   if (addrp)
+                   if (addrp && callback_ok)
                      if (!((*callback)(addrp, (int)(ifa->ifa_prefixlen), (int)(ifa->ifa_scope), 
                                        (int)(ifa->ifa_index), (int)(ifa->ifa_flags & IFA_F_TENTATIVE), parm)))
-                       return 0;
+                       callback_ok = 0;
                  }
 #endif
              }
@@ -258,9 +259,9 @@ int iface_enumerate(int family, void *parm, int (*callback)())
                rta = RTA_NEXT(rta, len1);
              }
 
-           if (inaddr && mac)
+           if (inaddr && mac && callback_ok)
              if (!((*callback)(neigh->ndm_family, inaddr, mac, maclen, parm)))
-               return 0;
+               callback_ok = 0;
          }
 #ifdef HAVE_DHCP6
        else if (h->nlmsg_type == RTM_NEWLINK && family == AF_LOCAL)
@@ -282,9 +283,9 @@ int iface_enumerate(int family, void *parm, int (*callback)())
                rta = RTA_NEXT(rta, len1);
              }
 
-           if (mac && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) && 
+           if (mac && callback_ok && !((link->ifi_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))) && 
                !((*callback)((unsigned int)link->ifi_type, mac, maclen, parm)))
-             return 0;
+             callback_ok = 0;
          }
 #endif
     }
index c8f62d0701b36f6de589c044244b4f95a1e43da2..4a39e2bad3940a2a44fd9963062dc27e318fcc16 100644 (file)
@@ -31,9 +31,9 @@ static void put_opt6_long(unsigned int val);
 static void put_opt6_string(char *s);
 
 static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context, 
-                            int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now);
+                            int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);
 static int dhcp6_no_relay(int msg_type,  struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context, 
-                         int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now);
+                         int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);
 static void log6_packet(char *type, unsigned char *clid, int clid_len, struct in6_addr *addr, int xid, char *iface, char *string);
 
 static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
@@ -45,7 +45,8 @@ static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
 #define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
 
 
-size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name, size_t sz, int is_unicast, time_t now)
+size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,
+                  struct in6_addr *fallback, size_t sz, int is_unicast, time_t now)
 {
   struct dhcp_netid *relay_tags = NULL;
   struct dhcp_vendor *vendor;
@@ -56,7 +57,7 @@ size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name
   
   outpacket_counter = 0;
   
-  if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, daemon->dhcp_packet.iov_base, sz, is_unicast, now))
+  if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now))
     return outpacket_counter;
 
   return 0;
@@ -64,7 +65,7 @@ size_t dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name
 
 /* This cost me blood to write, it will probably cost you blood to understand - srk. */
 static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context,
-                            int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now)
+                            int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now)
 {
   void *end = inbuff + sz;
   void *opts = inbuff + 34;
@@ -114,7 +115,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **
          return 0;
        }
 
-      return dhcp6_no_relay(msg_type, link_address, *relay_tagsp, context, interface, iface_name, inbuff, sz, is_unicast, now);
+      return dhcp6_no_relay(msg_type, link_address, *relay_tagsp, context, interface, iface_name, fallback, inbuff, sz, is_unicast, now);
     }
 
   /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
@@ -159,7 +160,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **
          memcpy(&link_address, inbuff + 2, IN6ADDRSZ); 
          /* Not, zero is_unicast since that is now known to refer to the 
             relayed packet, not the original sent by the client */
-         if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, interface, iface_name, opt6_ptr(opt, 0), opt6_len(opt), 0, now))
+         if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, interface, iface_name, fallback, opt6_ptr(opt, 0), opt6_len(opt), 0, now))
            return 0;
        }
       else
@@ -171,7 +172,7 @@ static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **
 }
 
 static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context, 
-                         int interface, char *iface_name, void *inbuff, size_t sz, int is_unicast, time_t now)
+                         int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now)
 {
   void *packet_options = inbuff + 4;
   void *end = inbuff + sz;
@@ -685,7 +686,7 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
                                    
                                    /* link temporarily */
                                    for (n = context_tags; n && n->next; n = n->next);
-                                   if (l = n)
+                                   if ((l = n))
                                      l->next = tags;
                                    
                                    for (n = run_tag_if(context_tags); n; n = n->next)
@@ -1191,10 +1192,15 @@ static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dh
       
     }
   
-  if (!done_dns)
+  if (!done_dns && 
+      (!IN6_IS_ADDR_UNSPECIFIED(&context->local6) ||
+       !IN6_IS_ADDR_UNSPECIFIED(fallback)))
     {
       o = new_opt6(OPTION6_DNS_SERVER);
-      put_opt6(&context->local6, IN6ADDRSZ);
+      if (IN6_IS_ADDR_UNSPECIFIED(&context->local6))
+       put_opt6(fallback, IN6ADDRSZ);
+      else
+       put_opt6(&context->local6, IN6ADDRSZ);
       end_opt6(o); 
     }