Diff for /embedaddon/dnsmasq/src/dhcp6.c between versions 1.1.1.3 and 1.1.1.4

version 1.1.1.3, 2016/11/02 09:57:01 version 1.1.1.4, 2021/03/17 00:56:46
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2016 Simon Kelley/* dnsmasq is Copyright (c) 2000-2021 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 51  void dhcp6_init(void) Line 51  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))
Line 135  void dhcp6_packet(time_t now) Line 135  void dhcp6_packet(time_t now)
   if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))    if (!indextoname(daemon->dhcp6fd, if_index, ifr.ifr_name))
     return;      return;
   
  if ((port = relay_reply6(&from, sz, ifr.ifr_name)) == 0)  if ((port = relay_reply6(&from, sz, ifr.ifr_name)) != 0)
     {      {
         from.sin6_port = htons(port);
         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;        struct dhcp_bridge *bridge, *alias;
   
       for (tmp = daemon->if_except; tmp; tmp = tmp->next)        for (tmp = daemon->if_except; tmp; tmp = tmp->next)
Line 233  void dhcp6_packet(time_t now) Line 240  void dhcp6_packet(time_t now)
       port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback,         port = dhcp6_reply(parm.current, if_index, ifr.ifr_name, &parm.fallback, 
                          &parm.ll_addr, &parm.ula_addr, sz, &from.sin6_addr, now);                           &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);
             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_file(now);
       lease_update_dns(0);        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);  
       while (retry_send(sendto(daemon->dhcp6fd, daemon->outpacket.iov_base,   
                                save_counter(0), 0, (struct sockaddr *)&from,   
                                sizeof(from))));  
     }  
 }  }
   
 void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)  void get_client_mac(struct in6_addr *client, int iface, unsigned char *mac, unsigned int *maclenp, unsigned int *mactypep, time_t now)
 {  {
  /* Recieving a packet from a host does not populate the neighbour  /* Receiving a packet from a host does not populate the neighbour
      cache, so we send a neighbour discovery request if we can't        cache, so we send a neighbour discovery request if we can't 
      find the sender. Repeat a few times in case of packet loss. */       find the sender. Repeat a few times in case of packet loss. */
       
Line 299  static int complete_context6(struct in6_addr *local,   Line 308  static int complete_context6(struct in6_addr *local,  
                              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 dhcp_relay *relay;
   struct iface_param *param = vparam;    struct iface_param *param = vparam;
   struct iname *tmp;    struct iname *tmp;
     
   (void)scope; /* warning */    (void)scope; /* warning */
       
  if (if_index == param->ind)  if (if_index != param->ind)
    {    return 1;
      if (IN6_IS_ADDR_LINKLOCAL(local))  
        param->ll_addr = *local;  if (IN6_IS_ADDR_LINKLOCAL(local))
      else if (IN6_IS_ADDR_ULA(local))    param->ll_addr = *local;
        param->ula_addr = *local;  else if (IN6_IS_ADDR_ULA(local))
    param->ula_addr = *local;
      if (!IN6_IS_ADDR_LOOPBACK(local) &&      
          !IN6_IS_ADDR_LINKLOCAL(local) &&  if (IN6_IS_ADDR_LOOPBACK(local) ||
          !IN6_IS_ADDR_MULTICAST(local))      IN6_IS_ADDR_LINKLOCAL(local) ||
        {      IN6_IS_ADDR_MULTICAST(local))
          /* if we have --listen-address config, see if the     return 1;
             arrival interface has a matching address. */  
          for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)  /* if we have --listen-address config, see if the 
            if (tmp->addr.sa.sa_family == AF_INET6 &&     arrival interface has a matching address. */
                IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))  for (tmp = daemon->if_addrs; tmp; tmp = tmp->next)
              param->addr_match = 1;    if (tmp->addr.sa.sa_family == AF_INET6 &&
                  IN6_ARE_ADDR_EQUAL(&tmp->addr.in6.sin6_addr, local))
          /* Determine a globally address on the arrival interface, even      param->addr_match = 1;
             if we have no matching dhcp-context, because we're only  
             allocating on remote subnets via relays. This  /* Determine a globally address on the arrival interface, even
             is used as a default for the DNS server option. */     if we have no matching dhcp-context, because we're only
          param->fallback = *local;     allocating on remote subnets via relays. This
               is used as a default for the DNS server option. */
          for (context = daemon->dhcp6; context; context = context->next)  param->fallback = *local;
            {  
              if ((context->flags & CONTEXT_DHCP) &&  for (context = daemon->dhcp6; context; context = context->next)
                  !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&    if ((context->flags & CONTEXT_DHCP) &&
                  prefix <= context->prefix &&        !(context->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
                  is_same_net6(local, &context->start6, context->prefix) &&        prefix <= context->prefix &&
                  is_same_net6(local, &context->end6, context->prefix))        context->current == context)
                {      {
                          if (is_same_net6(local, &context->start6, context->prefix) &&
                              is_same_net6(local, &context->end6, context->prefix))
                  /* link it onto the current chain if we've not seen it before */ 
                  if (context->current == context) 
                    { 
                      struct dhcp_context *tmp, **up; 
                       
                      /* use interface values only for contructed contexts */ 
                      if (!(context->flags & CONTEXT_CONSTRUCTED)) 
                        preferred = valid = 0xffffffff; 
                      else if (flags & IFACE_DEPRECATED) 
                        preferred = 0; 
                       
                      if (context->flags & CONTEXT_DEPRECATE) 
                        preferred = 0; 
                       
                      /* order chain, longest preferred time first */ 
                      for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current) 
                        if (tmp->preferred <= preferred) 
                          break; 
                        else 
                          up = &tmp->current; 
                       
                      context->current = *up; 
                      *up = context; 
                      context->local6 = *local; 
                      context->preferred = preferred; 
                      context->valid = valid; 
                    } 
                } 
            } 
        } 
 
      for (relay = daemon->relay6; relay; relay = relay->next) 
        if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr.addr6) && relay->current == relay && 
            (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local))) 
           {            {
            relay->current = param->relay;            struct dhcp_context *tmp, **up;
            param->relay = relay;            
            param->relay_local = *local;            /* use interface values only for constructed contexts */
             if (!(context->flags & CONTEXT_CONSTRUCTED))
               preferred = valid = 0xffffffff;
             else if (flags & IFACE_DEPRECATED)
               preferred = 0;
                     
             if (context->flags & CONTEXT_DEPRECATE)
               preferred = 0;
             
             /* order chain, longest preferred time first */
             for (up = &param->current, tmp = param->current; tmp; tmp = tmp->current)
               if (tmp->preferred <= preferred)
                 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)
 return 1;              {
                 /* 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;
                   }
               }
           }      
       }
 
   for (relay = daemon->relay6; relay; relay = relay->next)
     if (IN6_ARE_ADDR_EQUAL(local, &relay->local.addr6) && relay->current == relay &&
         (IN6_IS_ADDR_UNSPECIFIED(&param->relay_local) || IN6_ARE_ADDR_EQUAL(local, &param->relay_local)))
       {
         relay->current = param->relay;
         param->relay = relay;
         param->relay_local = *local;
       }
      
   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, int temp_addr,  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 431  struct dhcp_context *address6_allocate(struct dhcp_con Line 470  struct dhcp_context *address6_allocate(struct dhcp_con
       else        else
         {           { 
           if (!temp_addr && 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
             {              {
               u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);                u64 range = 1 + addr6part(&c->end6) - addr6part(&c->start6);
Line 453  struct dhcp_context *address6_allocate(struct dhcp_con Line 499  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 516  struct dhcp_context *address6_valid(struct dhcp_contex Line 561  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;  
 }  
   
 void make_duid(time_t now)  void make_duid(time_t now)
 {  {
   (void)now;    (void)now;
Line 617  static int construct_worker(struct in6_addr *local, in Line 641  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 636  static int construct_worker(struct in6_addr *local, in Line 661  static int construct_worker(struct in6_addr *local, in
   if (flags & IFACE_DEPRECATED)    if (flags & IFACE_DEPRECATED)
     return 1;      return 1;
   
  if (!indextoname(daemon->icmp6fd, if_index, ifrn_name))  /* Ignore interfaces where we're not doing RA/DHCP6 */
    return 0;  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, template->prefix) &&              is_same_net6(local, &template->start6, template->prefix) &&
             is_same_net6(local, &template->end6, template->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;
           }            }
Line 661  static int construct_worker(struct in6_addr *local, in Line 699  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))
             {              {
              int flags = context->flags;              /* If there's an absolute address context covering this address
              context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);                 then don't construct one as well. */
              if (flags & CONTEXT_OLD)              if (!(context->flags & CONTEXT_CONSTRUCTED))
                 break;
               
               if (context->if_index == if_index)
                 {                  {
                  /* address went, now it's back */                  int cflags = context->flags;
                  log_context(AF_INET6, context);                   context->flags &= ~(CONTEXT_GC | CONTEXT_OLD);
                  /* fast RAs for a while */                  if (cflags & CONTEXT_OLD)
                  ra_start_unsolicted(param->now, context);                    {
                  param->newone = 1;                       /* address went, now it's back, and on the same interface */
                  /* Add address to name again */                      log_context(AF_INET6, context); 
                  if (context->flags & CONTEXT_RA_NAME)                      /* fast RAs for a while */
                    param->newname = 1;                      ra_start_unsolicited(param->now, context);
                       param->newone = 1; 
                       /* Add address to name again */
                       if (context->flags & CONTEXT_RA_NAME)
                         param->newname = 1;
                     
                     }
                   break;
                 }                  }
               break;  
             }              }
                   
         if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))          if (!context && (context = whine_malloc(sizeof (struct dhcp_context))))
Line 695  static int construct_worker(struct in6_addr *local, in Line 742  static int construct_worker(struct in6_addr *local, in
             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 743  void dhcp_construct_contexts(time_t now) Line 790  void dhcp_construct_contexts(time_t now)
               /* maximum time is 2 hours, from RFC */                /* maximum time is 2 hours, from RFC */
               if (context->saved_valid > 7200) /* 2 hours */                if (context->saved_valid > 7200) /* 2 hours */
                 context->saved_valid = 7200;                  context->saved_valid = 7200;
              ra_start_unsolicted(now, context);              ra_start_unsolicited(now, context);
               param.newone = 1; /* include deletion */                 param.newone = 1; /* include deletion */ 
                               
               if (context->flags & CONTEXT_RA_NAME)                if (context->flags & CONTEXT_RA_NAME)

Removed from v.1.1.1.3  
changed lines
  Added in v.1.1.1.4


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