Diff for /embedaddon/dnsmasq/src/rfc3315.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 21 Line 21
   
 struct state {  struct state {
   unsigned char *clid;    unsigned char *clid;
  int clid_len, iaid, ia_type, interface, hostname_auth;  int clid_len, ia_type, interface, hostname_auth, lease_allocate;
   char *client_hostname, *hostname, *domain, *send_domain;    char *client_hostname, *hostname, *domain, *send_domain;
   struct dhcp_context *context;    struct dhcp_context *context;
  struct in6_addr *link_address;  struct in6_addr *link_address, *fallback, *ll_addr, *ula_addr;
  unsigned int xid, fqdn_flags;  unsigned int xid, fqdn_flags, iaid;
   char *iface_name;    char *iface_name;
   void *packet_options, *end;    void *packet_options, *end;
   struct dhcp_netid *tags, *context_tags;    struct dhcp_netid *tags, *context_tags;
#ifdef OPTION6_PREFIX_CLASS  unsigned char mac[DHCP_CHADDR_MAX];
  struct prefix_class *send_prefix_class;  unsigned int mac_len, mac_type;
#endif 
 };  };
   
static int dhcp6_maybe_relay(struct in6_addr *link_address, struct dhcp_netid **relay_tagsp, struct dhcp_context *context, static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz
                             int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now);                             struct in6_addr *client_addr, 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, static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *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_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);  static void log6_opts(int nest, unsigned int xid, void *start_opts, void *end_opts);
 static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);  static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string);
static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string);
 static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);  static void *opt6_find (void *opts, void *end, unsigned int search, unsigned int minsize);
 static void *opt6_next(void *opts, void *end);  static void *opt6_next(void *opts, void *end);
 static unsigned int opt6_uint(unsigned char *opt, int offset, int size);  static unsigned int opt6_uint(unsigned char *opt, int offset, int size);
Line 48  static void get_context_tag(struct state *state, struc Line 46  static void get_context_tag(struct state *state, struc
 static int check_ia(struct state *state, void *opt, void **endp, void **ia_option);  static int check_ia(struct state *state, void *opt, void **endp, void **ia_option);
 static int build_ia(struct state *state, int *t1cntr);  static int build_ia(struct state *state, int *t1cntr);
 static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz);  static void end_ia(int t1cntr, unsigned int min_time, int do_fuzz);
#ifdef OPTION6_PREFIX_CLASSstatic void mark_context_used(struct state *state, struct in6_addr *addr);
static struct prefix_class *prefix_class_from_context(struct dhcp_context *context); 
#endif 
static void mark_context_used(struct state *state, struct dhcp_context *context, struct in6_addr *addr); 
 static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr);  static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr);
 static int check_address(struct state *state, struct in6_addr *addr);  static int check_address(struct state *state, struct in6_addr *addr);
static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, unsigned int requested_timestatic int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now);
                        unsigned int *min_time, struct in6_addr *addr, int update_lease, time_t now);static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr);
 static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option
                         unsigned int *min_time, struct in6_addr *addr, time_t now);
 static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now);  static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now);
 static int add_local_addrs(struct dhcp_context *context);  static int add_local_addrs(struct dhcp_context *context);
struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, struct dhcp_context *context);static struct dhcp_netid *add_options(struct state *state, int do_refresh);
 static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep,   static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, 
                            unsigned int *preferred_timep, unsigned int lease_time, unsigned int requested_time);                            unsigned int *preferred_timep, unsigned int lease_time);
   
 #define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))  #define opt6_len(opt) ((int)(opt6_uint(opt, -2, 2)))
 #define opt6_type(opt) (opt6_uint(opt, -4, 2))  #define opt6_type(opt) (opt6_uint(opt, -4, 2))
 #define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))  #define opt6_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[4+(i)]))
   
   #define opt6_user_vendor_ptr(opt, i) ((void *)&(((unsigned char *)(opt))[2+(i)]))
   #define opt6_user_vendor_len(opt) ((int)(opt6_uint(opt, -4, 2)))
   #define opt6_user_vendor_next(opt, end) (opt6_next(((void *) opt) - 2, end))
    
   
 unsigned short dhcp6_reply(struct dhcp_context *context, int interface, char *iface_name,  unsigned short 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 in6_addr *fallback,  struct in6_addr *ll_addr, struct in6_addr *ula_addr,
                            size_t sz, struct in6_addr *client_addr, time_t now)
 {  {
   struct dhcp_netid *relay_tags = NULL;  
   struct dhcp_vendor *vendor;    struct dhcp_vendor *vendor;
   int msg_type;    int msg_type;
     struct state state;
       
   if (sz <= 4)    if (sz <= 4)
     return 0;      return 0;
Line 83  unsigned short dhcp6_reply(struct dhcp_context *contex Line 85  unsigned short dhcp6_reply(struct dhcp_context *contex
   for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)    for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
     vendor->netid.next = &vendor->netid;      vendor->netid.next = &vendor->netid;
       
  save_counter(0);  reset_counter();
    state.context = context;
  if (dhcp6_maybe_relay(NULL, &relay_tags, context, interface, iface_name, fallback, daemon->dhcp_packet.iov_base, sz, is_unicast, now))  state.interface = interface;
   state.iface_name = iface_name;
   state.fallback = fallback;
   state.ll_addr = ll_addr;
   state.ula_addr = ula_addr;
   state.mac_len = 0;
   state.tags = NULL;
   state.link_address = NULL;
 
   if (dhcp6_maybe_relay(&state, daemon->dhcp_packet.iov_base, sz, client_addr, 
                         IN6_IS_ADDR_MULTICAST(client_addr), now))
     return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;      return msg_type == DHCP6RELAYFORW ? DHCPV6_SERVER_PORT : DHCPV6_CLIENT_PORT;
   
   return 0;    return 0;
 }  }
   
 /* This cost me blood to write, it will probably cost you blood to understand - srk. */  /* 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,static int dhcp6_maybe_relay(struct state *state, unsigned char *inbuff, size_t sz
                             int interface, char *iface_name, struct in6_addr *fallback, void *inbuff, size_t sz, int is_unicast, time_t now)                             struct in6_addr *client_addr, int is_unicast, time_t now)
 {  {
   void *end = inbuff + sz;    void *end = inbuff + sz;
   void *opts = inbuff + 34;    void *opts = inbuff + 34;
  int msg_type = *((unsigned char *)inbuff);  int msg_type = *inbuff;
   unsigned char *outmsgtypep;    unsigned char *outmsgtypep;
   void *opt;    void *opt;
   struct dhcp_vendor *vendor;    struct dhcp_vendor *vendor;
   
  /* if not an encaplsulated relayed message, just do the stuff */  /* if not an encapsulated relayed message, just do the stuff */
   if (msg_type != DHCP6RELAYFORW)    if (msg_type != DHCP6RELAYFORW)
     {      {
       /* if link_address != NULL if points to the link address field of the         /* if link_address != NULL if points to the link address field of the 
          innermost nested RELAYFORW message, which is where we find the           innermost nested RELAYFORW message, which is where we find the
          address of the network on which we can allocate an address.           address of the network on which we can allocate an address.
         Recalculate the available contexts using that information. */         Recalculate the available contexts using that information. 
 
       link_address == NULL means there's no relay in use, so we try and find the client's 
       MAC address from the local ND cache. */
               
      if (link_address)      if (!state->link_address)
         get_client_mac(client_addr, state->interface, state->mac, &state->mac_len, &state->mac_type, now);
       else
         {          {
           struct dhcp_context *c;            struct dhcp_context *c;
          context = NULL;          struct shared_network *share = NULL;
                     state->context = NULL;
          if (!IN6_IS_ADDR_LOOPBACK(link_address) &&
              !IN6_IS_ADDR_LINKLOCAL(link_address) &&          if (!IN6_IS_ADDR_LOOPBACK(state->link_address) &&
              !IN6_IS_ADDR_MULTICAST(link_address))              !IN6_IS_ADDR_LINKLOCAL(state->link_address) &&
               !IN6_IS_ADDR_MULTICAST(state->link_address))
             for (c = daemon->dhcp6; c; c = c->next)              for (c = daemon->dhcp6; c; c = c->next)
              if ((c->flags & CONTEXT_DHCP) &&              {
                  !(c->flags & CONTEXT_TEMPLATE) &&                for (share = daemon->shared_networks; share; share = share->next)
                  is_same_net6(link_address, &c->start6, c->prefix) &&                  {
                  is_same_net6(link_address, &c->end6, c->prefix))                    if (share->shared_addr.s_addr != 0)
                {                      continue;
                  c->preferred = c->valid = 0xffffffff;                    
                  c->current = context;                    if (share->if_index != 0 ||
                  context = c;                        !IN6_ARE_ADDR_EQUAL(state->link_address, &share->match_addr6))
                }                      continue;
                     
                     if ((c->flags & CONTEXT_DHCP) &&
                         !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
                         is_same_net6(&share->shared_addr6, &c->start6, c->prefix) &&
                         is_same_net6(&share->shared_addr6, &c->end6, c->prefix))
                       break;
                   }
                 
                 if (share ||
                     ((c->flags & CONTEXT_DHCP) &&
                      !(c->flags & (CONTEXT_TEMPLATE | CONTEXT_OLD)) &&
                      is_same_net6(state->link_address, &c->start6, c->prefix) &&
                      is_same_net6(state->link_address, &c->end6, c->prefix)))
                   {
                     c->preferred = c->valid = 0xffffffff;
                     c->current = state->context;
                     state->context = c;
                   }
               }
                       
          if (!context)          if (!state->context)
             {              {
              inet_ntop(AF_INET6, link_address, daemon->addrbuff, ADDRSTRLEN);               inet_ntop(AF_INET6, state->link_address, daemon->addrbuff, ADDRSTRLEN); 
               my_syslog(MS_DHCP | LOG_WARNING,                 my_syslog(MS_DHCP | LOG_WARNING, 
                         _("no address range available for DHCPv6 request from relay at %s"),                          _("no address range available for DHCPv6 request from relay at %s"),
                         daemon->addrbuff);                          daemon->addrbuff);
               return 0;                return 0;
             }              }
         }          }
          
      if (!context)      if (!state->context)
         {          {
           my_syslog(MS_DHCP | LOG_WARNING,             my_syslog(MS_DHCP | LOG_WARNING, 
                    _("no address range available for DHCPv6 request via %s"), iface_name);                    _("no address range available for DHCPv6 request via %s"), state->iface_name);
           return 0;            return 0;
         }          }
   
      return dhcp6_no_relay(msg_type, link_address, *relay_tagsp, context, interface, iface_name, fallback, inbuff, sz, is_unicast, now);      return dhcp6_no_relay(state, msg_type, inbuff, sz, is_unicast, now);
     }      }
   
   /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option    /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
Line 155  static int dhcp6_maybe_relay(struct in6_addr *link_add Line 192  static int dhcp6_maybe_relay(struct in6_addr *link_add
     return 0;      return 0;
       
   /* copy header stuff into reply message and set type to reply */    /* copy header stuff into reply message and set type to reply */
  outmsgtypep = put_opt6(inbuff, 34);  if (!(outmsgtypep = put_opt6(inbuff, 34)))
     return 0;
   *outmsgtypep = DHCP6RELAYREPL;    *outmsgtypep = DHCP6RELAYREPL;
   
   /* look for relay options and set tags if found. */    /* look for relay options and set tags if found. */
Line 175  static int dhcp6_maybe_relay(struct in6_addr *link_add Line 213  static int dhcp6_maybe_relay(struct in6_addr *link_add
           memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 &&            memcmp(vendor->data, opt6_ptr(opt, 0), vendor->len) == 0 &&
           vendor->netid.next != &vendor->netid)            vendor->netid.next != &vendor->netid)
         {          {
          vendor->netid.next = *relay_tagsp;          vendor->netid.next = state->tags;
          *relay_tagsp = &vendor->netid;          state->tags = &vendor->netid;
           break;            break;
         }          }
     }      }
       
     /* RFC-6939 */
     if ((opt = opt6_find(opts, end, OPTION6_CLIENT_MAC, 3)))
       {
         if (opt6_len(opt) - 2 > DHCP_CHADDR_MAX) {
           return 0;
         }
         state->mac_type = opt6_uint(opt, 0, 2);
         state->mac_len = opt6_len(opt) - 2;
         memcpy(&state->mac[0], opt6_ptr(opt, 2), state->mac_len);
       }
     
   for (opt = opts; opt; opt = opt6_next(opt, end))    for (opt = opts; opt; opt = opt6_next(opt, end))
     {      {
      int o = new_opt6(opt6_type(opt));      if (opt6_ptr(opt, 0) + opt6_len(opt) > end) 
      if (opt6_type(opt) == OPTION6_RELAY_MSG)        return 0;
      
       /* Don't copy MAC address into reply. */
       if (opt6_type(opt) != OPTION6_CLIENT_MAC)
         {          {
          struct in6_addr link_address;          int o = new_opt6(opt6_type(opt));
          /* the packet data is unaligned, copy to aligned storage */          if (opt6_type(opt) == OPTION6_RELAY_MSG)
          memcpy(&link_address, inbuff + 2, IN6ADDRSZ);             {
          /* Not, zero is_unicast since that is now known to refer to the               struct in6_addr align;
             relayed packet, not the original sent by the client */              /* the packet data is unaligned, copy to aligned storage */
          if (!dhcp6_maybe_relay(&link_address, relay_tagsp, context, interface, iface_name, fallback, opt6_ptr(opt, 0), opt6_len(opt), 0, now))              memcpy(&align, inbuff + 2, IN6ADDRSZ); 
            return 0;              state->link_address = &align;
               /* 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(state, opt6_ptr(opt, 0), opt6_len(opt), client_addr, 0, now))
                 return 0;
             }
           else
             put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
           end_opt6(o);
         }          }
       else  
         put_opt6(opt6_ptr(opt, 0), opt6_len(opt));  
       end_opt6(o);            
     }      }
       
   return 1;    return 1;
 }  }
   
static int dhcp6_no_relay(int msg_type, struct in6_addr *link_address, struct dhcp_netid *tags, struct dhcp_context *context, static int dhcp6_no_relay(struct state *state, int msg_type, unsigned char *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 *opt;    void *opt;
  int i, o, o1, start_opts;  int i, o, o1, start_opts, start_msg;
   struct dhcp_opt *opt_cfg;    struct dhcp_opt *opt_cfg;
   struct dhcp_netid *tagif;    struct dhcp_netid *tagif;
   struct dhcp_config *config = NULL;    struct dhcp_config *config = NULL;
   struct dhcp_netid known_id, iface_id, v6_id;    struct dhcp_netid known_id, iface_id, v6_id;
  unsigned char *outmsgtypep;  unsigned char outmsgtype;
   struct dhcp_vendor *vendor;    struct dhcp_vendor *vendor;
   struct dhcp_context *context_tmp;    struct dhcp_context *context_tmp;
     struct dhcp_mac *mac_opt;
   unsigned int ignore = 0;    unsigned int ignore = 0;
   struct state state;  
 #ifdef OPTION6_PREFIX_CLASS  
   struct prefix_class *p;  
   int dump_all_prefix_classes = 0;  
 #endif  
   
  state.packet_options = inbuff + 4;  state->packet_options = inbuff + 4;
  state.end = inbuff + sz;  state->end = inbuff + sz;
  state.clid = NULL;  state->clid = NULL;
  state.clid_len = 0;  state->clid_len = 0;
  state.context_tags = NULL;  state->lease_allocate = 0;
  state.tags = tags;  state->context_tags = NULL;
  state.link_address = link_address;  state->domain = NULL;
  state.interface = interface;  state->send_domain = NULL;
  state.domain = NULL;  state->hostname_auth = 0;
  state.send_domain = NULL;  state->hostname = NULL;
  state.context = context;  state->client_hostname = NULL;
  state.hostname_auth = 0;  state->fqdn_flags = 0x01; /* default to send if we receive no FQDN option */
  state.hostname = NULL; 
  state.client_hostname = NULL; 
  state.iface_name = iface_name; 
  state.fqdn_flags = 0x01; /* default to send if we recieve no FQDN option */ 
#ifdef OPTION6_PREFIX_CLASS 
  state.send_prefix_class = NULL; 
#endif 
   
   /* set tag with name == interface */    /* set tag with name == interface */
  iface_id.net = iface_name;  iface_id.net = state->iface_name;
  iface_id.next = state.tags;  iface_id.next = state->tags;
  state.tags = &iface_id;   state->tags = &iface_id; 
   
   /* set tag "dhcpv6" */    /* set tag "dhcpv6" */
   v6_id.net = "dhcpv6";    v6_id.net = "dhcpv6";
  v6_id.next = state.tags;  v6_id.next = state->tags;
  state.tags = &v6_id;  state->tags = &v6_id;
   
  /* copy over transaction-id, and save pointer to message type */  start_msg = save_counter(-1);
  outmsgtypep = put_opt6(inbuff, 4);  /* copy over transaction-id */
   if (!put_opt6(inbuff, 4))
     return 0;
   start_opts = save_counter(-1);    start_opts = save_counter(-1);
  state.xid = outmsgtypep[3] | outmsgtypep[2] << 8 | outmsgtypep[1] << 16;  state->xid = inbuff[3] | inbuff[2] << 8 | inbuff[1] << 16;
       
   /* We're going to be linking tags from all context we use.     /* We're going to be linking tags from all context we use. 
      mark them as unused so we don't link one twice and break the list */       mark them as unused so we don't link one twice and break the list */
  for (context_tmp = context; context_tmp; context_tmp = context_tmp->current)  for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
     {      {
      context->netid.next = &context->netid;      context_tmp->netid.next = &context_tmp->netid;
   
       if (option_bool(OPT_LOG_OPTS))        if (option_bool(OPT_LOG_OPTS))
         {          {
Line 268  static int dhcp6_no_relay(int msg_type, struct in6_add Line 315  static int dhcp6_no_relay(int msg_type, struct in6_add
            inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN);              inet_ntop(AF_INET6, &context_tmp->end6, daemon->dhcp_buff2, ADDRSTRLEN); 
            if (context_tmp->flags & (CONTEXT_STATIC))             if (context_tmp->flags & (CONTEXT_STATIC))
              my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"),               my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCPv6 subnet: %s/%d"),
                       state.xid, daemon->dhcp_buff, context_tmp->prefix);                       state->xid, daemon->dhcp_buff, context_tmp->prefix);
            else             else
              my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"),                my_syslog(MS_DHCP | LOG_INFO, _("%u available DHCP range: %s -- %s"), 
                       state.xid, daemon->dhcp_buff, daemon->dhcp_buff2);                       state->xid, daemon->dhcp_buff, daemon->dhcp_buff2);
         }          }
     }      }
   
  if ((opt = opt6_find(state.packet_options, state.end, OPTION6_CLIENT_ID, 1)))  if ((opt = opt6_find(state->packet_options, state->end, OPTION6_CLIENT_ID, 1)))
     {      {
      state.clid = opt6_ptr(opt, 0);      state->clid = opt6_ptr(opt, 0);
      state.clid_len = opt6_len(opt);      state->clid_len = opt6_len(opt);
       o = new_opt6(OPTION6_CLIENT_ID);        o = new_opt6(OPTION6_CLIENT_ID);
      put_opt6(state.clid, state.clid_len);      put_opt6(state->clid, state->clid_len);
       end_opt6(o);        end_opt6(o);
     }      }
   else if (msg_type != DHCP6IREQ)    else if (msg_type != DHCP6IREQ)
     return 0;      return 0;
   
  /* server-id must match except for SOLICIT and CONFIRM messages */  /* server-id must match except for SOLICIT, CONFIRM and REBIND messages */
  if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ &&  if (msg_type != DHCP6SOLICIT && msg_type != DHCP6CONFIRM && msg_type != DHCP6IREQ && msg_type != DHCP6REBIND &&
      (!(opt = opt6_find(state.packet_options, state.end, OPTION6_SERVER_ID, 1)) ||      (!(opt = opt6_find(state->packet_options, state->end, OPTION6_SERVER_ID, 1)) ||
        opt6_len(opt) != daemon->duid_len ||         opt6_len(opt) != daemon->duid_len ||
        memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))         memcmp(opt6_ptr(opt, 0), daemon->duid, daemon->duid_len) != 0))
     return 0;      return 0;
Line 301  static int dhcp6_no_relay(int msg_type, struct in6_add Line 348  static int dhcp6_no_relay(int msg_type, struct in6_add
       (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))        (msg_type == DHCP6REQUEST || msg_type == DHCP6RENEW || msg_type == DHCP6RELEASE || msg_type == DHCP6DECLINE))
           
     {        {  
         outmsgtype = DHCP6REPLY;
       o1 = new_opt6(OPTION6_STATUS_CODE);        o1 = new_opt6(OPTION6_STATUS_CODE);
       put_opt6_short(DHCP6USEMULTI);        put_opt6_short(DHCP6USEMULTI);
       put_opt6_string("Use multicast");        put_opt6_string("Use multicast");
       end_opt6(o1);        end_opt6(o1);
      return 1;      goto done;
     }      }
   
   /* match vendor and user class options */    /* match vendor and user class options */
Line 320  static int dhcp6_no_relay(int msg_type, struct in6_add Line 368  static int dhcp6_no_relay(int msg_type, struct in6_add
       else        else
         continue;          continue;
   
      if ((opt = opt6_find(state.packet_options, state.end, mopt, 2)))      if ((opt = opt6_find(state->packet_options, state->end, mopt, 2)))
         {          {
           void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));            void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
           int offset = 0;            int offset = 0;
Line 336  static int dhcp6_no_relay(int msg_type, struct in6_add Line 384  static int dhcp6_no_relay(int msg_type, struct in6_add
               offset = 4;                offset = 4;
             }              }
     
          for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))          /* Note that format if user/vendor classes is different to DHCP options - no option types. */
            for (i = 0; i <= (opt6_len(enc_opt) - vendor->len); i++)          for (enc_opt = opt6_ptr(opt, offset); enc_opt; enc_opt = opt6_user_vendor_next(enc_opt, enc_end))
              if (memcmp(vendor->data, opt6_ptr(enc_opt, i), vendor->len) == 0)            for (i = 0; i <= (opt6_user_vendor_len(enc_opt) - vendor->len); i++)
               if (memcmp(vendor->data, opt6_user_vendor_ptr(enc_opt, i), vendor->len) == 0)
                 {                  {
                  vendor->netid.next = state.tags;                  vendor->netid.next = state->tags;
                  state.tags = &vendor->netid;                  state->tags = &vendor->netid;
                   break;                    break;
                 }                  }
         }          }
     }      }
   
  if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(state.packet_options, state.end, OPTION6_VENDOR_CLASS, 4)))  if (option_bool(OPT_LOG_OPTS) && (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
    my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), state.xid, opt6_uint(opt, 0, 4));    my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %u"), state->xid, opt6_uint(opt, 0, 4));
       
   /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.    /* dhcp-match. If we have hex-and-wildcards, look for a left-anchored match.
      Otherwise assume the option is an array, and look for a matching element.        Otherwise assume the option is an array, and look for a matching element. 
     If no data given, existance of the option is enough. This code handles      If no data given, existence of the option is enough. This code handles 
      V-I opts too. */       V-I opts too. */
   for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next)    for (opt_cfg = daemon->dhcp_match6; opt_cfg; opt_cfg = opt_cfg->next)
     {      {
Line 360  static int dhcp6_no_relay(int msg_type, struct in6_add Line 409  static int dhcp6_no_relay(int msg_type, struct in6_add
               
       if (opt_cfg->flags & DHOPT_RFC3925)        if (opt_cfg->flags & DHOPT_RFC3925)
         {          {
          for (opt = opt6_find(state.packet_options, state.end, OPTION6_VENDOR_OPTS, 4);          for (opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_OPTS, 4);
                opt;                 opt;
               opt = opt6_find(opt6_next(opt, state.end), state.end, OPTION6_VENDOR_OPTS, 4))               opt = opt6_find(opt6_next(opt, state->end), state->end, OPTION6_VENDOR_OPTS, 4))
             {              {
               void *vopt;                void *vopt;
               void *vend = opt6_ptr(opt, opt6_len(opt));                void *vend = opt6_ptr(opt, opt6_len(opt));
Line 378  static int dhcp6_no_relay(int msg_type, struct in6_add Line 427  static int dhcp6_no_relay(int msg_type, struct in6_add
         }          }
       else        else
         {          {
          if (!(opt = opt6_find(state.packet_options, state.end, opt_cfg->opt, 1)))          if (!(opt = opt6_find(state->packet_options, state->end, opt_cfg->opt, 1)))
             continue;              continue;
                       
           match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt));            match = match_bytes(opt_cfg, opt6_ptr(opt, 0), opt6_len(opt));
Line 386  static int dhcp6_no_relay(int msg_type, struct in6_add Line 435  static int dhcp6_no_relay(int msg_type, struct in6_add
       
       if (match)        if (match)
         {          {
          opt_cfg->netid->next = state.tags;          opt_cfg->netid->next = state->tags;
          state.tags = opt_cfg->netid;          state->tags = opt_cfg->netid;
         }          }
     }      }
   
     if (state->mac_len != 0)
       {
         if (option_bool(OPT_LOG_OPTS))
           {
             print_mac(daemon->dhcp_buff, state->mac, state->mac_len);
             my_syslog(MS_DHCP | LOG_INFO, _("%u client MAC address: %s"), state->xid, daemon->dhcp_buff);
           }
   
         for (mac_opt = daemon->dhcp_macs; mac_opt; mac_opt = mac_opt->next)
           if ((unsigned)mac_opt->hwaddr_len == state->mac_len &&
               ((unsigned)mac_opt->hwaddr_type == state->mac_type || mac_opt->hwaddr_type == 0) &&
               memcmp_masked(mac_opt->hwaddr, state->mac, state->mac_len, mac_opt->mask))
             {
               mac_opt->netid.next = state->tags;
               state->tags = &mac_opt->netid;
             }
       }
       
  if ((opt = opt6_find(state.packet_options, state.end, OPTION6_FQDN, 1)))  if ((opt = opt6_find(state->packet_options, state->end, OPTION6_FQDN, 1)))
     {      {
       /* RFC4704 refers */        /* RFC4704 refers */
        int len = opt6_len(opt) - 1;         int len = opt6_len(opt) - 1;
                 
       state.fqdn_flags = opt6_uint(opt, 0, 1);       state->fqdn_flags = opt6_uint(opt, 0, 1);
                 
        /* Always force update, since the client has no way to do it itself. */         /* Always force update, since the client has no way to do it itself. */
       if (!option_bool(OPT_FQDN_UPDATE) && !(state.fqdn_flags & 0x01))       if (!option_bool(OPT_FQDN_UPDATE) && !(state->fqdn_flags & 0x01))
         state.fqdn_flags |= 0x03;         state->fqdn_flags |= 0x03;
     
       state.fqdn_flags &= ~0x04;       state->fqdn_flags &= ~0x04;
   
        if (len != 0 && len < 255)         if (len != 0 && len < 255)
          {           {
Line 424  static int dhcp6_no_relay(int msg_type, struct in6_add Line 491  static int dhcp6_no_relay(int msg_type, struct in6_add
                         
            if (legal_hostname(daemon->dhcp_buff))             if (legal_hostname(daemon->dhcp_buff))
              {               {
               state.client_hostname = daemon->dhcp_buff;               struct dhcp_match_name *m;
                size_t nl = strlen(daemon->dhcp_buff);
                
                state->client_hostname = daemon->dhcp_buff;
                
                if (option_bool(OPT_LOG_OPTS))                 if (option_bool(OPT_LOG_OPTS))
                 my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), state.xid, state.client_hostname);                  my_syslog(MS_DHCP | LOG_INFO, _("%u client provides name: %s"), state->xid, state->client_hostname);
                
                for (m = daemon->dhcp_name_match; m; m = m->next)
                  {
                    size_t ml = strlen(m->name);
                    char save = 0;
                    
                    if (nl < ml)
                      continue;
                    if (nl > ml)
                      {
                        save = state->client_hostname[ml];
                        state->client_hostname[ml] = 0;
                      }
                    
                    if (hostname_isequal(state->client_hostname, m->name) &&
                        (save == 0 || m->wildcard))
                      {
                        m->netid->next = state->tags;
                        state->tags = m->netid;
                      }
                    
                    if (save != 0)
                      state->client_hostname[ml] = save;
                  }
              }               }
          }           }
     }          }    
       
  if (state.clid)  if (state->clid &&
       (config = find_config(daemon->dhcp_conf, state->context, state->clid, state->clid_len,
                             state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags))) &&
       have_config(config, CONFIG_NAME))
     {      {
      config = find_config6(daemon->dhcp_conf, context, state.clid, state.clid_len, NULL);      state->hostname = config->hostname;
       state->domain = config->domain;
       state->hostname_auth = 1;
     }
   else if (state->client_hostname)
     {
       state->domain = strip_hostname(state->client_hostname);
               
      if (have_config(config, CONFIG_NAME))      if (strlen(state->client_hostname) != 0)
         {          {
          state.hostname = config->hostname;          state->hostname = state->client_hostname;
          state.domain = config->domain; 
          state.hostname_auth = 1; 
        } 
      else if (state.client_hostname) 
        { 
          state.domain = strip_hostname(state.client_hostname); 
                       
          if (strlen(state.client_hostname) != 0)          if (!config)
             {              {
              state.hostname = state.client_hostname;              /* Search again now we have a hostname. 
              if (!config)                 Only accept configs without CLID here, (it won't match)
                {                 to avoid impersonation by name. */
                  /* Search again now we have a hostname.               struct dhcp_config *new = find_config(daemon->dhcp_conf, state->context, NULL, 0, NULL, 0, 0, state->hostname, run_tag_if(state->tags));
                     Only accept configs without CLID here, (it won't match)              if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr)
                     to avoid impersonation by name. */                config = new;
                  struct dhcp_config *new = find_config6(daemon->dhcp_conf, context, NULL, 0, state.hostname); 
                  if (new && !have_config(new, CONFIG_CLID) && !new->hwaddr) 
                    config = new; 
                } 
             }              }
         }          }
     }      }
Line 467  static int dhcp6_no_relay(int msg_type, struct in6_add Line 561  static int dhcp6_no_relay(int msg_type, struct in6_add
               
       for (list = config->netid; list; list = list->next)        for (list = config->netid; list; list = list->next)
         {          {
          list->list->next = state.tags;          list->list->next = state->tags;
          state.tags = list->list;          state->tags = list->list;
         }          }
   
       /* set "known" tag for known hosts */        /* set "known" tag for known hosts */
       known_id.net = "known";        known_id.net = "known";
      known_id.next = state.tags;      known_id.next = state->tags;
      state.tags = &known_id;      state->tags = &known_id;
   
       if (have_config(config, CONFIG_DISABLE))        if (have_config(config, CONFIG_DISABLE))
         ignore = 1;          ignore = 1;
     }      }
  else if (state->clid &&
#ifdef OPTION6_PREFIX_CLASS           find_config(daemon->dhcp_conf, NULL, state->clid, state->clid_len,
  /* OPTION_PREFIX_CLASS in ORO, send addresses in all prefix classes */                       state->mac, state->mac_len, state->mac_type, NULL, run_tag_if(state->tags)))
  if (daemon->prefix_classes && (msg_type == DHCP6SOLICIT || msg_type == DHCP6REQUEST)) 
     {      {
      void *oro;      known_id.net = "known-othernet";
            known_id.next = state->tags;
      if ((oro = opt6_find(state.packet_options, state.end, OPTION6_ORO, 0)))      state->tags = &known_id;
        for (i = 0; i <  opt6_len(oro) - 1; i += 2)    }
          if (opt6_uint(oro, i, 2) == OPTION6_PREFIX_CLASS) 
            { 
              dump_all_prefix_classes = 1; 
              break; 
            } 
       
      if (msg_type != DHCP6SOLICIT || dump_all_prefix_classes) 
        /* Add the tags associated with prefix classes so we can use the DHCP ranges. 
           Not done for SOLICIT as we add them  one-at-time. */ 
        for (p = daemon->prefix_classes; p ; p = p->next) 
          { 
            p->tag.next = state.tags; 
            state.tags = &p->tag; 
          } 
    }     
#endif 
 
  tagif = run_tag_if(state.tags); 
       
     tagif = run_tag_if(state->tags);
     
   /* if all the netids in the ignore list are present, ignore this client */    /* if all the netids in the ignore list are present, ignore this client */
   if (daemon->dhcp_ignore)    if (daemon->dhcp_ignore)
     {      {
Line 518  static int dhcp6_no_relay(int msg_type, struct in6_add Line 595  static int dhcp6_no_relay(int msg_type, struct in6_add
     }      }
       
   /* if all the netids in the ignore_name list are present, ignore client-supplied name */    /* if all the netids in the ignore_name list are present, ignore client-supplied name */
  if (!state.hostname_auth)  if (!state->hostname_auth)
     {      {
        struct dhcp_netid_list *id_list;         struct dhcp_netid_list *id_list;
                 
Line 526  static int dhcp6_no_relay(int msg_type, struct in6_add Line 603  static int dhcp6_no_relay(int msg_type, struct in6_add
          if ((!id_list->list) || match_netid(id_list->list, tagif, 0))           if ((!id_list->list) || match_netid(id_list->list, tagif, 0))
            break;             break;
        if (id_list)         if (id_list)
         state.hostname = NULL;         state->hostname = NULL;
     }      }
       
   
Line 534  static int dhcp6_no_relay(int msg_type, struct in6_add Line 611  static int dhcp6_no_relay(int msg_type, struct in6_add
     {      {
     default:      default:
       return 0;        return 0;
      
      
     case DHCP6SOLICIT:      case DHCP6SOLICIT:
       {        {
         void *rapid_commit = opt6_find(state.packet_options, state.end, OPTION6_RAPID_COMMIT, 0);  
         int address_assigned = 0;          int address_assigned = 0;
         /* tags without all prefix-class tags */          /* tags without all prefix-class tags */
        struct dhcp_netid *solicit_tags = tagif;        struct dhcp_netid *solicit_tags;
         struct dhcp_context *c;          struct dhcp_context *c;
        
        if (rapid_commit)        outmsgtype = DHCP6ADVERTISE;
         
         if (opt6_find(state->packet_options, state->end, OPTION6_RAPID_COMMIT, 0))
           {            {
               outmsgtype = DHCP6REPLY;
               state->lease_allocate = 1;
             o = new_opt6(OPTION6_RAPID_COMMIT);              o = new_opt6(OPTION6_RAPID_COMMIT);
             end_opt6(o);              end_opt6(o);
           }            }
           
           log6_quiet(state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL);
   
        /* set reply message type */      request_no_address:
        *outmsgtypep = rapid_commit ? DHCP6REPLY : DHCP6ADVERTISE;        solicit_tags = tagif;
 
        log6_packet(&state, "DHCPSOLICIT", NULL, ignore ? _("ignored") : NULL); 
                   
         if (ignore)          if (ignore)
           return 0;            return 0;
Line 562  static int dhcp6_no_relay(int msg_type, struct in6_add Line 642  static int dhcp6_no_relay(int msg_type, struct in6_add
         lease6_reset();          lease6_reset();
   
         /* Can use configured address max once per prefix */          /* Can use configured address max once per prefix */
        for (c = context; c; c = c->current)        for (c = state->context; c; c = c->current)
           c->flags &= ~CONTEXT_CONF_USED;            c->flags &= ~CONTEXT_CONF_USED;
   
        for (opt = state.packet_options; opt; opt = opt6_next(opt, state.end))        for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
           {               {   
             void *ia_option, *ia_end;              void *ia_option, *ia_end;
             unsigned int min_time = 0xffffffff;              unsigned int min_time = 0xffffffff;
Line 574  static int dhcp6_no_relay(int msg_type, struct in6_add Line 654  static int dhcp6_no_relay(int msg_type, struct in6_add
             /* set unless we're sending a particular prefix-class, when we              /* set unless we're sending a particular prefix-class, when we
                want only dhcp-ranges with the correct tags set and not those without any tags. */                 want only dhcp-ranges with the correct tags set and not those without any tags. */
             int plain_range = 1;              int plain_range = 1;
            u32 lease_time, requested_time;            u32 lease_time;
             struct dhcp_lease *ltmp;              struct dhcp_lease *ltmp;
            struct in6_addr *req_addr;            struct in6_addr req_addr, addr;
            struct in6_addr addr;            
            if (!check_ia(state, opt, &ia_end, &ia_option))
            if (!check_ia(&state, opt, &ia_end, &ia_option)) 
               continue;                continue;
                           
             /* reset USED bits in contexts - one address per prefix per IAID */              /* reset USED bits in contexts - one address per prefix per IAID */
            for (c = context; c; c = c->current)            for (c = state->context; c; c = c->current)
               c->flags &= ~CONTEXT_USED;                c->flags &= ~CONTEXT_USED;
   
#ifdef OPTION6_PREFIX_CLASS            o = build_ia(state, &t1cntr);
            if (daemon->prefix_classes && state.ia_type == OPTION6_IA_NA)            if (address_assigned)
              {                address_assigned = 2;
                void *prefix_opt; 
                int prefix_class; 
                 
                if (dump_all_prefix_classes) 
                  /* OPTION_PREFIX_CLASS in ORO, send addresses in all prefix classes */ 
                  plain_range = 0; 
                else  
                  {  
                    if ((prefix_opt = opt6_find(opt6_ptr(opt, 12), ia_end, OPTION6_PREFIX_CLASS, 2))) 
                      { 
                         
                        prefix_class = opt6_uint(prefix_opt, 0, 2); 
                         
                        for (p = daemon->prefix_classes; p ; p = p->next) 
                          if (p->class == prefix_class) 
                            break; 
                         
                        if (!p) 
                          my_syslog(MS_DHCP | LOG_WARNING, _("unknown prefix-class %d"), prefix_class); 
                        else 
                          { 
                            /* add tag to list, and exclude undecorated dhcp-ranges */ 
                            p->tag.next = state.tags; 
                            solicit_tags = run_tag_if(&p->tag); 
                            plain_range = 0; 
                            state.send_prefix_class = p; 
                          } 
                      } 
                    else 
                      { 
                        /* client didn't ask for a prefix class, lets see if we can find one. */ 
                        for (p = daemon->prefix_classes; p ; p = p->next) 
                          { 
                            p->tag.next = NULL; 
                            if (match_netid(&p->tag, solicit_tags, 1)) 
                              break; 
                          } 
                         
                        if (p) 
                          { 
                            plain_range = 0; 
                            state.send_prefix_class = p; 
                          } 
                      } 
   
                     if (p && option_bool(OPT_LOG_OPTS))  
                       my_syslog(MS_DHCP | LOG_INFO, "%u prefix class %d tag:%s", state.xid, p->class, p->tag.net);   
                   }  
               }  
 #endif  
   
             o = build_ia(&state, &t1cntr);  
   
             for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))              for (ia_counter = 0; ia_option; ia_counter++, ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
               {                {
                req_addr = opt6_ptr(ia_option, 0);                /* worry about alignment here. */
                requested_time = opt6_uint(ia_option, 16, 4);                memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
                                                
                if ((c = address6_valid(context, req_addr, solicit_tags, plain_range)))                if ((c = address6_valid(state->context, &req_addr, solicit_tags, plain_range)))
                   {                    {
                     lease_time = c->lease_time;                      lease_time = c->lease_time;
                     /* If the client asks for an address on the same network as a configured address,                       /* If the client asks for an address on the same network as a configured address, 
                        offer the configured address instead, to make moving to newly-configured                         offer the configured address instead, to make moving to newly-configured
                        addresses automatic. */                         addresses automatic. */
                    if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr) && check_address(&state, &addr))                    if (!(c->flags & CONTEXT_CONF_USED) && config_valid(config, c, &addr, state, now))
                       {                        {
                        req_addr = &addr;                        req_addr = addr;
                         mark_config_used(c, &addr);                          mark_config_used(c, &addr);
                         if (have_config(config, CONFIG_TIME))                          if (have_config(config, CONFIG_TIME))
                           lease_time = config->lease_time;                            lease_time = config->lease_time;
                       }                        }
                    else if (!(c = address6_available(context, req_addr, solicit_tags, plain_range)))                    else if (!(c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
                       continue; /* not an address we're allowed */                        continue; /* not an address we're allowed */
                    else if (!check_address(&state, req_addr))                    else if (!check_address(state, &req_addr))
                       continue; /* address leased elsewhere */                        continue; /* address leased elsewhere */
                                           
                     /* add address to output packet */                      /* add address to output packet */
#ifdef OPTION6_PREFIX_CLASS                    add_address(state, c, lease_time, ia_option, &min_time, &req_addr, now);
                    if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)                    mark_context_used(state, &req_addr);
                      state.send_prefix_class = prefix_class_from_context(c);                    get_context_tag(state, c);
#endif                     
                    add_address(&state, c, lease_time, requested_time, &min_time, req_addr, rapid_commit != NULL, now); 
                    mark_context_used(&state, context, req_addr); 
                    get_context_tag(&state, c); 
                     address_assigned = 1;                      address_assigned = 1;
                   }                    }
               }                }
                           
             /* Suggest configured address(es) */              /* Suggest configured address(es) */
            for (c = context; c; c = c->current)             for (c = state->context; c; c = c->current) 
               if (!(c->flags & CONTEXT_CONF_USED) &&                if (!(c->flags & CONTEXT_CONF_USED) &&
                   match_netid(c->filter, solicit_tags, plain_range) &&                    match_netid(c->filter, solicit_tags, plain_range) &&
                  config_valid(config, c, &addr) &&                   config_valid(config, c, &addr, state, now))
                  check_address(&state, &addr)) 
                 {                  {
                  mark_config_used(context, &addr);                  mark_config_used(state->context, &addr);
                   if (have_config(config, CONFIG_TIME))                    if (have_config(config, CONFIG_TIME))
                     lease_time = config->lease_time;                      lease_time = config->lease_time;
                   else                    else
                     lease_time = c->lease_time;                      lease_time = c->lease_time;
   
                   /* add address to output packet */                    /* add address to output packet */
#ifdef OPTION6_PREFIX_CLASS                  add_address(state, c, lease_time, NULL, &min_time, &addr, now);
                  if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)                  mark_context_used(state, &addr);
                    state.send_prefix_class = prefix_class_from_context(c);                  get_context_tag(state, c);
#endif 
                  add_address(&state, c, lease_time, lease_time, &min_time, &addr, rapid_commit != NULL, now); 
                  mark_context_used(&state, context, &addr); 
                  get_context_tag(&state, c); 
                   address_assigned = 1;                    address_assigned = 1;
                 }                  }
                           
             /* return addresses for existing leases */              /* return addresses for existing leases */
             ltmp = NULL;              ltmp = NULL;
            while ((ltmp = lease6_find_by_client(ltmp, state.ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state.clid, state.clid_len, state.iaid)))            while ((ltmp = lease6_find_by_client(ltmp, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, state->clid, state->clid_len, state->iaid)))
               {                {
                req_addr = (struct in6_addr *)ltmp->hwaddr;                req_addr = ltmp->addr6;
                if ((c = address6_available(context, req_addr, solicit_tags, plain_range)))                if ((c = address6_available(state->context, &req_addr, solicit_tags, plain_range)))
                   {                    {
#ifdef OPTION6_PREFIX_CLASS                    add_address(state, c, c->lease_time, NULL, &min_time, &req_addr, now);
                    if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)                    mark_context_used(state, &req_addr);
                      state.send_prefix_class = prefix_class_from_context(c);                    get_context_tag(state, c);
#endif 
                    add_address(&state, c, c->lease_time, c->lease_time, &min_time, req_addr, rapid_commit != NULL, now); 
                    mark_context_used(&state, context, req_addr); 
                    get_context_tag(&state, c); 
                     address_assigned = 1;                      address_assigned = 1;
                   }                    }
               }                }
                                                         
             /* Return addresses for all valid contexts which don't yet have one */              /* Return addresses for all valid contexts which don't yet have one */
            while ((c = address6_allocate(context, state.clid, state.clid_len, state.iaid, ia_counter, solicit_tags, plain_range, &addr)))            while ((c = address6_allocate(state->context, state->clid, state->clid_len, state->ia_type == OPTION6_IA_TA,
                                           state->iaid, ia_counter, solicit_tags, plain_range, &addr)))
               {                {
#ifdef OPTION6_PREFIX_CLASS                add_address(state, c, c->lease_time, NULL, &min_time, &addr, now);
                if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)                mark_context_used(state, &addr);
                  state.send_prefix_class = prefix_class_from_context(c);                get_context_tag(state, c);
#endif 
                add_address(&state, c, c->lease_time, c->lease_time, &min_time, &addr, rapid_commit != NULL, now); 
                mark_context_used(&state, context, &addr); 
                get_context_tag(&state, c); 
                 address_assigned = 1;                  address_assigned = 1;
               }                }
                           
               if (address_assigned != 1)
                 {
                   /* If the server will not assign any addresses to any IAs in a
                      subsequent Request from the client, the server MUST send an Advertise
                      message to the client that doesn't include any IA options. */
                   if (!state->lease_allocate)
                     {
                       save_counter(o);
                       continue;
                     }
                   
                   /* If the server cannot assign any addresses to an IA in the message
                      from the client, the server MUST include the IA in the Reply message
                      with no addresses in the IA and a Status Code option in the IA
                      containing status code NoAddrsAvail. */
                   o1 = new_opt6(OPTION6_STATUS_CODE);
                   put_opt6_short(DHCP6NOADDRS);
                   put_opt6_string(_("address unavailable"));
                   end_opt6(o1);
                 }
               
             end_ia(t1cntr, min_time, 0);              end_ia(t1cntr, min_time, 0);
             end_opt6(o);                      end_opt6(o);        
           }            }
Line 747  static int dhcp6_no_relay(int msg_type, struct in6_add Line 780  static int dhcp6_no_relay(int msg_type, struct in6_add
             o = new_opt6(OPTION6_PREFERENCE);              o = new_opt6(OPTION6_PREFERENCE);
             put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);              put_opt6_char(option_bool(OPT_AUTHORITATIVE) ? 255 : 0);
             end_opt6(o);              end_opt6(o);
            tagif = add_options(&state, fallback, context);            tagif = add_options(state, 0);
           }            }
         else          else
           {             { 
Line 756  static int dhcp6_no_relay(int msg_type, struct in6_add Line 789  static int dhcp6_no_relay(int msg_type, struct in6_add
             put_opt6_short(DHCP6NOADDRS);              put_opt6_short(DHCP6NOADDRS);
             put_opt6_string(_("no addresses available"));              put_opt6_string(_("no addresses available"));
             end_opt6(o1);              end_opt6(o1);
            log6_packet(&state, "DHCPADVERTISE", NULL, _("no addresses available"));
             /* Some clients will ask repeatedly when we're not giving
                out addresses because we're in stateless mode. Avoid spamming
                the log in that case. */
             for (c = state->context; c; c = c->current)
               if (!(c->flags & CONTEXT_RA_STATELESS))
                 {
                   log6_packet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", NULL, _("no addresses available"));
                   break;
                 }
           }            }
   
         break;          break;
Line 765  static int dhcp6_no_relay(int msg_type, struct in6_add Line 807  static int dhcp6_no_relay(int msg_type, struct in6_add
     case DHCP6REQUEST:      case DHCP6REQUEST:
       {        {
         int address_assigned = 0;          int address_assigned = 0;
                int start = save_counter(-1);
 
         /* set reply message type */          /* set reply message type */
        *outmsgtypep = DHCP6REPLY;        outmsgtype = DHCP6REPLY;
         state->lease_allocate = 1;
   
        log6_packet(&state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL);        log6_quiet(state, "DHCPREQUEST", NULL, ignore ? _("ignored") : NULL);
                   
         if (ignore)          if (ignore)
           return 0;            return 0;
                   
        for (opt = state.packet_options; opt; opt = opt6_next(opt, state.end))        for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
           {               {   
             void *ia_option, *ia_end;              void *ia_option, *ia_end;
             unsigned int min_time = 0xffffffff;              unsigned int min_time = 0xffffffff;
             int t1cntr;              int t1cntr;
                           
             if (!check_ia(&state, opt, &ia_end, &ia_option))             if (!check_ia(state, opt, &ia_end, &ia_option))
                continue;                 continue;
            
            o = build_ia(&state, &t1cntr);             if (!ia_option)
                {
                  /* If we get a request with an IA_*A without addresses, treat it exactly like
                     a SOLICT with rapid commit set. */
                  save_counter(start);
                  goto request_no_address; 
                }
 
             o = build_ia(state, &t1cntr);
                               
             for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))              for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
               {                {
                struct in6_addr *req_addr = opt6_ptr(ia_option, 0);                struct in6_addr req_addr;
                u32 requested_time = opt6_uint(ia_option, 16, 4); 
                 struct dhcp_context *dynamic, *c;                  struct dhcp_context *dynamic, *c;
                 unsigned int lease_time;                  unsigned int lease_time;
                 struct in6_addr addr;  
                 int config_ok = 0;                  int config_ok = 0;
   
                   /* align. */
                   memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
                                   
                if ((c = address6_valid(context, req_addr, tagif, 1)))                if ((c = address6_valid(state->context, &req_addr, tagif, 1)))
                  config_ok = config_valid(config, c, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr);                  config_ok = (config_implies(config, c, &req_addr) != NULL);
                                   
                if ((dynamic = address6_available(context, req_addr, tagif, 1)) || c)                if ((dynamic = address6_available(state->context, &req_addr, tagif, 1)) || c)
                   {                    {
                     if (!dynamic && !config_ok)                      if (!dynamic && !config_ok)
                       {                        {
                         /* Static range, not configured. */                          /* Static range, not configured. */
                         o1 = new_opt6(OPTION6_STATUS_CODE);                          o1 = new_opt6(OPTION6_STATUS_CODE);
                        put_opt6_short(DHCP6UNSPEC);                        put_opt6_short(DHCP6NOADDRS);
                         put_opt6_string(_("address unavailable"));                          put_opt6_string(_("address unavailable"));
                         end_opt6(o1);                          end_opt6(o1);
                       }                        }
                    else if (!check_address(&state, req_addr))                    else if (!check_address(state, &req_addr))
                       {                        {
                         /* Address leased to another DUID/IAID */                          /* Address leased to another DUID/IAID */
                         o1 = new_opt6(OPTION6_STATUS_CODE);                          o1 = new_opt6(OPTION6_STATUS_CODE);
Line 825  static int dhcp6_no_relay(int msg_type, struct in6_add Line 878  static int dhcp6_no_relay(int msg_type, struct in6_add
                         if (config_ok && have_config(config, CONFIG_TIME))                          if (config_ok && have_config(config, CONFIG_TIME))
                           lease_time = config->lease_time;                            lease_time = config->lease_time;
   
#ifdef OPTION6_PREFIX_CLASS                        add_address(state, dynamic, lease_time, ia_option, &min_time, &req_addr, now);
                        if (dump_all_prefix_classes && state.ia_type == OPTION6_IA_NA)                        get_context_tag(state, dynamic);
                          state.send_prefix_class = prefix_class_from_context(c); 
#endif 
                        add_address(&state, dynamic, lease_time, requested_time, &min_time, req_addr, 1, now); 
                        get_context_tag(&state, dynamic); 
                         address_assigned = 1;                          address_assigned = 1;
                       }                        }
                   }                    }
Line 862  static int dhcp6_no_relay(int msg_type, struct in6_add Line 911  static int dhcp6_no_relay(int msg_type, struct in6_add
             put_opt6_short(DHCP6NOADDRS);              put_opt6_short(DHCP6NOADDRS);
             put_opt6_string(_("no addresses available"));              put_opt6_string(_("no addresses available"));
             end_opt6(o1);              end_opt6(o1);
            log6_packet(&state, "DHCPREPLY", NULL, _("no addresses available"));            log6_packet(state, "DHCPREPLY", NULL, _("no addresses available"));
           }            }
   
        tagif = add_options(&state, fallback, context);        tagif = add_options(state, 0);
         break;          break;
       }        }
               
       
     case DHCP6RENEW:      case DHCP6RENEW:
       case DHCP6REBIND:
       {        {
           int address_assigned = 0;
   
         /* set reply message type */          /* set reply message type */
        *outmsgtypep = DHCP6REPLY;        outmsgtype = DHCP6REPLY;
                   
        log6_packet(&state, "DHCPRENEW", NULL, NULL);        log6_quiet(state, msg_type == DHCP6RENEW ? "DHCPRENEW" : "DHCPREBIND", NULL, NULL);
   
        for (opt = state.packet_options; opt; opt = opt6_next(opt, state.end))        for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
           {            {
             void *ia_option, *ia_end;              void *ia_option, *ia_end;
             unsigned int min_time = 0xffffffff;              unsigned int min_time = 0xffffffff;
             int t1cntr, iacntr;              int t1cntr, iacntr;
                           
            if (!check_ia(&state, opt, &ia_end, &ia_option))            if (!check_ia(state, opt, &ia_end, &ia_option))
               continue;                continue;
                           
            o = build_ia(&state, &t1cntr);            o = build_ia(state, &t1cntr);
             iacntr = save_counter(-1);               iacntr = save_counter(-1); 
                           
             for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))              for (; ia_option; ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
               {                {
                 struct dhcp_lease *lease = NULL;                  struct dhcp_lease *lease = NULL;
                struct in6_addr *req_addr = opt6_ptr(ia_option, 0);                struct in6_addr req_addr;
                u32 requested_time = opt6_uint(ia_option, 16, 4);                unsigned int preferred_time =  opt6_uint(ia_option, 16, 4);
                unsigned int preferred_time = 0; /* in case renewal inappropriate */                unsigned int valid_time =  opt6_uint(ia_option, 20, 4);
                unsigned int valid_time = 0; 
                 char *message = NULL;                  char *message = NULL;
                 struct dhcp_context *this_context;                  struct dhcp_context *this_context;
   
                   memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ); 
                                   
                if (!(lease = lease6_find(state.clid, state.clid_len,                if (!(lease = lease6_find(state->clid, state->clid_len,
                                          state.ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,                                           state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA, 
                                          state.iaid, req_addr)))                                          state->iaid, &req_addr)))
                   {                    {
                    /* If the server cannot find a client entry for the IA the server                    if (msg_type == DHCP6REBIND)
                       returns the IA containing no addresses with a Status Code option set                      {
                       to NoBinding in the Reply message. */                        /* When rebinding, we can create a lease if it doesn't exist. */
                    save_counter(iacntr);                        lease = lease6_allocate(&req_addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
                    t1cntr = 0;                        if (lease)
                                              lease_set_iaid(lease, state->iaid);
                    log6_packet(&state, "DHCPREPLY", req_addr, _("lease not found"));                        else
                                              break;
                    o1 = new_opt6(OPTION6_STATUS_CODE);                      }
                    put_opt6_short(DHCP6NOBINDING);                    else
                    put_opt6_string(_("no binding found"));                      {
                    end_opt6(o1);                        /* If the server cannot find a client entry for the IA the server
                    break;                           returns the IA containing no addresses with a Status Code option set
                            to NoBinding in the Reply message. */
                         save_counter(iacntr);
                         t1cntr = 0;
                         
                         log6_packet(state, "DHCPREPLY", &req_addr, _("lease not found"));
                         
                         o1 = new_opt6(OPTION6_STATUS_CODE);
                         put_opt6_short(DHCP6NOBINDING);
                         put_opt6_string(_("no binding found"));
                         end_opt6(o1);
                         
                         preferred_time = valid_time = 0;
                         break;
                       }
                   }                    }
                                   
                                if ((this_context = address6_available(state->context, &req_addr, tagif, 1)) ||
                if ((this_context = address6_available(context, req_addr, tagif, 1)) ||                    (this_context = address6_valid(state->context, &req_addr, tagif, 1)))
                    (this_context = address6_valid(context, req_addr, tagif, 1))) 
                   {                    {
                     struct in6_addr addr;  
                     unsigned int lease_time;                      unsigned int lease_time;
   
                    get_context_tag(&state, this_context);                    get_context_tag(state, this_context);
                                           
                    if (config_valid(config, this_context, &addr) && IN6_ARE_ADDR_EQUAL(&addr, req_addr) && have_config(config, CONFIG_TIME))                    if (config_implies(config, this_context, &req_addr) && have_config(config, CONFIG_TIME))
                       lease_time = config->lease_time;                        lease_time = config->lease_time;
                     else                       else 
                       lease_time = this_context->lease_time;                        lease_time = this_context->lease_time;
                                           
                    calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time, requested_time);                     calculate_times(this_context, &min_time, &valid_time, &preferred_time, lease_time); 
                                           
                     lease_set_expires(lease, valid_time, now);                      lease_set_expires(lease, valid_time, now);
                    if (state.ia_type == OPTION6_IA_NA && state.hostname)                    /* Update MAC record in case it's new information. */
                     if (state->mac_len != 0)
                       lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
                     if (state->ia_type == OPTION6_IA_NA && state->hostname)
                       {                        {
                        char *addr_domain = get_domain6(req_addr);                        char *addr_domain = get_domain6(&req_addr);
                        if (!state.send_domain)                        if (!state->send_domain)
                          state.send_domain = addr_domain;                          state->send_domain = addr_domain;
                        lease_set_hostname(lease, state.hostname, state.hostname_auth, addr_domain, state.domain);                         lease_set_hostname(lease, state->hostname, state->hostname_auth, addr_domain, state->domain); 
                        message = state.hostname;                        message = state->hostname;
                       }                        }
                                           
                                           
                     if (preferred_time == 0)                      if (preferred_time == 0)
                       message = _("deprecated");                        message = _("deprecated");
   
                       address_assigned = 1;
                   }                    }
                 else                  else
                  message = _("address invalid");                  {
                                    preferred_time = valid_time = 0;
                log6_packet(&state, "DHCPREPLY", req_addr, message);                         message = _("address invalid");
                                  
 
                 if (message && (message != state->hostname))
                   log6_packet(state, "DHCPREPLY", &req_addr, message);     
                 else
                   log6_quiet(state, "DHCPREPLY", &req_addr, message);
         
                 o1 =  new_opt6(OPTION6_IAADDR);                  o1 =  new_opt6(OPTION6_IAADDR);
                put_opt6(req_addr, sizeof(*req_addr));                put_opt6(&req_addr, sizeof(req_addr));
                 put_opt6_long(preferred_time);                  put_opt6_long(preferred_time);
                 put_opt6_long(valid_time);                  put_opt6_long(valid_time);
                 end_opt6(o1);                  end_opt6(o1);
Line 963  static int dhcp6_no_relay(int msg_type, struct in6_add Line 1039  static int dhcp6_no_relay(int msg_type, struct in6_add
             end_ia(t1cntr, min_time, 1);              end_ia(t1cntr, min_time, 1);
             end_opt6(o);              end_opt6(o);
           }            }
   
           if (!address_assigned && msg_type == DHCP6REBIND)
             { 
               /* can't create lease for any address, return error */
               o1 = new_opt6(OPTION6_STATUS_CODE);
               put_opt6_short(DHCP6NOADDRS);
               put_opt6_string(_("no addresses available"));
               end_opt6(o1);
             }
                   
        tagif = add_options(&state, fallback, context);        tagif = add_options(state, 0);
         break;          break;
           
       }        }
               
     case DHCP6CONFIRM:      case DHCP6CONFIRM:
       {        {
           int good_addr = 0;
   
         /* set reply message type */          /* set reply message type */
        *outmsgtypep = DHCP6REPLY;        outmsgtype = DHCP6REPLY;
                   
        log6_packet(&state, "DHCPCONFIRM", NULL, NULL);        log6_quiet(state, "DHCPCONFIRM", NULL, NULL);
                   
        for (opt = state.packet_options; opt; opt = opt6_next(opt, state.end))        for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
           {            {
             void *ia_option, *ia_end;              void *ia_option, *ia_end;
                           
            for (check_ia(&state, opt, &ia_end, &ia_option);            for (check_ia(state, opt, &ia_end, &ia_option);
                  ia_option;                   ia_option;
                  ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))                   ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))
               {                {
                struct in6_addr *req_addr = opt6_ptr(ia_option, 0);                struct in6_addr req_addr;
 
                 /* alignment */
                 memcpy(&req_addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
                                   
                if (!address6_available(context, req_addr, tagif, 1))                if (!address6_valid(state->context, &req_addr, tagif, 1))
                   {                    {
                     o1 = new_opt6(OPTION6_STATUS_CODE);                      o1 = new_opt6(OPTION6_STATUS_CODE);
                     put_opt6_short(DHCP6NOTONLINK);                      put_opt6_short(DHCP6NOTONLINK);
                     put_opt6_string(_("confirm failed"));                      put_opt6_string(_("confirm failed"));
                     end_opt6(o1);                      end_opt6(o1);
                       log6_quiet(state, "DHCPREPLY", &req_addr, _("confirm failed"));
                     return 1;                      return 1;
                   }                    }
   
                log6_packet(&state, "DHCPREPLY", req_addr, state.hostname);                good_addr = 1;
                 log6_quiet(state, "DHCPREPLY", &req_addr, state->hostname);
               }                }
           }                  }      
           
           /* No addresses, no reply: RFC 3315 18.2.2 */
           if (!good_addr)
             return 0;
   
         o1 = new_opt6(OPTION6_STATUS_CODE);          o1 = new_opt6(OPTION6_STATUS_CODE);
         put_opt6_short(DHCP6SUCCESS );          put_opt6_short(DHCP6SUCCESS );
Line 1010  static int dhcp6_no_relay(int msg_type, struct in6_add Line 1105  static int dhcp6_no_relay(int msg_type, struct in6_add
       {        {
         /* We can't discriminate contexts based on address, as we don't know it.          /* We can't discriminate contexts based on address, as we don't know it.
            If there is only one possible context, we can use its tags */             If there is only one possible context, we can use its tags */
        if (context && context->netid.net && !context->current)        if (state->context && state->context->netid.net && !state->context->current)
           {            {
            context->netid.next = NULL;            state->context->netid.next = NULL;
            state.context_tags =  &context->netid;            state->context_tags =  &state->context->netid;
           }            }
        log6_packet(&state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state.hostname);
         /* Similarly, we can't determine domain from address, but if the FQDN is
            given in --dhcp-host, we can use that, and failing that we can use the 
            unqualified configured domain, if any. */
         if (state->hostname_auth)
           state->send_domain = state->domain;
         else
           state->send_domain = get_domain6(NULL);
 
         log6_quiet(state, "DHCPINFORMATION-REQUEST", NULL, ignore ? _("ignored") : state->hostname);
         if (ignore)          if (ignore)
           return 0;            return 0;
        *outmsgtypep = DHCP6REPLY;        outmsgtype = DHCP6REPLY;
        tagif = add_options(&state, fallback, context);        tagif = add_options(state, 1);
         break;          break;
       }        }
               
Line 1027  static int dhcp6_no_relay(int msg_type, struct in6_add Line 1131  static int dhcp6_no_relay(int msg_type, struct in6_add
     case DHCP6RELEASE:      case DHCP6RELEASE:
       {        {
         /* set reply message type */          /* set reply message type */
        *outmsgtypep = DHCP6REPLY;        outmsgtype = DHCP6REPLY;
   
        log6_packet(&state, "DHCPRELEASE", NULL, NULL);        log6_quiet(state, "DHCPRELEASE", NULL, NULL);
   
        for (opt = state.packet_options; opt; opt = opt6_next(opt, state.end))        for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
           {            {
             void *ia_option, *ia_end;              void *ia_option, *ia_end;
             int made_ia = 0;              int made_ia = 0;
                                           
            for (check_ia(&state, opt, &ia_end, &ia_option);            for (check_ia(state, opt, &ia_end, &ia_option);
                  ia_option;                   ia_option;
                  ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))                    ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) 
               {                {
                 struct dhcp_lease *lease;                  struct dhcp_lease *lease;
                                struct in6_addr addr;
                if ((lease = lease6_find(state.clid, state.clid_len, state.ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
                                         state.iaid, opt6_ptr(ia_option, 0))))                /* align */
                 memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
                 if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
                                          state->iaid, &addr)))
                   lease_prune(lease, now);                    lease_prune(lease, now);
                 else                  else
                   {                    {
                     if (!made_ia)                      if (!made_ia)
                       {                        {
                        o = new_opt6(state.ia_type);                        o = new_opt6(state->ia_type);
                        put_opt6_long(state.iaid);                        put_opt6_long(state->iaid);
                        if (state.ia_type == OPTION6_IA_NA)                        if (state->ia_type == OPTION6_IA_NA)
                           {                            {
                             put_opt6_long(0);                              put_opt6_long(0);
                             put_opt6_long(0)                              put_opt6_long(0)
Line 1060  static int dhcp6_no_relay(int msg_type, struct in6_add Line 1167  static int dhcp6_no_relay(int msg_type, struct in6_add
                       }                        }
                                           
                     o1 = new_opt6(OPTION6_IAADDR);                      o1 = new_opt6(OPTION6_IAADDR);
                    put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);                    put_opt6(&addr, IN6ADDRSZ);
                     put_opt6_long(0);                      put_opt6_long(0);
                     put_opt6_long(0);                      put_opt6_long(0);
                     end_opt6(o1);                      end_opt6(o1);
Line 1089  static int dhcp6_no_relay(int msg_type, struct in6_add Line 1196  static int dhcp6_no_relay(int msg_type, struct in6_add
     case DHCP6DECLINE:      case DHCP6DECLINE:
       {        {
         /* set reply message type */          /* set reply message type */
        *outmsgtypep = DHCP6REPLY;        outmsgtype = DHCP6REPLY;
                   
        log6_packet(&state, "DHCPDECLINE", NULL, NULL);        log6_quiet(state, "DHCPDECLINE", NULL, NULL);
   
        for (opt = state.packet_options; opt; opt = opt6_next(opt, state.end))        for (opt = state->packet_options; opt; opt = opt6_next(opt, state->end))
           {            {
             void *ia_option, *ia_end;              void *ia_option, *ia_end;
             int made_ia = 0;              int made_ia = 0;
                                           
            for (check_ia(&state, opt, &ia_end, &ia_option);            for (check_ia(state, opt, &ia_end, &ia_option);
                  ia_option;                   ia_option;
                  ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24))                    ia_option = opt6_find(opt6_next(ia_option, ia_end), ia_end, OPTION6_IAADDR, 24)) 
               {                {
                 struct dhcp_lease *lease;                  struct dhcp_lease *lease;
                struct in6_addr *addrp = opt6_ptr(ia_option, 0);                struct in6_addr addr;
                 struct addrlist *addr_list;
                 
                 /* align */
                 memcpy(&addr, opt6_ptr(ia_option, 0), IN6ADDRSZ);
   
                if (have_config(config, CONFIG_ADDR6) && IN6_ARE_ADDR_EQUAL(&config->addr6, addrp))                if ((addr_list = config_implies(config, state->context, &addr)))
                   {                    {
                     prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);                      prettyprint_time(daemon->dhcp_buff3, DECLINE_BACKOFF);
                    inet_ntop(AF_INET6, addrp, daemon->addrbuff, ADDRSTRLEN);                    inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
                     my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"),                       my_syslog(MS_DHCP | LOG_WARNING, _("disabling DHCP static address %s for %s"), 
                               daemon->addrbuff, daemon->dhcp_buff3);                                daemon->addrbuff, daemon->dhcp_buff3);
                    config->flags |= CONFIG_DECLINED;                    addr_list->flags |= ADDRLIST_DECLINED;
                    config->decline_time = now;                    addr_list->decline_time = now;
                   }                    }
                 else                  else
                   /* make sure this host gets a different address next time. */                    /* make sure this host gets a different address next time. */
                  for (; context; context = context->current)                  for (context_tmp = state->context; context_tmp; context_tmp = context_tmp->current)
                    context->addr_epoch++;                    context_tmp->addr_epoch++;
                                   
                if ((lease = lease6_find(state.clid, state.clid_len, state.ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,                if ((lease = lease6_find(state->clid, state->clid_len, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA,
                                         state.iaid, opt6_ptr(ia_option, 0))))                                         state->iaid, &addr)))
                   lease_prune(lease, now);                    lease_prune(lease, now);
                 else                  else
                   {                    {
                     if (!made_ia)                      if (!made_ia)
                       {                        {
                        o = new_opt6(state.ia_type);                        o = new_opt6(state->ia_type);
                        put_opt6_long(state.iaid);                        put_opt6_long(state->iaid);
                        if (state.ia_type == OPTION6_IA_NA)                        if (state->ia_type == OPTION6_IA_NA)
                           {                            {
                             put_opt6_long(0);                              put_opt6_long(0);
                             put_opt6_long(0)                              put_opt6_long(0)
Line 1137  static int dhcp6_no_relay(int msg_type, struct in6_add Line 1248  static int dhcp6_no_relay(int msg_type, struct in6_add
                       }                        }
                                           
                     o1 = new_opt6(OPTION6_IAADDR);                      o1 = new_opt6(OPTION6_IAADDR);
                    put_opt6(opt6_ptr(ia_option, 0), IN6ADDRSZ);                    put_opt6(&addr, IN6ADDRSZ);
                     put_opt6_long(0);                      put_opt6_long(0);
                     put_opt6_long(0);                      put_opt6_long(0);
                     end_opt6(o1);                      end_opt6(o1);
Line 1155  static int dhcp6_no_relay(int msg_type, struct in6_add Line 1266  static int dhcp6_no_relay(int msg_type, struct in6_add
               }                }
                           
           }            }
   
           /* We must answer with 'success' in global section anyway */
           o1 = new_opt6(OPTION6_STATUS_CODE);
           put_opt6_short(DHCP6SUCCESS);
           put_opt6_string(_("success"));
           end_opt6(o1);
         break;          break;
       }        }
   
     }      }
   
     log_tags(tagif, state->xid);
   
    done:
     /* Fill in the message type. Note that we store the offset,
        not a direct pointer, since the packet memory may have been 
        reallocated. */
     ((unsigned char *)(daemon->outpacket.iov_base))[start_msg] = outmsgtype;
   
     log6_opts(0, state->xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));
       
   log_tags(tagif, state.xid);  
     
   if (option_bool(OPT_LOG_OPTS))  
     log6_opts(0, state.xid, daemon->outpacket.iov_base + start_opts, daemon->outpacket.iov_base + save_counter(-1));  
     
   return 1;    return 1;
   
 }  }
   
struct dhcp_netid *add_options(struct state *state, struct in6_addr *fallback, struct dhcp_context *context)  static struct dhcp_netid *add_options(struct state *state, int do_refresh)  
 {  {
   void *oro;    void *oro;
   /* filter options based on tags, those we want get DHOPT_TAGOK bit set */    /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
   struct dhcp_netid *tagif = option_filter(state->tags, state->context_tags, daemon->dhcp_opts6);    struct dhcp_netid *tagif = option_filter(state->tags, state->context_tags, daemon->dhcp_opts6);
   struct dhcp_opt *opt_cfg;    struct dhcp_opt *opt_cfg;
  int done_dns = 0, do_encap = 0;  int done_dns = 0, done_refresh = !do_refresh, do_encap = 0;
   int i, o, o1;    int i, o, o1;
   
   oro = opt6_find(state->packet_options, state->end, OPTION6_ORO, 0);    oro = opt6_find(state->packet_options, state->end, OPTION6_ORO, 0);
Line 1197  struct dhcp_netid *add_options(struct state *state, st Line 1319  struct dhcp_netid *add_options(struct state *state, st
             continue;              continue;
         }          }
               
         if (opt_cfg->opt == OPTION6_REFRESH_TIME)
           done_refresh = 1;
          
       if (opt_cfg->opt == OPTION6_DNS_SERVER)        if (opt_cfg->opt == OPTION6_DNS_SERVER)
        {        done_dns = 1;
          done_dns = 1; 
          if (opt_cfg->len == 0) 
            continue; 
        } 
               
       o = new_opt6(opt_cfg->opt);  
       if (opt_cfg->flags & DHOPT_ADDR6)        if (opt_cfg->flags & DHOPT_ADDR6)
         {          {
          int j;          int len, j;
          struct in6_addr *a = (struct in6_addr *)opt_cfg->val;          struct in6_addr *a;
          for (j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)          
            {          for (a = (struct in6_addr *)opt_cfg->val, len = opt_cfg->len, j = 0; 
              /* zero means "self" (but not in vendorclass options.) */               j < opt_cfg->len; j += IN6ADDRSZ, a++)
              if (IN6_IS_ADDR_UNSPECIFIED(a))            if ((IN6_IS_ADDR_ULA_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ula_addr)) ||
                {                (IN6_IS_ADDR_LINK_LOCAL_ZERO(a) && IN6_IS_ADDR_UNSPECIFIED(state->ll_addr)))
                  if (!add_local_addrs(context))              len -= IN6ADDRSZ;
                    put_opt6(fallback, IN6ADDRSZ);          
           if (len != 0)
             {
               
               o = new_opt6(opt_cfg->opt);
                   
               for (a = (struct in6_addr *)opt_cfg->val, j = 0; j < opt_cfg->len; j+=IN6ADDRSZ, a++)
                 {
                   struct in6_addr *p = NULL;
 
                   if (IN6_IS_ADDR_UNSPECIFIED(a))
                     {
                       if (!add_local_addrs(state->context))
                         p = state->fallback;
                     }
                   else if (IN6_IS_ADDR_ULA_ZERO(a))
                     {
                       if (!IN6_IS_ADDR_UNSPECIFIED(state->ula_addr))
                         p = state->ula_addr;
                     }
                   else if (IN6_IS_ADDR_LINK_LOCAL_ZERO(a))
                     {
                       if (!IN6_IS_ADDR_UNSPECIFIED(state->ll_addr))
                         p = state->ll_addr;
                     }
                   else
                     p = a;
 
                   if (!p)
                     continue;
                   else if (opt_cfg->opt == OPTION6_NTP_SERVER)
                     {
                       if (IN6_IS_ADDR_MULTICAST(p))
                         o1 = new_opt6(NTP_SUBOPTION_MC_ADDR);
                       else
                         o1 = new_opt6(NTP_SUBOPTION_SRV_ADDR);
                       put_opt6(p, IN6ADDRSZ);
                       end_opt6(o1);
                     }
                   else
                     put_opt6(p, IN6ADDRSZ);
                 }                  }
              else
                put_opt6(a, IN6ADDRSZ);              end_opt6(o);
            }            }
         }          }
      else if (opt_cfg->val)      else
        put_opt6(opt_cfg->val, opt_cfg->len);        {
      end_opt6(o);          o = new_opt6(opt_cfg->opt);
           if (opt_cfg->val)
             put_opt6(opt_cfg->val, opt_cfg->len);
           end_opt6(o);
         }
     }      }
       
  if (daemon->port == NAMESERVER_PORT && !done_dns &&   if (daemon->port == NAMESERVER_PORT && !done_dns)
      (!IN6_IS_ADDR_UNSPECIFIED(&context->local6) || 
       !IN6_IS_ADDR_UNSPECIFIED(fallback))) 
     {      {
       o = new_opt6(OPTION6_DNS_SERVER);        o = new_opt6(OPTION6_DNS_SERVER);
      if (!add_local_addrs(context))      if (!add_local_addrs(state->context))
        put_opt6(fallback, IN6ADDRSZ);        put_opt6(state->fallback, IN6ADDRSZ);
       end_opt6(o);         end_opt6(o); 
     }      }
   
     if (state->context && !done_refresh)
       {
         struct dhcp_context *c;
         unsigned int lease_time = 0xffffffff;
         
         /* Find the smallest lease tie of all contexts,
            subject to the RFC-4242 stipulation that this must not 
            be less than 600. */
         for (c = state->context; c; c = c->next)
           if (c->lease_time < lease_time)
             {
               if (c->lease_time < 600)
                 lease_time = 600;
               else
                 lease_time = c->lease_time;
             }
   
         o = new_opt6(OPTION6_REFRESH_TIME);
         put_opt6_long(lease_time);
         end_opt6(o); 
       }
         
     /* handle vendor-identifying vendor-encapsulated options,      /* handle vendor-identifying vendor-encapsulated options,
        dhcp-option = vi-encap:13,17,....... */         dhcp-option = vi-encap:13,17,....... */
Line 1299  struct dhcp_netid *add_options(struct state *state, st Line 1483  struct dhcp_netid *add_options(struct state *state, st
       size_t len = strlen(state->hostname);        size_t len = strlen(state->hostname);
               
       if (state->send_domain)        if (state->send_domain)
        len += strlen(state->send_domain) + 1;        len += strlen(state->send_domain) + 2;
   
       o = new_opt6(OPTION6_FQDN);        o = new_opt6(OPTION6_FQDN);
      if ((p = expand(len + 3)))      if ((p = expand(len + 2)))
         {          {
           *(p++) = state->fqdn_flags;            *(p++) = state->fqdn_flags;
          p = do_rfc1035_name(p, state->hostname);          p = do_rfc1035_name(p, state->hostname, NULL);
           if (state->send_domain)            if (state->send_domain)
            p = do_rfc1035_name(p, state->send_domain);            {
          *p = 0;              p = do_rfc1035_name(p, state->send_domain, NULL);
               *p = 0;
             }
         }          }
       end_opt6(o);        end_opt6(o);
     }      }
Line 1383  static void get_context_tag(struct state *state, struc Line 1569  static void get_context_tag(struct state *state, struc
     }      }
 }   } 
   
 #ifdef OPTION6_PREFIX_CLASS  
 static struct prefix_class *prefix_class_from_context(struct dhcp_context *context)  
 {  
   struct prefix_class *p;  
   struct dhcp_netid *t;  
     
   for (p = daemon->prefix_classes; p ; p = p->next)  
     for (t = context->filter; t; t = t->next)  
       if (strcmp(p->tag.net, t->net) == 0)  
         return p;  
     
  return NULL;  
 }  
 #endif  
   
 static int check_ia(struct state *state, void *opt, void **endp, void **ia_option)  static int check_ia(struct state *state, void *opt, void **endp, void **ia_option)
 {  {
   state->ia_type = opt6_type(opt);    state->ia_type = opt6_type(opt);
Line 1443  static void end_ia(int t1cntr, unsigned int min_time,  Line 1614  static void end_ia(int t1cntr, unsigned int min_time, 
 {  {
   if (t1cntr != 0)    if (t1cntr != 0)
     {      {
      /* go back an fill in fields in IA_NA option */      /* go back and fill in fields in IA_NA option */
       int sav = save_counter(t1cntr);        int sav = save_counter(t1cntr);
       unsigned int t1, t2, fuzz = 0;        unsigned int t1, t2, fuzz = 0;
   
Line 1463  static void end_ia(int t1cntr, unsigned int min_time,  Line 1634  static void end_ia(int t1cntr, unsigned int min_time, 
     }         }   
 }  }
   
static void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, unsigned int requested_timestatic void add_address(struct state *state, struct dhcp_context *context, unsigned int lease_time, void *ia_option
                        unsigned int *min_time, struct in6_addr *addr, int do_update, time_t now)                        unsigned int *min_time, struct in6_addr *addr, time_t now)
 {  {
  unsigned int valid_time, preferred_time;  unsigned int valid_time = 0, preferred_time = 0;
   int o = new_opt6(OPTION6_IAADDR);    int o = new_opt6(OPTION6_IAADDR);
   struct dhcp_lease *lease;    struct dhcp_lease *lease;
   
  calculate_times(context, min_time, &valid_time, &preferred_time, lease_time, requested_time);   /* get client requested times */
   if (ia_option)
     {
       preferred_time =  opt6_uint(ia_option, 16, 4);
       valid_time =  opt6_uint(ia_option, 20, 4);
     }
 
   calculate_times(context, min_time, &valid_time, &preferred_time, lease_time); 
       
   put_opt6(addr, sizeof(*addr));    put_opt6(addr, sizeof(*addr));
   put_opt6_long(preferred_time);    put_opt6_long(preferred_time);
   put_opt6_long(valid_time);                    put_opt6_long(valid_time);                
     
 #ifdef OPTION6_PREFIX_CLASS  
   if (state->send_prefix_class)  
     {  
       int o1 = new_opt6(OPTION6_PREFIX_CLASS);  
       put_opt6_short(state->send_prefix_class->class);  
       end_opt6(o1);  
     }  
 #endif  
   
   end_opt6(o);    end_opt6(o);
       
  if (do_update)  if (state->lease_allocate)
     update_leases(state, context, addr, valid_time, now);      update_leases(state, context, addr, valid_time, now);
   
   if ((lease = lease6_find_by_addr(addr, 128, 0)))    if ((lease = lease6_find_by_addr(addr, 128, 0)))
Line 1511  static void add_address(struct state *state, struct dh Line 1679  static void add_address(struct state *state, struct dh
         }          }
     }      }
   
  log6_packet(state, do_update ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname);  log6_quiet(state, state->lease_allocate ? "DHCPREPLY" : "DHCPADVERTISE", addr, state->hostname);
   
 }  }
   
static void mark_context_used(struct state *state, struct dhcp_context *context, struct in6_addr *addr)static void mark_context_used(struct state *state, struct in6_addr *addr)
 {  {
     struct dhcp_context *context;
   
   /* Mark that we have an address for this prefix. */    /* Mark that we have an address for this prefix. */
#ifdef OPTION6_PREFIX_CLASS  for (context = state->context; context; context = context->current)
  for (; context; context = context->current) 
    if (is_same_net6(addr, &context->start6, context->prefix) && 
        (!state->send_prefix_class || state->send_prefix_class == prefix_class_from_context(context))) 
      context->flags |= CONTEXT_USED; 
#else 
  (void)state; /* warning */ 
  for (; context; context = context->current) 
     if (is_same_net6(addr, &context->start6, context->prefix))      if (is_same_net6(addr, &context->start6, context->prefix))
       context->flags |= CONTEXT_USED;        context->flags |= CONTEXT_USED;
 #endif  
 }  }
   
 static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr)  static void mark_config_used(struct dhcp_context *context, struct in6_addr *addr)
Line 1548  static int check_address(struct state *state, struct i Line 1710  static int check_address(struct state *state, struct i
   
   if (lease->clid_len != state->clid_len ||     if (lease->clid_len != state->clid_len || 
       memcmp(lease->clid, state->clid, state->clid_len) != 0 ||        memcmp(lease->clid, state->clid, state->clid_len) != 0 ||
      lease->hwaddr_type != state->iaid)      lease->iaid != state->iaid)
     return 0;      return 0;
   
   return 1;    return 1;
 }  }
   
static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, 
                            unsigned int *preferred_timep, unsigned int lease_time, unsigned int requested_time)/* return true of *addr could have been generated from config. */
 static struct addrlist *config_implies(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr)
 {  {
  unsigned int preferred_time, valid_time;  int prefix;
   struct in6_addr wild_addr;
   struct addrlist *addr_list;
   
   if (!config || !(config->flags & CONFIG_ADDR6))
     return NULL;
   
   for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
     {
       prefix = (addr_list->flags & ADDRLIST_PREFIX) ? addr_list->prefixlen : 128;
       wild_addr = addr_list->addr.addr6;
       
       if ((addr_list->flags & ADDRLIST_WILDCARD) && context->prefix == 64)
         {
           wild_addr = context->start6;
           setaddr6part(&wild_addr, addr6part(&addr_list->addr.addr6));
         }
       else if (!is_same_net6(&context->start6, addr, context->prefix))
         continue;
       
       if (is_same_net6(&wild_addr, addr, prefix))
         return addr_list;
     }
   
   return NULL;
 }
   
  if (requested_time < 120u )static int config_valid(struct dhcp_config *config, struct dhcp_context *context, struct in6_addr *addr, struct state *state, time_t now)
    requested_time = 120u; /* sanity */{
  if (lease_time == 0xffffffff || (requested_time != 0xffffffff && requested_time < lease_time))  u64 addrpart, i, addresses;
    lease_time = requested_time;  struct addrlist *addr_list;
                      
  valid_time = (context->valid < lease_time) ? context->valid : lease_time;  if (!config || !(config->flags & CONFIG_ADDR6))
  preferred_time = (context->preferred < lease_time) ? context->preferred : lease_time;    return 0;
   
  if (context->flags & CONTEXT_DEPRECATE)  for (addr_list = config->addr6; addr_list; addr_list = addr_list->next)
    preferred_time = 0;    if (!(addr_list->flags & ADDRLIST_DECLINED) ||
         difftime(now, addr_list->decline_time) >= (float)DECLINE_BACKOFF)
       {
         addrpart = addr6part(&addr_list->addr.addr6);
         addresses = 1;
         
         if (addr_list->flags & ADDRLIST_PREFIX)
           addresses = (u64)1<<(128-addr_list->prefixlen);
         
         if ((addr_list->flags & ADDRLIST_WILDCARD))
           {
             if (context->prefix != 64)
               continue;
             
             *addr = context->start6;
           }
         else if (is_same_net6(&context->start6, &addr_list->addr.addr6, context->prefix))
           *addr = addr_list->addr.addr6;
         else
           continue;
         
         for (i = 0 ; i < addresses; i++)
           {
             setaddr6part(addr, addrpart+i);
             
             if (check_address(state, addr))
               return 1;
           }
       }
   
   return 0;
 }
   
   /* Calculate valid and preferred times to send in leases/renewals. 
   
      Inputs are:
   
      *valid_timep, *preferred_timep - requested times from IAADDR options.
      context->valid, context->preferred - times associated with subnet address on local interface.
      context->flags | CONTEXT_DEPRECATE - "deprecated" flag in dhcp-range.
      lease_time - configured time for context for individual client.
      *min_time - smallest valid time sent so far.
   
      Outputs are :
      
      *valid_timep, *preferred_timep - times to be send in IAADDR option.
      *min_time - smallest valid time sent so far, to calculate T1 and T2.
      
      */
   static void calculate_times(struct dhcp_context *context, unsigned int *min_time, unsigned int *valid_timep, 
                               unsigned int *preferred_timep, unsigned int lease_time)
   {
     unsigned int req_preferred = *preferred_timep, req_valid = *valid_timep;
     unsigned int valid_time = lease_time, preferred_time = lease_time;
     
     /* RFC 3315: "A server ignores the lifetimes set
        by the client if the preferred lifetime is greater than the valid
        lifetime. */
     if (req_preferred <= req_valid)
       {
         if (req_preferred != 0)
           {
             /* 0 == "no preference from client" */
             if (req_preferred < 120u)
               req_preferred = 120u; /* sanity */
             
             if (req_preferred < preferred_time)
               preferred_time = req_preferred;
           }
         
         if (req_valid != 0)
           /* 0 == "no preference from client" */
           {
             if (req_valid < 120u)
               req_valid = 120u; /* sanity */
             
             if (req_valid < valid_time)
               valid_time = req_valid;
           }
       }
   
     /* deprecate (preferred == 0) which configured, or when local address 
        is deprecated */
     if ((context->flags & CONTEXT_DEPRECATE) || context->preferred == 0)
       preferred_time = 0;
     
   if (preferred_time != 0 && preferred_time < *min_time)    if (preferred_time != 0 && preferred_time < *min_time)
     *min_time = preferred_time;      *min_time = preferred_time;
       
Line 1583  static void calculate_times(struct dhcp_context *conte Line 1855  static void calculate_times(struct dhcp_context *conte
 static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now)  static void update_leases(struct state *state, struct dhcp_context *context, struct in6_addr *addr, unsigned int lease_time, time_t now)
 {  {
   struct dhcp_lease *lease = lease6_find_by_addr(addr, 128, 0);    struct dhcp_lease *lease = lease6_find_by_addr(addr, 128, 0);
   #ifdef HAVE_SCRIPT
   struct dhcp_netid *tagif = run_tag_if(state->tags);    struct dhcp_netid *tagif = run_tag_if(state->tags);
   #endif
   
     (void)context;
   
   if (!lease)    if (!lease)
     lease = lease6_allocate(addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);      lease = lease6_allocate(addr, state->ia_type == OPTION6_IA_NA ? LEASE_NA : LEASE_TA);
       
   if (lease)    if (lease)
     {      {
       lease_set_expires(lease, lease_time, now);        lease_set_expires(lease, lease_time, now);
      lease_set_hwaddr(lease, NULL, state->clid, 0, state->iaid, state->clid_len, now, 0);      lease_set_iaid(lease, state->iaid); 
       lease_set_hwaddr(lease, state->mac, state->clid, state->mac_len, state->mac_type, state->clid_len, now, 0);
       lease_set_interface(lease, state->interface, now);        lease_set_interface(lease, state->interface, now);
       if (state->hostname && state->ia_type == OPTION6_IA_NA)        if (state->hostname && state->ia_type == OPTION6_IA_NA)
         {          {
Line 1604  static void update_leases(struct state *state, struct  Line 1881  static void update_leases(struct state *state, struct 
 #ifdef HAVE_SCRIPT  #ifdef HAVE_SCRIPT
       if (daemon->lease_change_command)        if (daemon->lease_change_command)
         {          {
          void *class_opt;          void *opt;
           
           lease->flags |= LEASE_CHANGED;            lease->flags |= LEASE_CHANGED;
           free(lease->extradata);            free(lease->extradata);
           lease->extradata = NULL;            lease->extradata = NULL;
           lease->extradata_size = lease->extradata_len = 0;            lease->extradata_size = lease->extradata_len = 0;
           lease->vendorclass_count = 0;             lease->vendorclass_count = 0; 
                       
          if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))          if ((opt = opt6_find(state->packet_options, state->end, OPTION6_VENDOR_CLASS, 4)))
             {              {
              void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));              void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
               lease->vendorclass_count++;                lease->vendorclass_count++;
               /* send enterprise number first  */                /* send enterprise number first  */
              sprintf(daemon->dhcp_buff2, "%u", opt6_uint(class_opt, 0, 4));              sprintf(daemon->dhcp_buff2, "%u", opt6_uint(opt, 0, 4));
               lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);                lease_add_extradata(lease, (unsigned char *)daemon->dhcp_buff2, strlen(daemon->dhcp_buff2), 0);
                               
              if (opt6_len(class_opt) >= 6)               if (opt6_len(opt) >= 6) 
                for (enc_opt = opt6_ptr(class_opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))                for (enc_opt = opt6_ptr(opt, 4); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
                   {                    {
                     lease->vendorclass_count++;                      lease->vendorclass_count++;
                     lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);                      lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
Line 1630  static void update_leases(struct state *state, struct  Line 1908  static void update_leases(struct state *state, struct 
           lease_add_extradata(lease, (unsigned char *)state->client_hostname,             lease_add_extradata(lease, (unsigned char *)state->client_hostname, 
                               state->client_hostname ? strlen(state->client_hostname) : 0, 0);                                                          state->client_hostname ? strlen(state->client_hostname) : 0, 0);                          
                       
             /* DNSMASQ_REQUESTED_OPTIONS */
             if ((opt = opt6_find(state->packet_options, state->end, OPTION6_ORO, 2)))
               {
                 int i, len = opt6_len(opt)/2;
                 u16 *rop = opt6_ptr(opt, 0);
                 
                 for (i = 0; i < len; i++)
                   lease_add_extradata(lease, (unsigned char *)daemon->namebuff,
                                       sprintf(daemon->namebuff, "%u", ntohs(rop[i])), (i + 1) == len ? 0 : ',');
               }
             else
               lease_add_extradata(lease, NULL, 0, 0);
   
             if ((opt = opt6_find(state->packet_options, state->end, OPTION6_MUD_URL, 1)))
               lease_add_extradata(lease, opt6_ptr(opt, 0), opt6_len(opt), 0);
             else
               lease_add_extradata(lease, NULL, 0, 0);
   
           /* space-concat tag set */            /* space-concat tag set */
           if (!tagif && !context->netid.net)            if (!tagif && !context->netid.net)
             lease_add_extradata(lease, NULL, 0, 0);              lease_add_extradata(lease, NULL, 0, 0);
Line 1659  static void update_leases(struct state *state, struct  Line 1955  static void update_leases(struct state *state, struct 
                       
           lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0);            lease_add_extradata(lease, (unsigned char *)daemon->addrbuff, state->link_address ? strlen(daemon->addrbuff) : 0, 0);
                       
          if ((class_opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))          if ((opt = opt6_find(state->packet_options, state->end, OPTION6_USER_CLASS, 2)))
             {              {
              void *enc_opt, *enc_end = opt6_ptr(class_opt, opt6_len(class_opt));              void *enc_opt, *enc_end = opt6_ptr(opt, opt6_len(opt));
              for (enc_opt = opt6_ptr(class_opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))              for (enc_opt = opt6_ptr(opt, 0); enc_opt; enc_opt = opt6_next(enc_opt, enc_end))
                 lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);                  lease_add_extradata(lease, opt6_ptr(enc_opt, 0), opt6_len(enc_opt), 0);
             }              }
         }          }
Line 1678  static void log6_opts(int nest, unsigned int xid, void Line 1974  static void log6_opts(int nest, unsigned int xid, void
   void *opt;    void *opt;
   char *desc = nest ? "nest" : "sent";    char *desc = nest ? "nest" : "sent";
       
  if (start_opts == end_opts)  if (!option_bool(OPT_LOG_OPTS) || start_opts == end_opts)
     return;      return;
       
   for (opt = start_opts; opt; opt = opt6_next(opt, end_opts))    for (opt = start_opts; opt; opt = opt6_next(opt, end_opts))
Line 1702  static void log6_opts(int nest, unsigned int xid, void Line 1998  static void log6_opts(int nest, unsigned int xid, void
         }          }
       else if (type == OPTION6_IAADDR)        else if (type == OPTION6_IAADDR)
         {          {
          inet_ntop(AF_INET6, opt6_ptr(opt, 0), daemon->addrbuff, ADDRSTRLEN);          struct in6_addr addr;
 
           /* align */
           memcpy(&addr, opt6_ptr(opt, 0), IN6ADDRSZ);
           inet_ntop(AF_INET6, &addr, daemon->addrbuff, ADDRSTRLEN);
           sprintf(daemon->namebuff, "%s PL=%u VL=%u",             sprintf(daemon->namebuff, "%s PL=%u VL=%u", 
                   daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));                    daemon->addrbuff, opt6_uint(opt, 16, 4), opt6_uint(opt, 20, 4));
           optname = "iaaddr";            optname = "iaaddr";
           ia_options = opt6_ptr(opt, 24);            ia_options = opt6_ptr(opt, 24);
         }          }
 #ifdef OPTION6_PREFIX_CLASS  
       else if (type == OPTION6_PREFIX_CLASS)  
         {  
           optname = "prefix-class";  
           sprintf(daemon->namebuff, "class=%u", opt6_uint(opt, 0, 2));  
         }  
 #endif  
       else if (type == OPTION6_STATUS_CODE)        else if (type == OPTION6_STATUS_CODE)
         {          {
           int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));            int len = sprintf(daemon->namebuff, "%u ", opt6_uint(opt, 0, 2));
Line 1737  static void log6_opts(int nest, unsigned int xid, void Line 2030  static void log6_opts(int nest, unsigned int xid, void
     }      }
 }                  }                
     
   static void log6_quiet(struct state *state, char *type, struct in6_addr *addr, char *string)
   {
     if (option_bool(OPT_LOG_OPTS) || !option_bool(OPT_QUIET_DHCP6))
       log6_packet(state, type, addr, string);
   }
   
 static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string)  static void log6_packet(struct state *state, char *type, struct in6_addr *addr, char *string)
 {  {
   int clid_len = state->clid_len;    int clid_len = state->clid_len;
Line 1749  static void log6_packet(struct state *state, char *typ Line 2048  static void log6_packet(struct state *state, char *typ
   
   if (addr)    if (addr)
     {      {
      inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, 255);      inet_ntop(AF_INET6, addr, daemon->dhcp_buff2, DHCP_BUFF_SZ - 1);
       strcat(daemon->dhcp_buff2, " ");        strcat(daemon->dhcp_buff2, " ");
     }      }
   else    else
Line 1827  static unsigned int opt6_uint(unsigned char *opt, int  Line 2126  static unsigned int opt6_uint(unsigned char *opt, int 
       
   return ret;    return ret;
 }   } 
   
   int relay_upstream6(int iface_index, ssize_t sz, 
                       struct in6_addr *peer_address, u32 scope_id, time_t now)
   {
     unsigned char *header;
     unsigned char *inbuff = daemon->dhcp_packet.iov_base;
     int msg_type = *inbuff;
     int hopcount, o;
     struct in6_addr multicast;
     unsigned int maclen, mactype;
     unsigned char mac[DHCP_CHADDR_MAX];
     struct dhcp_relay *relay;
     
     for (relay = daemon->relay6; relay; relay = relay->next)
       if (relay->iface_index != 0 && relay->iface_index == iface_index)
         break;
   
     /* No relay config. */
     if (!relay)
       return 0;
     
     inet_pton(AF_INET6, ALL_SERVERS, &multicast);
     get_client_mac(peer_address, scope_id, mac, &maclen, &mactype, now);
     
     /* Get hop count from nested relayed message */ 
     if (msg_type == DHCP6RELAYFORW)
       hopcount = *((unsigned char *)inbuff+1) + 1;
     else
       hopcount = 0;
   
     reset_counter();
   
     /* RFC 3315 HOP_COUNT_LIMIT */
     if (hopcount > 32 || !(header = put_opt6(NULL, 34)))
       return 1;
     
     header[0] = DHCP6RELAYFORW;
     header[1] = hopcount;
     memcpy(&header[18], peer_address, IN6ADDRSZ);
     
     /* RFC-6939 */
     if (maclen != 0)
       {
         o = new_opt6(OPTION6_CLIENT_MAC);
         put_opt6_short(mactype);
         put_opt6(mac, maclen);
         end_opt6(o);
       }
     
     o = new_opt6(OPTION6_RELAY_MSG);
     put_opt6(inbuff, sz);
     end_opt6(o);
     
     for (; relay; relay = relay->next)
       if (relay->iface_index != 0 && relay->iface_index == iface_index)
         {
           union mysockaddr to;
   
           memcpy(&header[2], &relay->local.addr6, IN6ADDRSZ);
           
           to.sa.sa_family = AF_INET6;
           to.in6.sin6_addr = relay->server.addr6;
           to.in6.sin6_port = htons(relay->port);
           to.in6.sin6_flowinfo = 0;
           to.in6.sin6_scope_id = 0;
           
           if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
             {
               int multicast_iface;
               if (!relay->interface || strchr(relay->interface, '*') ||
                   (multicast_iface = if_nametoindex(relay->interface)) == 0 ||
                   setsockopt(daemon->dhcp6fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_iface, sizeof(multicast_iface)) == -1)
                 {
                   my_syslog(MS_DHCP | LOG_ERR, _("Cannot multicast DHCP relay via interface %s"), relay->interface);
                   continue;
                 }
             }
           
   #ifdef HAVE_DUMPFILE
           dump_packet_udp(DUMP_DHCPV6, (void *)daemon->outpacket.iov_base, save_counter(-1), NULL, &to, daemon->dhcp6fd);
   #endif
   
           while (retry_send(sendto(daemon->dhcp6fd, (void *)daemon->outpacket.iov_base, save_counter(-1),
                                    0, (struct sockaddr *)&to, sa_len(&to))));
           
           if (option_bool(OPT_LOG_OPTS))
             {
               inet_ntop(AF_INET6, &relay->local, daemon->addrbuff, ADDRSTRLEN);
               if (IN6_ARE_ADDR_EQUAL(&relay->server.addr6, &multicast))
                 snprintf(daemon->namebuff, MAXDNAME, _("multicast via %s"), relay->interface);
               else
                 inet_ntop(AF_INET6, &relay->server, daemon->namebuff, ADDRSTRLEN);
               my_syslog(MS_DHCP | LOG_INFO, _("DHCP relay at %s -> %s"), daemon->addrbuff, daemon->namebuff);
             }
           
         }
     
     return 1;
   }
   
   int relay_reply6(struct sockaddr_in6 *peer, ssize_t sz, char *arrival_interface)
   {
     struct dhcp_relay *relay;
     struct in6_addr link;
     unsigned char *inbuff = daemon->dhcp_packet.iov_base;
     
     /* must have at least msg_type+hopcount+link_address+peer_address+minimal size option
        which is               1   +    1   +    16      +     16     + 2 + 2 = 38 */
     
     if (sz < 38 || *inbuff != DHCP6RELAYREPL)
       return 0;
     
     memcpy(&link, &inbuff[2], IN6ADDRSZ); 
     
     for (relay = daemon->relay6; relay; relay = relay->next)
       if (IN6_ARE_ADDR_EQUAL(&link, &relay->local.addr6) &&
           (!relay->interface || wildcard_match(relay->interface, arrival_interface)))
         break;
         
     reset_counter();
   
     if (relay)
       {
         void *opt, *opts = inbuff + 34;
         void *end = inbuff + sz;
         for (opt = opts; opt; opt = opt6_next(opt, end))
           if (opt6_type(opt) == OPTION6_RELAY_MSG && opt6_len(opt) > 0)
             {
               int encap_type = *((unsigned char *)opt6_ptr(opt, 0));
               put_opt6(opt6_ptr(opt, 0), opt6_len(opt));
               memcpy(&peer->sin6_addr, &inbuff[18], IN6ADDRSZ); 
               peer->sin6_scope_id = relay->iface_index;
   
               if (encap_type == DHCP6RELAYREPL)
                 {
                   peer->sin6_port = ntohs(DHCPV6_SERVER_PORT);
                   return 1;
                 }
   
               peer->sin6_port = ntohs(DHCPV6_CLIENT_PORT);
               
   #ifdef HAVE_SCRIPT
               if (daemon->lease_change_command && encap_type == DHCP6REPLY)
                 {
                   /* decapsulate relayed message */
                   opts = opt6_ptr(opt, 4);
                   end = opt6_ptr(opt, opt6_len(opt));
   
                   for (opt = opts; opt; opt = opt6_next(opt, end))
                     if (opt6_type(opt) == OPTION6_IA_PD && opt6_len(opt) > 12) 
                       {
                         void *ia_opts = opt6_ptr(opt, 12);
                         void *ia_end = opt6_ptr(opt, opt6_len(opt));
                         void *ia_opt;
                         
                         for (ia_opt = ia_opts; ia_opt; ia_opt = opt6_next(ia_opt, ia_end))
                           /* valid lifetime must not be zero. */
                           if (opt6_type(ia_opt) == OPTION6_IAPREFIX && opt6_len(ia_opt) >= 25 && opt6_uint(ia_opt, 4, 4) != 0)
                             {
                               if (daemon->free_snoops ||
                                   (daemon->free_snoops = whine_malloc(sizeof(struct snoop_record))))
                                 {
                                   struct snoop_record *snoop = daemon->free_snoops;
                                   
                                   daemon->free_snoops = snoop->next;
                                   snoop->client = peer->sin6_addr;
                                   snoop->prefix_len = opt6_uint(ia_opt, 8, 1); 
                                   memcpy(&snoop->prefix, opt6_ptr(ia_opt, 9), IN6ADDRSZ); 
                                   snoop->next = relay->snoop_records;
                                   relay->snoop_records = snoop;
                                 }
                             }
                       }
                 }
   #endif          
               return 1;
             }
         
       }
     
     return 0;
   }
   
   #ifdef HAVE_SCRIPT
   int do_snoop_script_run(void)
   {
     struct dhcp_relay *relay;
     struct snoop_record *snoop;
     
     for (relay = daemon->relay6; relay; relay = relay->next)
       if ((snoop = relay->snoop_records))
         {
           relay->snoop_records = snoop->next;
           snoop->next = daemon->free_snoops;
           daemon->free_snoops = snoop;
           
           queue_relay_snoop(&snoop->client, relay->iface_index, &snoop->prefix, snoop->prefix_len);
           return 1;
         }
     
     return 0;
   }
   #endif
   
 #endif  #endif

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


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