Diff for /embedaddon/dnsmasq/src/dhcp6.c between versions 1.1.1.1 and 1.1.1.5

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.5, 2023/09/27 11:02:07
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2022 Simon Kelley
   
    This program is free software; you can redistribute it and/or modify     This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by     it under the terms of the GNU General Public License as published by
Line 18 Line 18
   
 #ifdef HAVE_DHCP6  #ifdef HAVE_DHCP6
   
   #include <netinet/icmp6.h>
   
 struct iface_param {  struct iface_param {
   struct dhcp_context *current;    struct dhcp_context *current;
  struct in6_addr fallback;  struct in6_addr fallback, ll_addr, ula_addr;
   int ind, addr_match;    int ind, addr_match;
 };  };
   
   
 static int complete_context6(struct in6_addr *local,  int prefix,  static int complete_context6(struct in6_addr *local,  int prefix,
                              int scope, int if_index, int flags,                                int scope, int if_index, int flags, 
                              unsigned int preferred, unsigned int valid, void *vparam);                               unsigned int preferred, unsigned int valid, void *vparam);
   
 static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm);   static int make_duid1(int index, unsigned int type, char *mac, size_t maclen, void *parm); 
   
 void dhcp6_init(void)  void dhcp6_init(void)
Line 48  void dhcp6_init(void) Line 50  void dhcp6_init(void)
       !set_ipv6pktinfo(fd))        !set_ipv6pktinfo(fd))
     die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);      die (_("cannot create DHCPv6 socket: %s"), NULL, EC_BADNET);
       
 /* When bind-interfaces is set, there might be more than one dnmsasq /* When bind-interfaces is set, there might be more than one dnsmasq
      instance binding port 547. That's OK if they serve different networks.       instance binding port 547. That's OK if they serve different networks.
     Need to set REUSEADDR|REUSEPORT to make this posible.     Need to set REUSEADDR|REUSEPORT to make this possible.
      Handle the case that REUSEPORT is defined, but the kernel doesn't        Handle the case that REUSEPORT is defined, but the kernel doesn't 
      support it. This handles the introduction of REUSEPORT on Linux. */       support it. This handles the introduction of REUSEPORT on Linux. */
   if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))    if (option_bool(OPT_NOWILD) || option_bool(OPT_CLEVERBIND))
     {      {
      int rc = -1, porterr = 0;      int rc = 0;
   
 #ifdef SO_REUSEPORT  #ifdef SO_REUSEPORT
       if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&        if ((rc = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &oneopt, sizeof(oneopt))) == -1 &&
          errno != ENOPROTOOPT)          errno == ENOPROTOOPT)
        porterr = 1;        rc = 0;
 #endif  #endif
               
      if (rc == -1 && !porterr)      if (rc != -1)
         rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));          rc = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &oneopt, sizeof(oneopt));
               
       if (rc == -1)        if (rc == -1)
Line 100  void dhcp6_packet(time_t now) Line 102  void dhcp6_packet(time_t now)
   struct ifreq ifr;    struct ifreq ifr;
   struct iname *tmp;    struct iname *tmp;
   unsigned short port;    unsigned short port;
     struct in6_addr dst_addr;
     struct in6_addr all_servers;
     
     memset(&dst_addr, 0, sizeof(dst_addr));
   
   msg.msg_control = control_u.control6;    msg.msg_control = control_u.control6;
   msg.msg_controllen = sizeof(control_u);    msg.msg_controllen = sizeof(control_u);
Line 112  void dhcp6_packet(time_t now) Line 118  void dhcp6_packet(time_t now)
   if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)    if ((sz = recv_dhcp_packet(daemon->dhcp6fd, &msg)) == -1)
     return;      return;
       
   #ifdef HAVE_DUMPFILE
     dump_packet_udp(DUMP_DHCPV6, (void *)daemon->dhcp_packet.iov_base, sz,
                     (union mysockaddr *)&from, NULL, daemon->dhcp6fd);
   #endif
     
   for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr; cmptr = CMSG_NXTHDR(&msg, cmptr))
     if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)      if (cmptr->cmsg_level == IPPROTO_IPV6 && cmptr->cmsg_type == daemon->v6pktinfo)
       {        {
Line 122  void dhcp6_packet(time_t now) Line 133  void dhcp6_packet(time_t now)
         p.c = CMSG_DATA(cmptr);          p.c = CMSG_DATA(cmptr);
                   
         if_index = p.p->ipi6_ifindex;          if_index = p.p->ipi6_ifindex;
           dst_addr = p.p->ipi6_addr;
       }        }
   
   if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))    if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
     return;      return;
       
   for (tmp = daemon->if_except; tmp; tmp = tmp->next)  
     if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))  
       return;  
   
  for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)  if (relay_reply6(&from, sz, ifr.ifr_name))
    if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name)) 
      return; 
  
  parm.current = NULL; 
  parm.ind = if_index; 
  parm.addr_match = 0; 
  memset(&parm.fallback, 0, IN6ADDRSZ); 
 
  for (context = daemon->dhcp6; context; context = context->next) 
    if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0) 
      { 
        /* wildcard context for DHCP-stateless only */ 
        parm.current = context; 
        context->current = NULL; 
      } 
    else 
      { 
        /* unlinked contexts are marked by context->current == context */ 
        context->current = context; 
        memset(&context->local6, 0, IN6ADDRSZ); 
      } 
   
  if (!iface_enumerate(AF_INET6, &parm, complete_context6)) 
    return; 
   
  if (daemon->if_names || daemon->if_addrs) 
     {      {
   #ifdef HAVE_DUMPFILE
         dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL,
                         (union mysockaddr *)&from, daemon->dhcp6fd);
   #endif
               
      for (tmp = daemon->if_names; tmp; tmp = tmp->next)      while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, 
                                save_counter(-1), 0, (struct sockaddr *)&from, 
                                sizeof(from))));
     }
   else
     {
       struct dhcp_bridge *bridge, *alias;
       
       for (tmp = daemon->if_except; tmp; tmp = tmp->next)
         if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))          if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
          break;          return;
      
      if (!tmp && !parm.addr_match)      for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
         if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
           return;
       
       parm.current = NULL;
       parm.ind = if_index;
       parm.addr_match = 0;
       memset(&parm.fallback, 0, IN6ADDRSZ);
       memset(&parm.ll_addr, 0, IN6ADDRSZ);
       memset(&parm.ula_addr, 0, IN6ADDRSZ);
       
       /* If the interface on which the DHCPv6 request was received is
          an alias of some other interface (as specified by the
          --bridge-interface option), change parm.ind so that we look
          for DHCPv6 contexts associated with the aliased interface
          instead of with the aliasing one. */
       for (bridge = daemon->bridges; bridge; bridge = bridge->next)
         {
           for (alias = bridge->alias; alias; alias = alias->next)
             if (wildcard_matchn(alias->iface, ifr.ifr_name, IF_NAMESIZE))
               {
                 parm.ind = if_nametoindex(bridge->iface);
                 if (!parm.ind)
                   {
                     my_syslog(MS_DHCP | LOG_WARNING,
                               _("unknown interface %s in bridge-interface"),
                               bridge->iface);
                     return;
                   }
                 break;
               }
           if (alias)
             break;
         }
       
       for (context = daemon->dhcp6; context; context = context->next)
         if (IN6_IS_ADDR_UNSPECIFIED(&context->start6) && context->prefix == 0)
           {
             /* wildcard context for DHCP-stateless only */
             parm.current = context;
             context->current = NULL;
           }
         else
           {
             /* unlinked contexts are marked by context->current == context */
             context->current = context;
             memset(&context->local6, 0, IN6ADDRSZ);
           }
       
       /* Ignore requests sent to the ALL_SERVERS multicast address for relay when
          we're listening there for DHCPv6 server reasons. */
       inet_pton(AF_INET6, ALL_SERVERS, &all_servers);
       
       if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
           relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
         return;          return;
         
         if (!iface_enumerate(AF_INET6, &parm, complete_context6))
           return;
         
         /* Check for a relay again after iface_enumerate/complete_context has had
            chance to fill in relay->iface_index fields. This handles first time through
            and any changes in interface config. */
         if (!IN6_ARE_ADDR_EQUAL(&dst_addr, &all_servers) &&
             relay_upstream6(if_index, (size_t)sz, &from.sin6_addr, from.sin6_scope_id, now))
           return;
         
         if (daemon->if_names || daemon->if_addrs)
           {
             
             for (tmp = daemon->if_names; tmp; tmp = tmp->next)
               if (tmp->name && wildcard_match(tmp->name, ifr.ifr_name))
                 break;
             
             if (!tmp && !parm.addr_match)
               return;
           }
         
         /* May have configured relay, but not DHCP server */
         if (!daemon->doing_dhcp6)
           return;
         
         lease_prune(NULL, now); /* lose any expired leases */
         
         port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, 
                            &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);
         
         /* The port in the source address of the original request should
            be correct, but at least once client sends from the server port,
            so we explicitly send to the client port to a client, and the
            server port to a relay. */
         if (port != 0)
           {
             from.sin6_port = htons(port);
             
   #ifdef HAVE_DUMPFILE
             dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1),
                             NULL, (union mysockaddr *)&from, daemon->dhcp6fd);
   #endif 
             
             while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,
                                      save_counter(-1), 0, (struct sockaddr *)&from, sizeof(from))));
           }
         
         /* These need to be called _after_ we send DHCPv6 packet, since lease_update_file()
            may trigger sending an RA packet, which overwrites our buffer. */
         lease_update_file(now);
         lease_update_dns(0);
     }      }
   }
   
  lease_prune(NULL, now); /* lose any expired leases */void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
 {
   /* Receiving a packet from a host does not populate the neighbour
      cache, so we send a neighbour discovery request if we can't 
      find the sender. Repeat a few times in case of packet loss. */
   
   struct neigh_packet neigh;
   union mysockaddr addr;
   int i, maclen;
   
  port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,   neigh.type = ND_NEIGHBOR_SOLICIT;
                     sz, IN6_IS_ADDR_MULTICAST(&from.sin6_addr), now);  neigh.code = 0;
   neigh.reserved = 0;
   neigh.target = *client;
   /* RFC4443 section-2.3: checksum has to be zero to be calculated */
   neigh.checksum = 0;
    
   memset(&addr, 0, sizeof(addr));
 #ifdef HAVE_SOCKADDR_SA_LEN
   addr.in6.sin6_len = sizeof(struct sockaddr_in6);
 #endif
   addr.in6.sin6_family = AF_INET6;
   addr.in6.sin6_port = htons(IPPROTO_ICMPV6);
   addr.in6.sin6_addr = *client;
   addr.in6.sin6_scope_id = iface;
       
  lease_update_file(now);  for (i = 0; i < 5; i++)
  lease_update_dns(0); 
   
  /* The port in the source address of the original request should 
     be correct, but at least once client sends from the server port, 
     so we explicitly send to the client port to a client, and the 
     server port to a relay. */ 
  if (port != 0) 
     {      {
      from.sin6_port = htons(port);      struct timespec ts;
      while (sendto(daemon->dhcp6fd, daemon->outpacket.iov_base, save_counter(0)      
                    0, (struct sockaddr *)&from, sizeof(from)) == -1 &&      if ((maclen = find_mac(&addr, mac, 0, now)) != 0)
           retry_send());        break;
           
       while(retry_send(sendto(daemon->icmp6fd, &neigh, sizeof(neigh), 0, &addr.sa, sizeof(addr))));
       
       ts.tv_sec = 0;
       ts.tv_nsec = 100000000; /* 100ms */
       nanosleep(&ts, NULL);
     }      }
 }  
   
     *maclenp = maclen;
     *mactypep = ARPHRD_ETHER;
   }
       
 static int complete_context6(struct in6_addr *local,  int prefix,  static int complete_context6(struct in6_addr *local,  int prefix,
                              int scope, int if_index, int flags, unsigned int preferred,                                int scope, int if_index, int flags, unsigned int preferred, 
                              unsigned int valid, void *vparam)                               unsigned int valid, void *vparam)
 {  {
   struct dhcp_context *context;    struct dhcp_context *context;
     struct shared_network *share;
     struct dhcp_relay *relay;
   struct iface_param *param = vparam;    struct iface_param *param = vparam;
   struct iname *tmp;    struct iname *tmp;
     int match = !daemon->if_addrs;
     
   (void)scope; /* warning */    (void)scope; /* warning */
       
  if (if_index == param->ind &&  if (if_index != param->ind)
      !IN6_IS_ADDR_LOOPBACK(local) &&    return 1;
      !IN6_IS_ADDR_LINKLOCAL(local) &&  
      !IN6_IS_ADDR_MULTICAST(local))  if (IN6_IS_ADDR_LINKLOCAL(local))
    {    param->ll_addr = *local;
      /* if we have --listen-address config, see if the   else if (IN6_IS_ADDR_ULA(local))
         arrival interface has a matching address. */    param->ula_addr = *local;
      for (tmp = daemon->if_addrs; tmp; tmp = tmp->next) 
        if (tmp->addr.sa.sa_family == AF_INET6 && 
            IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local)) 
          param->addr_match = 1; 
               
      /* Determine a globally address on the arrival interface, even  if (IN6_IS_ADDR_LOOPBACK(local) ||
         if we have no matching dhcp-context, because we're only      IN6_IS_ADDR_LINKLOCAL(local) ||
         allocating on remote subnets via relays. This      IN6_IS_ADDR_MULTICAST(local))
         is used as a default for the DNS server option. */    return 1;
      param->fallback = *local;  
        /* if we have --listen-address config, see if the 
      for (context = daemon->dhcp6; context; context = context->next)     arrival interface has a matching address. */
        {  for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
          if ((context->flags & CONTEXT_DHCP) &&    if (tmp->addr.sa.sa_family == AF_INET6 &&
              !(context->flags & CONTEXT_TEMPLATE) &&        IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
              prefix == context->prefix &&      match = param->addr_match = 1;
              is_same_net6(local, &context->start6, prefix) &&  
              is_same_net6(local, &context->end6, prefix))  /* 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. */
              /* link it onto the current chain if we've not seen it before */  param->fallback = *local;
              if (context->current == context)  
                {  for (context = daemon->dhcp6; context; context = context->next)
                  struct dhcp_context *tmp, **up;    if ((context->flags & CONTEXT_DHCP) &&
                          !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
                  /* use interface values only for contructed contexts */        prefix <= context->prefix &&
                  if (!(context->flags & CONTEXT_CONSTRUCTED))        context->current == context)
                    preferred = valid = 0xffffffff;      {
                  else if (flags & IFACE_DEPRECATED)        if (is_same_net6(local, &context->start6, context->prefix) &&
                    preferred = 0;            is_same_net6(local, &context->end6, context->prefix))
          {
                  if (context->flags & CONTEXT_DEPRECATE)            struct dhcp_context *tmp, **up;
                    preferred = 0;            
                              /* use interface values only for constructed contexts */
                  /* order chain, longest preferred time first */            if (!(context->flags & CONTEXT_CONSTRUCTED))
                  for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)              preferred = valid = 0xffffffff;
                    if (tmp->preferred <= preferred)            else if (flags & IFACE_DEPRECATED)
                      break;              preferred = 0;
                    else                    
                      up = &tmp->current;            if (context->flags & CONTEXT_DEPRECATE)
                                preferred = 0;
                  context->current = *up;            
                  *up = context;            /* order chain, longest preferred time first */
                  context->local6 = *local;            for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
                  context->preferred = preferred;              if (tmp->preferred <= preferred)
                  context->valid = valid;                break;
                }              else
            }                up = &tmp->current;
        }            
    }                      context->current = *up;
             *up = context;
             context->local6 = *local;
             context->preferred = preferred;
             context->valid = valid;
           }
         else
           {
             for (share = daemon->shared_networks; share; share = share->next)
               {
                 /* IPv4 shared_address - ignore */
                 if (share->shared_addr.s_addr != 0)
                   continue;
                         
                 if (share->if_index != 0)
                   {
                     if (share->if_index != if_index)
                       continue;
                   }
                 else
                   {
                     if (!IN6_ARE_ADDR_EQUAL(&share->match_addr6, local))
                       continue;
                   }
                 
                 if (is_same_net6(&share->shared_addr6, &context->start6, context->prefix) &&
                     is_same_net6(&share->shared_addr6, &context->end6, context->prefix))
                   {
                     context->current = param->current;
                     param->current = context;
                     context->local6 = *local;
                     context->preferred = context->flags & CONTEXT_DEPRECATE ? 0 :0xffffffff;
                     context->valid = 0xffffffff;
                   }
               }
           }      
       }
   
   if (match)
     for (relay = daemon->relay6; relay; relay = relay->next)
       if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6))
         relay->iface_index = if_index;
   
   return 1;    return 1;
 }  }
   
struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix, u64 addr)struct dhcp_config *config_find_by_address6(struct dhcp_config *configs, struct in6_addr *net, int prefix,  struct in6_addr *addr)
 {  {
   struct dhcp_config *config;    struct dhcp_config *config;
       
   for (config = configs; config; config = config->next)    for (config = configs; config; config = config->next)
    if ((config->flags & CONFIG_ADDR6) &&    if (config->flags & CONFIG_ADDR6)
        is_same_net6(&config->addr6, net, prefix) &&      {
        (prefix == 128 || addr6part(&config->addr6) == addr))        struct addrlist *addr_list;
      return config;        
         for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
           if ((!net || is_same_net6(&addr_list->addr.addr6, net, prefix) || ((addr_list->flags & ADDRLIST_WILDCARD) && prefix == 64)) &&
               is_same_net6(&addr_list->addr.addr6, addr, (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128))
             return config;
       }
       
   return NULL;    return NULL;
 }  }
   
struct dhcp_context *address6_allocate(struct dhcp_context *context,  unsigned char *clid, int clid_len, struct dhcp_context *address6_allocate(struct dhcp_context *context,  unsigned char *clid, int clid_len, int temp_addr,
                                       int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)                                          unsigned int iaid, int serial, struct dhcp_netid *netids, int plain_range, struct in6_addr *ans)
 {  {
   /* Find a free address: exclude anything in use and anything allocated to    /* Find a free address: exclude anything in use and anything allocated to
      a particular hwaddr/clientid/hostname in our configuration.       a particular hwaddr/clientid/hostname in our configuration.
Line 290  struct dhcp_context *address6_allocate(struct dhcp_con Line 460  struct dhcp_context *address6_allocate(struct dhcp_con
   u64 j;     u64 j; 
   
   /* hash hwaddr: use the SDBM hashing algorithm.  This works    /* hash hwaddr: use the SDBM hashing algorithm.  This works
     for MAC addresses, let's see how it manages with client-ids! */     for MAC addresses, let's see how it manages with client-ids! 
  for (j = iaid, i = 0; i < clid_len; i++)     For temporary addresses, we generate a new random one each time. */
    j += clid[i] + (j << 6) + (j << 16) - j;  if (temp_addr)
     j = rand64();
   else
     for (j = iaid, i = 0; i < clid_len; i++)
       j = clid[i] + (j << 6) + (j << 16) - j;
       
   for (pass = 0; pass <= plain_range ? 1 : 0; pass++)    for (pass = 0; pass <= plain_range ? 1 : 0; pass++)
     for (c = context; c; c = c->current)      for (c = context; c; c = c->current)
Line 302  struct dhcp_context *address6_allocate(struct dhcp_con Line 476  struct dhcp_context *address6_allocate(struct dhcp_con
         continue;          continue;
       else        else
         {           { 
          if (option_bool(OPT_CONSEC_ADDR))          if (!temp_addr && option_bool(OPT_CONSEC_ADDR))
            /* seed is largest extant lease addr in this context */            {
            start = lease_find_max_addr6(c) + serial;              /* seed is largest extant lease addr in this context,
                  skip addresses equal to the number of addresses rejected
                  by clients. This should avoid the same client being offered the same
                  address after it has rjected it. */
               start = lease_find_max_addr6(c) + 1 + serial + c->addr_epoch;
               if (c->addr_epoch)
                 c->addr_epoch--;
             }
           else            else
            start = addr6part(&c->start6) + ((j + c->addr_epoch) % (1 + addr6part(&c->end6) - addr6part(&c->start6)));            {
               u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);
               u64 offset = j + c->addr_epoch;
   
                 /* don't divide by zero if range is whole 2^64 */
                 if (range != 0)
                   offset = offset % range;
   
                 start = addr6part(&c->start6) + offset;
               }
   
           /* iterate until we find a free address. */            /* iterate until we find a free address. */
           addr = start;            addr = start;
                       
Line 316  struct dhcp_context *address6_allocate(struct dhcp_con Line 506  struct dhcp_context *address6_allocate(struct dhcp_con
             for (d = context; d; d = d->current)              for (d = context; d; d = d->current)
               if (addr == addr6part(&d->local6))                if (addr == addr6part(&d->local6))
                 break;                  break;
               
               *ans = c->start6;
               setaddr6part (ans, addr);
   
             if (!d &&              if (!d &&
                 !lease6_find_by_addr(&c->start6, c->prefix, addr) &&                   !lease6_find_by_addr(&c->start6, c->prefix, addr) && 
                !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, addr))                !config_find_by_address6(daemon->dhcp_conf, &c->start6, c->prefix, ans))
              {              return c;
                *ans = c->start6;            
                setaddr6part (ans, addr); 
                return c; 
              } 
         
             addr++;              addr++;
                           
             if (addr  == addr6part(&c->end6) + 1)              if (addr  == addr6part(&c->end6) + 1)
Line 379  struct dhcp_context *address6_valid(struct dhcp_contex Line 568  struct dhcp_context *address6_valid(struct dhcp_contex
   return NULL;    return NULL;
 }  }
   
 int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)  
 {  
   if (!config || !(config->flags & CONFIG_ADDR6))  
     return 0;  
   
   if ((config->flags & CONFIG_WILDCARD) && context->prefix == 64)  
     {  
       *addr = context->start6;  
       setaddr6part(addr, addr6part(&config->addr6));  
       return 1;  
     }  
     
   if (is_same_net6(&context->start6, &config->addr6, context->prefix))  
     {  
       *addr = config->addr6;  
       return 1;  
     }  
     
   return 0;  
 }  
   
 static int is_config_in_context6(struct dhcp_context *context, struct dhcp_config *config)  
 {  
   if (!(config->flags & CONFIG_ADDR6) ||   
       (config->flags & CONFIG_WILDCARD))  
   
     return 1;  
     
   for (; context; context = context->current)  
     if (is_same_net6(&config->addr6, &context->start6, context->prefix))  
       return 1;  
         
   return 0;  
 }  
   
   
 struct dhcp_config *find_config6(struct dhcp_config *configs,  
                                  struct dhcp_context *context,  
                                  unsigned char *duid, int duid_len,  
                                  char *hostname)  
 {  
   struct dhcp_config *config;   
         
   if (duid)  
     for (config = configs; config; config = config->next)  
       if (config->flags & CONFIG_CLID)  
         {  
           if (config->clid_len == duid_len &&   
               memcmp(config->clid, duid, duid_len) == 0 &&  
               is_config_in_context6(context, config))  
             return config;  
         }  
       
   if (hostname && context)  
     for (config = configs; config; config = config->next)  
       if ((config->flags & CONFIG_NAME) &&   
           hostname_isequal(config->hostname, hostname) &&  
           is_config_in_context6(context, config))  
         return config;  
   
   return NULL;  
 }  
   
 void make_duid(time_t now)  void make_duid(time_t now)
 {  {
     (void)now;
   
   if (daemon->duid_config)    if (daemon->duid_config)
     {      {
       unsigned char *p;        unsigned char *p;
Line 456  void make_duid(time_t now) Line 584  void make_duid(time_t now)
     }      }
   else    else
     {      {
         time_t newnow = 0;
         
         /* If we have no persistent lease database, or a non-stable RTC, use DUID_LL (newnow == 0) */
   #ifndef HAVE_BROKEN_RTC
       /* rebase epoch to 1/1/2000 */        /* rebase epoch to 1/1/2000 */
      time_t newnow = now - 946684800;      if (!option_bool(OPT_LEASE_RO) || daemon->lease_change_command)
         newnow = now - 946684800;
 #endif      
               
       iface_enumerate(AF_LOCAL, &newnow, make_duid1);        iface_enumerate(AF_LOCAL, &newnow, make_duid1);
               
Line 475  static int make_duid1(int index, unsigned int type, ch Line 609  static int make_duid1(int index, unsigned int type, ch
       
   unsigned char *p;    unsigned char *p;
   (void)index;    (void)index;
  (void)parm;
   time_t newnow = *((time_t *)parm);
   
   if (type >= 256)    if (type >= 256)
     return 1;      return 1;
   
#ifdef HAVE_BROKEN_RTC  if (newnow == 0)
  daemon->duid = p = safe_malloc(maclen + 4);    {
  daemon->duid_len = maclen + 4;      daemon->duid = p = safe_malloc(maclen + 4);
  PUTSHORT(3, p); /* DUID_LL */      daemon->duid_len = maclen + 4;
  PUTSHORT(type, p); /* address type */      PUTSHORT(3, p); /* DUID_LL */
#else      PUTSHORT(type, p); /* address type */
  daemon->duid = p = safe_malloc(maclen + 8);    }
  daemon->duid_len = maclen + 8;  else
  PUTSHORT(1, p); /* DUID_LLT */    {
  PUTSHORT(type, p); /* address type */      daemon->duid = p = safe_malloc(maclen + 8);
  PUTLONG(*((time_t *)parm), p); /* time */      daemon->duid_len = maclen + 8;
#endif      PUTSHORT(1, p); /* DUID_LLT */
      PUTSHORT(type, p); /* address type */
       PUTLONG(*((time_t *)parm), p); /* time */
     }
   
   memcpy(p, mac, maclen);    memcpy(p, mac, maclen);
   
   return 0;    return 0;
Line 509  static int construct_worker(struct in6_addr *local, in Line 648  static int construct_worker(struct in6_addr *local, in
   char ifrn_name[IFNAMSIZ];    char ifrn_name[IFNAMSIZ];
   struct in6_addr start6, end6;    struct in6_addr start6, end6;
   struct dhcp_context *template, *context;    struct dhcp_context *template, *context;
  struct iname *tmp;
   
   (void)scope;    (void)scope;
   (void)flags;    (void)flags;
   (void)valid;    (void)valid;
Line 522  static int construct_worker(struct in6_addr *local, in Line 662  static int construct_worker(struct in6_addr *local, in
       IN6_IS_ADDR_MULTICAST(local))        IN6_IS_ADDR_MULTICAST(local))
     return 1;      return 1;
   
  if (!indextoname(daemon->doing_dhcp6 ? daemon->dhcp6fd : daemon->icmp6fd, if_index, ifrn_name))  if (!(flags & IFACE_PERMANENT))
    return 0;    return 1;
 
   if (flags & IFACE_DEPRECATED)
     return 1;
 
   /* Ignore interfaces where we're not doing RA/DHCP6 */
   if (!indextoname(daemon->icmp6fd, if_index, ifrn_name) ||
       !iface_check(AF_LOCAL, NULL, ifrn_name, NULL))
     return 1;
       
     for (tmp = daemon->dhcp_except; tmp; tmp = tmp->next)
       if (tmp->name && wildcard_match(tmp->name, ifrn_name))
         return 1;
   
   for (template = daemon->dhcp6; template; template = template->next)    for (template = daemon->dhcp6; template; template = template->next)
    if (!(template->flags & CONTEXT_TEMPLATE))    if (!(template->flags & (CONTEXT_TEMPLATE | CONTEXT_CONSTRUCTED)))
       {        {
         /* non-template entries, just fill in interface and local addresses */          /* non-template entries, just fill in interface and local addresses */
        if (prefix == template->prefix &&        if (prefix <= template->prefix &&
            is_same_net6(local, &template->start6, prefix) &&            is_same_net6(local, &template->start6, template->prefix) &&
            is_same_net6(local, &template->end6, prefix))            is_same_net6(local, &template->end6, template->prefix))
           {            {
               /* First time found, do fast RA. */
               if (template->if_index == 0)
                 {
                   ra_start_unsolicited(param->now, template);
                   param->newone = 1;
                 }
               
             template->if_index = if_index;              template->if_index = if_index;
             template->local6 = *local;              template->local6 = *local;
           }            }
                   
       }        }
    else if (addr6part(local) == addr6part(&template->start6) && wildcard_match(template->template_interface, ifrn_name))    else if (wildcard_match(template->template_interface, ifrn_name) &&
              template->prefix >= prefix)
       {        {
         start6 = *local;          start6 = *local;
         setaddr6part(&start6, addr6part(&template->start6));          setaddr6part(&start6, addr6part(&template->start6));
Line 546  static int construct_worker(struct in6_addr *local, in Line 706  static int construct_worker(struct in6_addr *local, in
         setaddr6part(&end6, addr6part(&template->end6));          setaddr6part(&end6, addr6part(&template->end6));
                   
         for (context = daemon->dhcp6; context; context = context->next)          for (context = daemon->dhcp6; context; context = context->next)
          if ((context->flags & CONTEXT_CONSTRUCTED) &&          if (!(context->flags & CONTEXT_TEMPLATE) &&
               IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&                IN6_ARE_ADDR_EQUAL(&start6, &context->start6) &&
               IN6_ARE_ADDR_EQUAL(&end6, &context->end6))                IN6_ARE_ADDR_EQUAL(&end6, &context->end6))
             {              {
              context->flags &= ~CONTEXT_GC;              /* If there's an absolute address context covering this address
              break;                 then don't construct one as well. */
               if (!(context->flags & CONTEXT_CONSTRUCTED))
                 break;
               
               if (context->if_index == if_index)
                 {
                   int cflags = context->flags;
                   context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
                   if (cflags & CONTEXT_OLD)
                     {
                       /* address went, now it's back, and on the same interface */
                       log_context(AF_INET6, context); 
                       /* fast RAs for a while */
                       ra_start_unsolicited(param->now, context);
                       param->newone = 1; 
                       /* Add address to name again */
                       if (context->flags & CONTEXT_RA_NAME)
                         param->newname = 1;
                     
                     }
                   break;
                 }
             }              }
                   
         if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))          if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
Line 563  static int construct_worker(struct in6_addr *local, in Line 744  static int construct_worker(struct in6_addr *local, in
             context->flags |= CONTEXT_CONSTRUCTED;              context->flags |= CONTEXT_CONSTRUCTED;
             context->if_index = if_index;              context->if_index = if_index;
             context->local6 = *local;              context->local6 = *local;
               context->saved_valid = 0;
                           
             context->next = daemon->dhcp6;              context->next = daemon->dhcp6;
             daemon->dhcp6 = context;              daemon->dhcp6 = context;
   
            ra_start_unsolicted(param->now, context);            ra_start_unsolicited(param->now, context);
             /* we created a new one, need to call              /* we created a new one, need to call
                lease_update_file to get periodic functions called */                 lease_update_file to get periodic functions called */
             param->newone = 1;               param->newone = 1; 
Line 585  static int construct_worker(struct in6_addr *local, in Line 767  static int construct_worker(struct in6_addr *local, in
   
 void dhcp_construct_contexts(time_t now)  void dhcp_construct_contexts(time_t now)
 {   { 
  struct dhcp_context *tmp, *context, **up;  struct dhcp_context *context, *tmp, **up;
   struct cparam param;    struct cparam param;
   param.newone = 0;    param.newone = 0;
   param.newname = 0;    param.newname = 0;
   param.now = now;    param.now = now;
   
   for (context = daemon->dhcp6; context; context = context->next)    for (context = daemon->dhcp6; context; context = context->next)
    {    if (context->flags & CONTEXT_CONSTRUCTED)
      context->if_index = 0;      context->flags |= CONTEXT_GC;
      if (context->flags & CONTEXT_CONSTRUCTED)   
        context->flags |= CONTEXT_GC; 
    } 
  
   iface_enumerate(AF_INET6, &param, construct_worker);    iface_enumerate(AF_INET6, &param, construct_worker);
   
   for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)    for (up = &daemon->dhcp6, context = daemon->dhcp6; context; context = tmp)
     {      {
       tmp = context->next;  
               
      if (context->flags & CONTEXT_GC)      tmp = context->next; 
      
       if (context->flags & CONTEXT_GC && !(context->flags & CONTEXT_OLD))
         {          {
          *up = context->next;          if ((context->flags & CONTEXT_RA) || option_bool(OPT_RA))
          param.newone = 1; /* include deletion */             {
          if (context->flags & CONTEXT_RA_NAME)              /* previously constructed context has gone. advertise it's demise */
            param.newname = 1;               context->flags |= CONTEXT_OLD;
          free(context);              context->address_lost_time = now;
               /* Apply same ceiling of configured lease time as in radv.c */
               if (context->saved_valid > context->lease_time)
                 context->saved_valid = context->lease_time;
               /* maximum time is 2 hours, from RFC */
               if (context->saved_valid > 7200) /* 2 hours */
                 context->saved_valid = 7200;
               ra_start_unsolicited(now, context);
               param.newone = 1; /* include deletion */ 
               
               if (context->flags & CONTEXT_RA_NAME)
                 param.newname = 1; 
                               
               log_context(AF_INET6, context);
               
               up = &context->next;
             }
           else
             {
               /* we were never doing RA for this, so free now */
               *up = context->next;
               free(context);
             }
         }          }
       else        else
        up = &context->next;         up = &context->next;
     }      }
       
   if (param.newone)    if (param.newone)
Line 630  void dhcp_construct_contexts(time_t now) Line 832  void dhcp_construct_contexts(time_t now)
     }      }
 }  }
   
#endif#endif /* HAVE_DHCP6 */
 
 

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.5


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>