]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Make RA without DHCPv6 possible.
authorSimon Kelley <simon@thekelleys.org.uk>
Mon, 27 Feb 2012 17:42:38 +0000 (17:42 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Mon, 27 Feb 2012 17:42:38 +0000 (17:42 +0000)
dnsmasq.conf.example
src/dhcp-common.c
src/dhcp6.c
src/dnsmasq.c
src/dnsmasq.h
src/lease.c
src/netlink.c
src/radv.c

index f2ec51a6015401c8fc2b008fa6fa4a207500ed97..e293024cac972da9ef56f5002f7146fd7b239449 100644 (file)
 # an explicit netmask instead.
 #dhcp-range=192.168.0.0,static
 
+# Enable DHCPv6. Note that the prefix-length does not need to be specified
+# and defaults to 64 if missing/
+#dhcp-range=1234::2, 1234::500, 64, 12h
+
+# Not Router Advertisements, BUT NOT DHCP for this subnet.
+#dhcp-range=1234::, ra-only 
+
 # Supply parameters for specified hosts using DHCP. There are lots
 # of valid alternatives, so we will give examples of each. Note that
 # IP addresses DO NOT have to be in the range given above, they just
 # any machine with Ethernet address starting 11:22:33:
 #dhcp-host=11:22:33:*:*:*,set:red
 
+# Give a fixed IPv6 address and name to client with 
+# DUID 00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2
+# Note the MAC addresses CANNOT be used to identify DHCPv6 clients.
+# Note also the they [] around the IPv6 address are obilgatory.
+#dhcp-host=id:00:01:00:01:16:d2:83:fc:92:d4:19:e2:d8:b2, fred, [1234::5] 
+
 # Ignore any clients which are not specified in dhcp-host lines
 # or /etc/ethers. Equivalent to ISC "deny unknown-clients".
 # This relies on the special "known" tag which is set when
 # Set the NTP time server addresses to 192.168.0.4 and 10.10.0.5
 #dhcp-option=option:ntp-server,192.168.0.4,10.10.0.5
 
+# Send DHCPv6 option. Note [] around IPv6 addresses.
+dhcp-option=option6:dns-server,[1234::77],[1234::88]
+
 # Set the NTP time server address to be the same machine as
 # is running dnsmasq
 #dhcp-option=42,0.0.0.0
index cf2c8751269f64c036c313d8bf3344f0d7d23554..451a3c13e2e4dec069a9597e646f82b616c9144e 100644 (file)
@@ -358,4 +358,90 @@ void dhcp_update_configs(struct dhcp_config *configs)
 
 }
 
+#ifdef HAVE_DHCP6
+static int join_multicast_worker(struct in6_addr *local, int prefix, 
+                                int scope, int if_index, int dad, void *vparam)
+{
+  char ifrn_name[IFNAMSIZ];
+  struct ipv6_mreq mreq;
+  int fd, i, max = *((int *)vparam);
+  struct dhcp_context *context;
+  struct iname *tmp;
+
+  (void)prefix;
+  (void)scope;
+  (void)dad;
+  
+  /* record which interfaces we join on, so that we do it at most one per 
+     interface, even when they have multiple addresses. Use outpacket
+     as an array of int, since it's always allocated here and easy
+     to expand for theoretical vast numbers of interfaces. */
+  for (i = 0; i < max; i++)
+    if (if_index == ((int *)daemon->outpacket.iov_base)[i])
+      return 1;
+  
+  if ((fd = socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
+    return 0;
+  
+  if (!indextoname(fd, if_index, ifrn_name))
+    {
+      close(fd);
+      return 0;
+    }
+  
+  close(fd);
+
+  /* Are we doing DHCP on this interface? */
+  if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
+    return 1;
+  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
+    if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
+      return 1;
+
+  /* weird libvirt-inspired access control */
+  for (context = daemon->dhcp6; context; context = context->next)
+    if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
+      break;
+
+  if (!context)
+    return 1;
+  
+  mreq.ipv6mr_interface = if_index;
+  
+  inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
+  
+  if (daemon->dhcp6 &&
+      setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
+    return 0;
+
+  inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
+  
+  if (daemon->dhcp6 && 
+      setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
+    return 0;
+  
+  inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
+  
+  if (daemon->ra_contexts &&
+      setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
+    return 0;
+  
+  expand_buf(&daemon->outpacket, (max+1) * sizeof(int));
+  ((int *)daemon->outpacket.iov_base)[max++] = if_index;
+  
+  *((int *)vparam) = max;
+  
+  return 1;
+}
+
+void join_multicast(void)
+{
+  int count = 0;
+
+   if (!iface_enumerate(AF_INET6, &count, join_multicast_worker))
+     die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
+}
+#endif
+
 #endif
index 4639d54939f32f0ca62fba03751c7b0d80cfb313..9f9e161b2a7b6148d90d29fd87bb5ce203d136f2 100644 (file)
@@ -24,14 +24,6 @@ struct iface_param {
   int ind;
 };
 
-struct listen_param {
-  int fd_or_iface;
-  struct listen_param *next;
-};
-
-static int join_multicast(struct in6_addr *local, int prefix, 
-                         int scope, int if_index, int dad, void *vparam);
-
 static int complete_context6(struct in6_addr *local,  int prefix,
                             int scope, int if_index, int dad, void *vparam);
 
@@ -41,7 +33,6 @@ void dhcp6_init(void)
 {
   int fd;
   struct sockaddr_in6 saddr;
-  struct listen_param *listenp, listen; 
 #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
   int class = IPTOS_CLASS_CS6;
 #endif
@@ -65,88 +56,9 @@ void dhcp6_init(void)
   if (bind(fd, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in6)))
     die(_("failed to bind DHCPv6 server socket: %s"), NULL, EC_BADNET);
   
-  /* join multicast groups on each interface we're interested in */
-  listen.fd_or_iface = fd;
-  listen.next = NULL;
-  if (!iface_enumerate(AF_INET6, &listen, join_multicast))
-     die(_("failed to join DHCPv6 multicast group: %s"), NULL, EC_BADNET);
-  for (listenp = listen.next; listenp; )
-    {
-      struct listen_param *tmp = listenp->next;
-      free(listenp);
-      listenp = tmp;
-    }
-  
   daemon->dhcp6fd = fd;
 }
 
-static int join_multicast(struct in6_addr *local, int prefix, 
-                         int scope, int if_index, int dad, void *vparam)
-{
-  char ifrn_name[IFNAMSIZ];
-  struct ipv6_mreq mreq;
-  struct listen_param *listenp, *param = vparam;
-  int fd = param->fd_or_iface;
-  struct dhcp_context *context;
-  struct iname *tmp;
-
-  (void)prefix;
-  (void)scope;
-  (void)dad;
-  
-  /* record which interfaces we join on, so
-     that we do it at most one per interface, even when they
-     have multiple addresses */
-  for (listenp = param->next; listenp; listenp = listenp->next)
-    if (if_index == listenp->fd_or_iface)
-      return 1;
-  
-  if (!indextoname(fd, if_index, ifrn_name))
-    return 0;
-
-  /* Are we doing DHCP on this interface? */
-  if (!iface_check(AF_INET6, (struct all_addr *)local, ifrn_name))
-    return 1;
-  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
-    if (tmp->name && (strcmp(tmp->name, ifrn_name) == 0))
-      return 1;
-
-  /* weird libvirt-inspired access control */
-  for (context = daemon->dhcp6; context; context = context->next)
-    if (!context->interface || strcmp(context->interface, ifrn_name) == 0)
-      break;
-
-  if (!context)
-    return 1;
-  
-  mreq.ipv6mr_interface = if_index;
-  
-  inet_pton(AF_INET6, ALL_RELAY_AGENTS_AND_SERVERS, &mreq.ipv6mr_multiaddr);
-  
-  if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
-    return 0;
-
-  inet_pton(AF_INET6, ALL_SERVERS, &mreq.ipv6mr_multiaddr);
-  
-  if (setsockopt(fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
-    return 0;
-  
-  inet_pton(AF_INET6, ALL_ROUTERS, &mreq.ipv6mr_multiaddr);
-  
-  if (daemon->icmp6fd != -1 &&
-      setsockopt(daemon->icmp6fd, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq, sizeof(mreq)) == -1)
-    return 0;
-  
-  listenp = whine_malloc(sizeof(struct listen_param));
-  listenp->fd_or_iface = if_index;
-  listenp->next = param->next;
-  param->next = listenp;
-  
-  return 1;
-}
-
-
 void dhcp6_packet(time_t now)
 {
   struct dhcp_context *context;
index cd59dd252171a3321de734fae6cd916523778c0b..82e4cad17acf7c5e8dd5f3b9a88f1be2d938220d 100644 (file)
@@ -154,24 +154,41 @@ int main (int argc, char **argv)
         before lease_init to allocate buffers it uses.*/
       dhcp_common_init();
       lease_init(now);
+  
       if (daemon->dhcp)
        dhcp_init();
-#ifdef HAVE_DHCP6
-      daemon->icmp6fd = -1;
-      if (daemon->dhcp6)
+    }
+  
+#  ifdef HAVE_DHCP6
+  /* Start RA subsystem if --enable-ra OR dhcp-range=<subnet>, ra-only */
+  if (daemon->ra_contexts || option_bool(OPT_RA))
+    {
+      /* link the DHCP6 contexts to the ra-only ones so we can traverse them all 
+        from ->ra_contexts, but only the non-ra-onlies from ->dhcp6 */
+      struct dhcp_context *context;
+      
+      if (!daemon->ra_contexts)
+       daemon->ra_contexts = daemon->dhcp6;
+      else
        {
-         /* ra_init before dhcp6_init, so dhcp6_init can setup multicast listening */
-         if (option_bool(OPT_RA))
-           ra_init(now); 
-         dhcp6_init();
+         for (context = daemon->ra_contexts; context->next; context = context->next);
+         context->next = daemon->dhcp6;
        }
-#endif
+      ra_init(now);
     }
+
+  if (daemon->dhcp6)
+    dhcp6_init();
+
+  if (daemon->ra_contexts || daemon->dhcp6)
+    join_multicast();
+#  endif
+
 #endif
 
   if (!enumerate_interfaces())
     die(_("failed to find list of interfaces: %s"), NULL, EC_MISC);
-    
+  
   if (option_bool(OPT_NOWILD)) 
     {
       create_bound_listeners(1);
@@ -211,7 +228,8 @@ int main (int argc, char **argv)
 
 #if defined(HAVE_SCRIPT)
   /* Note getpwnam returns static storage */
-  if ((daemon->dhcp || daemon->dhcp6) && daemon->scriptuser && 
+  if ((daemon->dhcp || daemon->dhcp6) && 
+      daemon->scriptuser && 
       (daemon->lease_change_command || daemon->luascript))
     {
       if ((ent_pw = getpwnam(daemon->scriptuser)))
@@ -502,7 +520,7 @@ int main (int argc, char **argv)
   if (daemon->max_logs != 0)
     my_syslog(LOG_INFO, _("asynchronous logging enabled, queue limit is %d messages"), daemon->max_logs);
  
-  if (option_bool(OPT_RA))
+  if (daemon->ra_contexts)
     my_syslog(MS_DHCP | LOG_INFO, _("IPv6 router advertisement enabled"));
 
 #ifdef HAVE_DHCP
@@ -668,7 +686,7 @@ int main (int argc, char **argv)
          FD_SET(daemon->dhcp6fd, &rset);
          bump_maxfd(daemon->dhcp6fd, &maxfd);
          
-         if (daemon->icmp6fd != -1)
+         if (daemon->ra_contexts)
            {
              FD_SET(daemon->icmp6fd, &rset);
              bump_maxfd(daemon->icmp6fd, &maxfd); 
@@ -777,7 +795,7 @@ int main (int argc, char **argv)
          if (FD_ISSET(daemon->dhcp6fd, &rset))
            dhcp6_packet(now);
 
-         if (daemon->icmp6fd != -1 && FD_ISSET(daemon->icmp6fd, &rset))
+         if (daemon->ra_contexts && FD_ISSET(daemon->icmp6fd, &rset))
            icmp6_packet();
        }
 #endif
@@ -953,6 +971,16 @@ static void async_event(int pipe, time_t now)
            lease_prune(NULL, now);
            lease_update_file(now);
          }
+#ifdef HAVE_DHCP6
+       else if (daemon->ra_contexts)
+         {
+           /* Not doing DHCP, so no lease system, manage 
+              alarms for ra only */
+           time_t next_event = periodic_ra(now);
+           if (next_event != 0)
+             alarm((unsigned)difftime(next_event, now)); 
+         }
+#endif
 #endif
        break;
                
@@ -1117,6 +1145,16 @@ void clear_cache_and_reload(time_t now)
       lease_update_file(now); 
       lease_update_dns();
     }
+#ifdef HAVE_DHCP6
+  else if (daemon->ra_contexts)
+    {
+      /* Not doing DHCP, so no lease system, manage 
+        alarms for ra only */
+      time_t next_event = periodic_ra(now);
+      if (next_event != 0)
+       alarm((unsigned)difftime(next_event, now)); 
+    }
+#endif
 #endif
 }
 
@@ -1402,7 +1440,7 @@ int icmp_ping(struct in_addr addr)
       set_log_writer(&wset, &maxfd);
       
 #ifdef HAVE_DHCP6
-      if (daemon->icmp6fd != -1)
+      if (daemon->ra_contexts)
        {
          FD_SET(daemon->icmp6fd, &rset);
          bump_maxfd(daemon->icmp6fd, &maxfd); 
@@ -1421,7 +1459,7 @@ int icmp_ping(struct in_addr addr)
       check_dns_listeners(&rset, now);
 
 #ifdef HAVE_DHCP6
-      if (daemon->icmp6fd != -1 && FD_ISSET(daemon->icmp6fd, &rset))
+      if (daemon->ra_contexts && FD_ISSET(daemon->icmp6fd, &rset))
        icmp6_packet();
 #endif
       
index 70b5dac6e9866645269e69d33f8f9b52ccb13333..e72e53ca79fcfcbd5af7b7509ea52c12945804df 100644 (file)
@@ -1059,6 +1059,9 @@ void log_tags(struct dhcp_netid *netid, u32 xid);
 int match_bytes(struct dhcp_opt *o, unsigned char *p, int len);
 void dhcp_update_configs(struct dhcp_config *configs);
 void check_dhcp_hosts(int fatal);
+#  ifdef HAVE_DHCP6
+void join_multicast(void);
+#  endif
 #endif
 
 /* outpacket.c */
index 49ce016759af184c297c7bc358b563132d92f732..b4a1c3055894f4e6ef9cf703750950bf99bfba2d 100644 (file)
@@ -317,7 +317,7 @@ void lease_update_file(time_t now)
 
 #ifdef HAVE_DHCP6
   /* do timed RAs and determine when the next is */
-  if (option_bool(OPT_RA))
+  if (daemon->ra_contexts)
     next_event = periodic_ra(now);
 #endif
 
index a8cea33470faf53bda4fb70d2a1e6f8bbbbaf714..993c1f9748ec2330ce52d261f2bd09ed5dac22c2 100644 (file)
@@ -343,7 +343,7 @@ static void nl_routechange(struct nlmsghdr *h)
       
 #ifdef HAVE_DHCP6
       /* force RAs to sync new network and pick up new interfaces.  */
-      if (option_bool(OPT_RA))
+      if (daemon->ra_contexts)
        {
          ra_start_unsolicted(dnsmasq_time());
          /* cause lease_update_file to run after we return, in case we were called from
index e4adc8c8205dfc4804f84995beb6cf1fbcf187e4..d8e522d95427439523bf0fada764326a242099ad 100644 (file)
 */
 
 
-/* NB. This code may be called during a DHCPv4 transaction which is in ping-wait
+/* NB. This code may be called during a DHCPv4 or transaction which is in ping-wait
    It therefore cannot use any DHCP buffer resources except outpacket, which is
-   not used by DHCPv4 code. */
+   not used by DHCPv4 code. This code may also be called when DHCP 4 or 6 isn't
+   active, so we ensure that outpacket is allocated here too */
 
 #include "dnsmasq.h"
 
@@ -47,7 +48,6 @@ static time_t ra_short_period_start;
 
 void ra_init(time_t now)
 {
-  struct dhcp_context *context;
   struct icmp6_filter filter;
   int fd;
 #if defined(IP_TOS) && defined(IPTOS_CLASS_CS6)
@@ -56,6 +56,9 @@ void ra_init(time_t now)
   int val = 255; /* radvd uses this value */
   size_t len = sizeof(int);
 
+  /* ensure this is around even if we're not doing DHCPv6 */
+  expand_buf(&daemon->outpacket, sizeof(struct dhcp_packet));
+
   ICMP6_FILTER_SETBLOCKALL(&filter);
   ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filter);
   ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
@@ -74,19 +77,6 @@ void ra_init(time_t now)
   
    daemon->icmp6fd = fd;
    
-   /* link the DHCP6 contexts to the ra-only ones so we can traverse them all 
-      from ->ra_contexts, but only the non-ra-onlies from ->dhcp6 */
-   if (!daemon->ra_contexts)
-     daemon->ra_contexts = daemon->dhcp6;
-   else
-     {
-       for (context = daemon->ra_contexts; context->next; context = context->next);
-       context->next = daemon->dhcp6;
-     }
-
-   if (!daemon->dhcp6)
-     die(_("cannot do router advertisement unless DHCPv6 is enabled"), NULL, EC_BADCONF);
-
    ra_start_unsolicted(now);
 }