Diff for /embedaddon/dnsmasq/src/rfc2131.c between versions 1.1.1.1 and 1.1.1.3

version 1.1.1.1, 2013/07/29 19:37:40 version 1.1.1.3, 2016/11/02 09:57:01
Line 1 Line 1
/* dnsmasq is Copyright (c) 2000-2013 Simon Kelley/* dnsmasq is Copyright (c) 2000-2016 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 34  static void option_put_string(struct dhcp_packet *mess Line 34  static void option_put_string(struct dhcp_packet *mess
 static struct in_addr option_addr(unsigned char *opt);  static struct in_addr option_addr(unsigned char *opt);
 static unsigned int option_uint(unsigned char *opt, int i, int size);  static unsigned int option_uint(unsigned char *opt, int i, int size);
 static void log_packet(char *type, void *addr, unsigned char *ext_mac,   static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
                       int mac_len, char *interface, char *string, u32 xid);                       int mac_len, char *interface, char *string, char *err, u32 xid);
 static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);  static unsigned char *option_find(struct dhcp_packet *mess, size_t size, int opt_type, int minsize);
 static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);  static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int minsize);
 static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);  static size_t dhcp_packet_size(struct dhcp_packet *mess, unsigned char *agent_id, unsigned char *real_end);
 static void clear_packet(struct dhcp_packet *mess, unsigned char *end);  static void clear_packet(struct dhcp_packet *mess, unsigned char *end);
   static int in_list(unsigned char *list, int opt);
 static void do_options(struct dhcp_context *context,  static void do_options(struct dhcp_context *context,
                        struct dhcp_packet *mess,                         struct dhcp_packet *mess,
                        unsigned char *real_end,                          unsigned char *real_end, 
Line 51  static void do_options(struct dhcp_context *context, Line 52  static void do_options(struct dhcp_context *context,
                        int null_term, int pxearch,                         int null_term, int pxearch,
                        unsigned char *uuid,                         unsigned char *uuid,
                        int vendor_class_len,                         int vendor_class_len,
                       time_t now);                       time_t now,
                        unsigned int lease_time,
                        unsigned short fuzz);
   
   
 static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt);   static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
Line 60  static void pxe_misc(struct dhcp_packet *mess, unsigne Line 63  static void pxe_misc(struct dhcp_packet *mess, unsigne
 static int prune_vendor_opts(struct dhcp_netid *netid);  static int prune_vendor_opts(struct dhcp_netid *netid);
 static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);  static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now);
 struct dhcp_boot *find_boot(struct dhcp_netid *netid);  struct dhcp_boot *find_boot(struct dhcp_netid *netid);
static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe);
       
 size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,  size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
                   size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)                    size_t sz, time_t now, int unicast_dest, int *is_inform, int pxe, struct in_addr fallback)
Line 91  size_t dhcp_reply(struct dhcp_context *context, char * Line 94  size_t dhcp_reply(struct dhcp_context *context, char *
   struct dhcp_netid known_id, iface_id, cpewan_id;    struct dhcp_netid known_id, iface_id, cpewan_id;
   struct dhcp_opt *o;    struct dhcp_opt *o;
   unsigned char pxe_uuid[17];    unsigned char pxe_uuid[17];
  unsigned char *oui = NULL, *serial = NULL, *class = NULL;  unsigned char *oui = NULL, *serial = NULL;
 #ifdef HAVE_SCRIPT
   unsigned char *class = NULL;
 #endif
   
   subnet_addr.s_addr = override.s_addr = 0;    subnet_addr.s_addr = override.s_addr = 0;
   
Line 155  size_t dhcp_reply(struct dhcp_context *context, char * Line 161  size_t dhcp_reply(struct dhcp_context *context, char *
                   unsigned char *y = option_ptr(opt, offset + elen + 5);                    unsigned char *y = option_ptr(opt, offset + elen + 5);
                   oui = option_find1(x, y, 1, 1);                    oui = option_find1(x, y, 1, 1);
                   serial = option_find1(x, y, 2, 1);                    serial = option_find1(x, y, 2, 1);
                  class = option_find1(x, y, 3, 1);#ifdef HAVE_SCRIPT
                                    class = option_find1(x, y, 3, 1);               
 #endif
                   /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing                     /* If TR069-id is present set the tag "cpewan-id" to facilitate echoing 
                      the gateway id back. Note that the device class is optional */                       the gateway id back. Note that the device class is optional */
                   if (oui && serial)                    if (oui && serial)
Line 354  size_t dhcp_reply(struct dhcp_context *context, char * Line 361  size_t dhcp_reply(struct dhcp_context *context, char *
                       ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));                        ntohl(mess->xid), daemon->namebuff, inet_ntoa(context_tmp->end));
         }          }
     }      }
     
     /* 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. 
        If no data given, existance of the option is enough. This code handles 
        rfc3925 V-I classes too. */
     for (o = daemon->dhcp_match; o; o = o->next)
       {
         unsigned int len, elen, match = 0;
         size_t offset, o2;
   
         if (o->flags & DHOPT_RFC3925)
           {
             if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))
               continue;
             
             for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)
               {
                 len = option_uint(opt, offset + 4 , 1);
                 /* Need to take care that bad data can't run us off the end of the packet */
                 if ((offset + len + 5 <= (option_len(opt))) &&
                     (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))
                   for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)
                     { 
                       elen = option_uint(opt, o2, 1);
                       if ((o2 + elen + 1 <= option_len(opt)) &&
                           (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))
                         break;
                     }
                 if (match) 
                   break;
               }     
           }
         else
           {
             if (!(opt = option_find(mess, sz, o->opt, 1)))
               continue;
             
             match = match_bytes(o, option_ptr(opt, 0), option_len(opt));
           } 
   
         if (match)
           {
             o->netid->next = netid;
             netid = o->netid;
           }
       }
           
     /* user-class options are, according to RFC3004, supposed to contain
        a set of counted strings. Here we check that this is so (by seeing
        if the counts are consistent with the overall option length) and if
        so zero the counts so that we don't get spurious matches between 
        the vendor string and the counts. If the lengths don't add up, we
        assume that the option is a single string and non RFC3004 compliant 
        and just do the substring match. dhclient provides these broken options.
        The code, later, which sends user-class data to the lease-change script
        relies on the transformation done here.
     */
   
     if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))
       {
         unsigned char *ucp = option_ptr(opt, 0);
         int tmp, j;
         for (j = 0; j < option_len(opt); j += ucp[j] + 1);
         if (j == option_len(opt))
           for (j = 0; j < option_len(opt); j = tmp)
             {
               tmp = j + ucp[j] + 1;
               ucp[j] = 0;
             }
       }
       
     for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)
       {
         int mopt;
         
         if (vendor->match_type == MATCH_VENDOR)
           mopt = OPTION_VENDOR_ID;
         else if (vendor->match_type == MATCH_USER)
           mopt = OPTION_USER_CLASS; 
         else
           continue;
   
         if ((opt = option_find(mess, sz, mopt, 1)))
           {
             int i;
             for (i = 0; i <= (option_len(opt) - vendor->len); i++)
               if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)
                 {
                   vendor->netid.next = netid;
                   netid = &vendor->netid;
                   break;
                 }
           }
       }
   
     /* mark vendor-encapsulated options which match the client-supplied vendor class,
        save client-supplied vendor class */
     if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))
       {
         memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));
         vendor_class_len = option_len(opt);
       }
     match_vendor_opts(opt, daemon->dhcp_opts);
     
     if (option_bool(OPT_LOG_OPTS))
       {
         if (sanitise(opt, daemon->namebuff))
           my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);
         if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))
           my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);
       }
   
   mess->op = BOOTREPLY;    mess->op = BOOTREPLY;
       
   config = find_config(daemon->dhcp_conf, context, clid, clid_len,     config = find_config(daemon->dhcp_conf, context, clid, clid_len, 
Line 493  size_t dhcp_reply(struct dhcp_context *context, char * Line 611  size_t dhcp_reply(struct dhcp_context *context, char *
               lease_set_interface(lease, int_index, now);                lease_set_interface(lease, int_index, now);
                               
               clear_packet(mess, end);                clear_packet(mess, end);
               match_vendor_opts(NULL, daemon->dhcp_opts); /* clear flags */  
               do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr),                 do_options(context, mess, end, NULL, hostname, get_domain(mess->yiaddr), 
                         netid, subnet_addr, 0, 0, -1, NULL, 0, now);                         netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, now, 0xffffffff, 0);
             }              }
         }          }
               
      log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, message, mess->xid);      log_packet("BOOTP", logaddr, mess->chaddr, mess->hlen, iface_name, NULL, message, mess->xid);
               
       return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);        return message ? 0 : dhcp_packet_size(mess, agent_id, real_end);
     }      }
               
  if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 4)))  if ((opt = option_find(mess, sz, OPTION_CLIENT_FQDN, 3)))
     {      {
       /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */        /* http://tools.ietf.org/wg/dhc/draft-ietf-dhc-fqdn-option/draft-ietf-dhc-fqdn-option-10.txt */
       int len = option_len(opt);        int len = option_len(opt);
Line 534  size_t dhcp_reply(struct dhcp_context *context, char * Line 651  size_t dhcp_reply(struct dhcp_context *context, char *
         }          }
               
       if (fqdn_flags & 0x04)        if (fqdn_flags & 0x04)
        while (*op != 0 && ((op + (*op) + 1) - pp) < len)        while (*op != 0 && ((op + (*op)) - pp) < len)
           {            {
             memcpy(pq, op+1, *op);              memcpy(pq, op+1, *op);
             pq += *op;              pq += *op;
Line 622  size_t dhcp_reply(struct dhcp_context *context, char * Line 739  size_t dhcp_reply(struct dhcp_context *context, char *
         }          }
     }      }
       
   /* 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.   
      If no data given, existance of the option is enough. This code handles   
      rfc3925 V-I classes too. */  
   for (o = daemon->dhcp_match; o; o = o->next)  
     {  
       unsigned int len, elen, match = 0;  
       size_t offset, o2;  
   
       if (o->flags & DHOPT_RFC3925)  
         {  
           if (!(opt = option_find(mess, sz, OPTION_VENDOR_IDENT, 5)))  
             continue;  
             
           for (offset = 0; offset < (option_len(opt) - 5u); offset += len + 5)  
             {  
               len = option_uint(opt, offset + 4 , 1);  
               /* Need to take care that bad data can't run us off the end of the packet */  
               if ((offset + len + 5 <= (option_len(opt))) &&  
                   (option_uint(opt, offset, 4) == (unsigned int)o->u.encap))  
                 for (o2 = offset + 5; o2 < offset + len + 5; o2 += elen + 1)  
                   {   
                     elen = option_uint(opt, o2, 1);  
                     if ((o2 + elen + 1 <= option_len(opt)) &&  
                         (match = match_bytes(o, option_ptr(opt, o2 + 1), elen)))  
                       break;  
                   }  
               if (match)   
                 break;  
             }       
         }  
       else  
         {  
           if (!(opt = option_find(mess, sz, o->opt, 1)))  
             continue;  
             
           match = match_bytes(o, option_ptr(opt, 0), option_len(opt));  
         }   
   
       if (match)  
         {  
           o->netid->next = netid;  
           netid = o->netid;  
         }  
     }  
           
   /* user-class options are, according to RFC3004, supposed to contain  
      a set of counted strings. Here we check that this is so (by seeing  
      if the counts are consistent with the overall option length) and if  
      so zero the counts so that we don't get spurious matches between   
      the vendor string and the counts. If the lengths don't add up, we  
      assume that the option is a single string and non RFC3004 compliant   
      and just do the substring match. dhclient provides these broken options.  
      The code, later, which sends user-class data to the lease-change script  
      relies on the transformation done here.  
   */  
   
   if ((opt = option_find(mess, sz, OPTION_USER_CLASS, 1)))  
     {  
       unsigned char *ucp = option_ptr(opt, 0);  
       int tmp, j;  
       for (j = 0; j < option_len(opt); j += ucp[j] + 1);  
       if (j == option_len(opt))  
         for (j = 0; j < option_len(opt); j = tmp)  
           {  
             tmp = j + ucp[j] + 1;  
             ucp[j] = 0;  
           }  
     }  
       
   for (vendor = daemon->dhcp_vendors; vendor; vendor = vendor->next)  
     {  
       int mopt;  
         
       if (vendor->match_type == MATCH_VENDOR)  
         mopt = OPTION_VENDOR_ID;  
       else if (vendor->match_type == MATCH_USER)  
         mopt = OPTION_USER_CLASS;   
       else  
         continue;  
   
       if ((opt = option_find(mess, sz, mopt, 1)))  
         {  
           int i;  
           for (i = 0; i <= (option_len(opt) - vendor->len); i++)  
             if (memcmp(vendor->data, option_ptr(opt, i), vendor->len) == 0)  
               {  
                 vendor->netid.next = netid;  
                 netid = &vendor->netid;  
                 break;  
               }  
         }  
     }  
   
   /* mark vendor-encapsulated options which match the client-supplied vendor class,  
      save client-supplied vendor class */  
   if ((opt = option_find(mess, sz, OPTION_VENDOR_ID, 1)))  
     {  
       memcpy(daemon->dhcp_buff3, option_ptr(opt, 0), option_len(opt));  
       vendor_class_len = option_len(opt);  
     }  
   match_vendor_opts(opt, daemon->dhcp_opts);  
     
   if (option_bool(OPT_LOG_OPTS))  
     {  
       if (sanitise(opt, daemon->namebuff))  
         my_syslog(MS_DHCP | LOG_INFO, _("%u vendor class: %s"), ntohl(mess->xid), daemon->namebuff);  
       if (sanitise(option_find(mess, sz, OPTION_USER_CLASS, 1), daemon->namebuff))  
         my_syslog(MS_DHCP | LOG_INFO, _("%u user class: %s"), ntohl(mess->xid), daemon->namebuff);  
     }  
   
   tagif_netid = run_tag_if(netid);    tagif_netid = run_tag_if(netid);
  
   /* 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 */
   for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)    for (id_list = daemon->dhcp_ignore; id_list; id_list = id_list->next)
     if (match_netid(id_list->list, tagif_netid, 0))      if (match_netid(id_list->list, tagif_netid, 0))
Line 799  size_t dhcp_reply(struct dhcp_context *context, char * Line 805  size_t dhcp_reply(struct dhcp_context *context, char *
             if (service->type == type)              if (service->type == type)
               break;                break;
                       
          if (!service || !service->basename)          for (; context; context = context->current)
            return 0;            if (match_netid(context->filter, tagif_netid, 1) &&
                 is_same_net(mess->ciaddr, context->start, context->netmask))
               break;
                       
             if (!service || !service->basename || !context)
               return 0;
                     
           clear_packet(mess, end);            clear_packet(mess, end);
                       
           mess->yiaddr = mess->ciaddr;            mess->yiaddr = mess->ciaddr;
Line 813  size_t dhcp_reply(struct dhcp_context *context, char * Line 824  size_t dhcp_reply(struct dhcp_context *context, char *
           else            else
             mess->siaddr = context->local;               mess->siaddr = context->local; 
                       
          snprintf((char *)mess->file, sizeof(mess->file), "%s.%d", service->basename, layer);          snprintf((char *)mess->file, sizeof(mess->file), 
                    strchr(service->basename, '.') ? "%s" :"%s.%d", 
                    service->basename, layer);
           
           option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);            option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
           option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));            option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));
           pxe_misc(mess, end, uuid);            pxe_misc(mess, end, uuid);
Line 827  size_t dhcp_reply(struct dhcp_context *context, char * Line 841  size_t dhcp_reply(struct dhcp_context *context, char *
           opt71.next = daemon->dhcp_opts;            opt71.next = daemon->dhcp_opts;
           do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);            do_encap_opts(&opt71, OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
                       
          log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, mess->xid);          log_packet("PXE", &mess->yiaddr, emac, emac_len, iface_name, (char *)mess->file, NULL, mess->xid);
           log_tags(tagif_netid, ntohl(mess->xid));            log_tags(tagif_netid, ntohl(mess->xid));
           return dhcp_packet_size(mess, agent_id, real_end);                  return dhcp_packet_size(mess, agent_id, real_end);      
         }          }
Line 840  size_t dhcp_reply(struct dhcp_context *context, char * Line 854  size_t dhcp_reply(struct dhcp_context *context, char *
           if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))            if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
             {              {
               struct dhcp_context *tmp;                struct dhcp_context *tmp;
                 int workaround = 0;
                               
               for (tmp = context; tmp; tmp = tmp->current)                for (tmp = context; tmp; tmp = tmp->current)
                 if ((tmp->flags & CONTEXT_PROXY) &&                  if ((tmp->flags & CONTEXT_PROXY) &&
Line 848  size_t dhcp_reply(struct dhcp_context *context, char * Line 863  size_t dhcp_reply(struct dhcp_context *context, char *
                               
               if (tmp)                if (tmp)
                 {                  {
                  struct dhcp_boot *boot = find_boot(tagif_netid);                  struct dhcp_boot *boot;
                                  int redirect4011 = 0;
 
                   if (tmp->netid.net)
                     {
                       tmp->netid.next = netid;
                       tagif_netid = run_tag_if(&tmp->netid);
                     }
                   
                   boot = find_boot(tagif_netid);
                   
                   mess->yiaddr.s_addr = 0;                    mess->yiaddr.s_addr = 0;
                   if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)                    if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
                     {                      {
Line 859  size_t dhcp_reply(struct dhcp_context *context, char * Line 883  size_t dhcp_reply(struct dhcp_context *context, char *
                                       
                   clear_packet(mess, end);                    clear_packet(mess, end);
                                       
                  /* Provide the bootfile here, for gPXE, and in case we have no menu items                  /* Redirect EFI clients to port 4011 */
                     and set discovery_control = 8 */                  if (pxearch >= 6)
                  if (boot) 
                     {                      {
                         redirect4011 = 1;
                         mess->siaddr = tmp->local;
                       }
                     
                     /* Returns true if only one matching service is available. On port 4011, 
                        it also inserts the boot file and server name. */
                     workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now, pxe);
                     
                     if (!workaround && boot)
                       {
                         /* Provide the bootfile here, for gPXE, and in case we have no menu items
                            and set discovery_control = 8 */
                       if (boot->next_server.s_addr)                         if (boot->next_server.s_addr) 
                         mess->siaddr = boot->next_server;                          mess->siaddr = boot->next_server;
                       else if (boot->tftp_sname)                         else if (boot->tftp_sname) 
Line 874  size_t dhcp_reply(struct dhcp_context *context, char * Line 909  size_t dhcp_reply(struct dhcp_context *context, char *
                                       
                   option_put(mess, end, OPTION_MESSAGE_TYPE, 1,                     option_put(mess, end, OPTION_MESSAGE_TYPE, 1, 
                              mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);                               mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
                  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(context->local.s_addr));                  option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->local.s_addr));
                   pxe_misc(mess, end, uuid);                    pxe_misc(mess, end, uuid);
                   prune_vendor_opts(tagif_netid);                    prune_vendor_opts(tagif_netid);
                  do_encap_opts(pxe_opts(pxearch, tagif_netid, context->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);                  if ((pxe && !workaround) || !redirect4011)
                                      do_encap_opts(pxe_opts(pxearch, tagif_netid, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0);
                  log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", mess->xid);            
                   log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
                   log_tags(tagif_netid, ntohl(mess->xid));                    log_tags(tagif_netid, ntohl(mess->xid));
                   return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);                             return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);         
                 }                  }
Line 911  size_t dhcp_reply(struct dhcp_context *context, char * Line 947  size_t dhcp_reply(struct dhcp_context *context, char *
       if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))        if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP, INADDRSZ)))
         return 0;          return 0;
               
      log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, daemon->dhcp_buff, mess->xid);      log_packet("DHCPDECLINE", option_ptr(opt, 0), emac, emac_len, iface_name, NULL, daemon->dhcp_buff, mess->xid);
               
       if (lease && lease->addr.s_addr == option_addr(opt).s_addr)        if (lease && lease->addr.s_addr == option_addr(opt).s_addr)
         lease_prune(lease, now);          lease_prune(lease, now);
Line 943  size_t dhcp_reply(struct dhcp_context *context, char * Line 979  size_t dhcp_reply(struct dhcp_context *context, char *
       else        else
         message = _("unknown lease");          message = _("unknown lease");
   
      log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);      log_packet("DHCPRELEASE", &mess->ciaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
                   
       return 0;        return 0;
               
     case DHCPDISCOVER:      case DHCPDISCOVER:
       if (ignore || have_config(config, CONFIG_DISABLE))        if (ignore || have_config(config, CONFIG_DISABLE))
         {          {
             if (option_bool(OPT_QUIET_DHCP))
               return 0;
           message = _("ignored");            message = _("ignored");
           opt = NULL;            opt = NULL;
         }          }
Line 1007  size_t dhcp_reply(struct dhcp_context *context, char * Line 1045  size_t dhcp_reply(struct dhcp_context *context, char *
             message = _("no address available");                    message = _("no address available");      
         }          }
               
      log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, message, mess->xid);       log_packet("DHCPDISCOVER", opt ? option_ptr(opt, 0) : NULL, emac, emac_len, iface_name, NULL, message, mess->xid); 
   
       if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))        if (message || !(context = narrow_context(context, mess->yiaddr, tagif_netid)))
         return 0;          return 0;
Line 1020  size_t dhcp_reply(struct dhcp_context *context, char * Line 1058  size_t dhcp_reply(struct dhcp_context *context, char *
   
       log_tags(tagif_netid, ntohl(mess->xid));        log_tags(tagif_netid, ntohl(mess->xid));
               
      log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);      log_packet("DHCPOFFER" , &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
               
       time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));        time = calc_time(context, config, option_find(mess, sz, OPTION_LEASE_TIME, 4));
       clear_packet(mess, end);        clear_packet(mess, end);
Line 1028  size_t dhcp_reply(struct dhcp_context *context, char * Line 1066  size_t dhcp_reply(struct dhcp_context *context, char *
       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));        option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
       option_put(mess, end, OPTION_LEASE_TIME, 4, time);        option_put(mess, end, OPTION_LEASE_TIME, 4, time);
       /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */        /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. */
       if (time != 0xffffffff)  
         {  
           option_put(mess, end, OPTION_T1, 4, (time/2));  
           option_put(mess, end, OPTION_T2, 4, (time*7)/8);  
         }  
       do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr),         do_options(context, mess, end, req_options, offer_hostname, get_domain(mess->yiaddr), 
                 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);                 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
               
       return dhcp_packet_size(mess, agent_id, real_end);        return dhcp_packet_size(mess, agent_id, real_end);
               
Line 1072  size_t dhcp_reply(struct dhcp_context *context, char * Line 1105  size_t dhcp_reply(struct dhcp_context *context, char *
                          Have to set override to make sure we echo back the correct server-id */                           Have to set override to make sure we echo back the correct server-id */
                       struct irec *intr;                        struct irec *intr;
                                               
                      enumerate_interfaces();                      enumerate_interfaces(0);
   
                       for (intr = daemon->interfaces; intr; intr = intr->next)                        for (intr = daemon->interfaces; intr; intr = intr->next)
                         if (intr->addr.sa.sa_family == AF_INET &&                          if (intr->addr.sa.sa_family == AF_INET &&
Line 1136  size_t dhcp_reply(struct dhcp_context *context, char * Line 1169  size_t dhcp_reply(struct dhcp_context *context, char *
           mess->yiaddr = mess->ciaddr;            mess->yiaddr = mess->ciaddr;
         }          }
               
      log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, mess->xid);      log_packet("DHCPREQUEST", &mess->yiaddr, emac, emac_len, iface_name, NULL, NULL, mess->xid);
     
       if (!message)        if (!message)
         {          {
Line 1208  size_t dhcp_reply(struct dhcp_context *context, char * Line 1241  size_t dhcp_reply(struct dhcp_context *context, char *
   
       if (message)        if (message)
         {          {
          log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, message, mess->xid);          log_packet("DHCPNAK", &mess->yiaddr, emac, emac_len, iface_name, NULL, message, mess->xid);
                       
           mess->yiaddr.s_addr = 0;            mess->yiaddr.s_addr = 0;
           clear_packet(mess, end);            clear_packet(mess, end);
Line 1292  size_t dhcp_reply(struct dhcp_context *context, char * Line 1325  size_t dhcp_reply(struct dhcp_context *context, char *
                       /* If the user-class option started as counted strings, the first byte will be zero. */                        /* If the user-class option started as counted strings, the first byte will be zero. */
                       if (len != 0 && ucp[0] == 0)                        if (len != 0 && ucp[0] == 0)
                         ucp++, len--;                          ucp++, len--;
                      lease_add_extradata(lease, ucp, len, 0);                      lease_add_extradata(lease, ucp, len, -1);
                     }                      }
                 }                  }
 #endif  #endif
Line 1347  size_t dhcp_reply(struct dhcp_context *context, char * Line 1380  size_t dhcp_reply(struct dhcp_context *context, char *
           else            else
             override = lease->override;              override = lease->override;
   
          log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, mess->xid);            log_packet("DHCPACK", &mess->yiaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);  
                       
           clear_packet(mess, end);            clear_packet(mess, end);
           option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);            option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
           option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));            option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
           option_put(mess, end, OPTION_LEASE_TIME, 4, time);            option_put(mess, end, OPTION_LEASE_TIME, 4, time);
           if (time != 0xffffffff)  
             {  
               while (fuzz > (time/16))  
                 fuzz = fuzz/2;   
               option_put(mess, end, OPTION_T1, 4, (time/2) - fuzz);  
               option_put(mess, end, OPTION_T2, 4, ((time/8)*7) - fuzz);  
             }  
           do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr),             do_options(context, mess, end, req_options, hostname, get_domain(mess->yiaddr), 
                     netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);                     netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, time, fuzz);
         }          }
   
       return dhcp_packet_size(mess, agent_id, real_end);         return dhcp_packet_size(mess, agent_id, real_end); 
Line 1370  size_t dhcp_reply(struct dhcp_context *context, char * Line 1396  size_t dhcp_reply(struct dhcp_context *context, char *
       if (ignore || have_config(config, CONFIG_DISABLE))        if (ignore || have_config(config, CONFIG_DISABLE))
         message = _("ignored");          message = _("ignored");
               
      log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, mess->xid);      log_packet("DHCPINFORM", &mess->ciaddr, emac, emac_len, iface_name, message, NULL, mess->xid);
             
       if (message || mess->ciaddr.s_addr == 0)        if (message || mess->ciaddr.s_addr == 0)
         return 0;          return 0;
Line 1385  size_t dhcp_reply(struct dhcp_context *context, char * Line 1411  size_t dhcp_reply(struct dhcp_context *context, char *
           lease->hostname)            lease->hostname)
         hostname = lease->hostname;          hostname = lease->hostname;
               
      if (!hostname && (hostname = host_from_dns(mess->ciaddr)))      if (!hostname)
        domain = get_domain(mess->ciaddr);        hostname = host_from_dns(mess->ciaddr);
               
       if (context && context->netid.net)        if (context && context->netid.net)
         {          {
Line 1396  size_t dhcp_reply(struct dhcp_context *context, char * Line 1422  size_t dhcp_reply(struct dhcp_context *context, char *
   
       log_tags(tagif_netid, ntohl(mess->xid));        log_tags(tagif_netid, ntohl(mess->xid));
               
      log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, mess->xid);      log_packet("DHCPACK", &mess->ciaddr, emac, emac_len, iface_name, hostname, NULL, mess->xid);
               
       if (lease)        if (lease)
         {          {
Line 1410  size_t dhcp_reply(struct dhcp_context *context, char * Line 1436  size_t dhcp_reply(struct dhcp_context *context, char *
       clear_packet(mess, end);        clear_packet(mess, end);
       option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);        option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
       option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));        option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(server_id(context, override, fallback).s_addr));
           
       /* RFC 2131 says that DHCPINFORM shouldn't include lease-time parameters, but 
          we supply a utility which makes DHCPINFORM requests to get this information.
          Only include lease time if OPTION_LEASE_TIME is in the parameter request list,
          which won't be true for ordinary clients, but will be true for the 
          dhcp_lease_time utility. */
       if (lease && in_list(req_options, OPTION_LEASE_TIME))
         {
           if (lease->expires == 0)
             time = 0xffffffff;
           else
             time = (unsigned int)difftime(lease->expires, now);
           option_put(mess, end, OPTION_LEASE_TIME, 4, time);
         }
 
       do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),        do_options(context, mess, end, req_options, hostname, get_domain(mess->ciaddr),
                 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now);                 netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, vendor_class_len, now, 0xffffffff, 0);
               
       *is_inform = 1; /* handle reply differently */        *is_inform = 1; /* handle reply differently */
       return dhcp_packet_size(mess, agent_id, real_end);         return dhcp_packet_size(mess, agent_id, real_end); 
Line 1516  static void add_extradata_opt(struct dhcp_lease *lease Line 1556  static void add_extradata_opt(struct dhcp_lease *lease
 #endif  #endif
   
 static void log_packet(char *type, void *addr, unsigned char *ext_mac,   static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
                       int mac_len, char *interface, char *string, u32 xid)                       int mac_len, char *interface, char *string, char *err, u32 xid)
 {  {
   struct in_addr a;    struct in_addr a;
     
     if (!err && !option_bool(OPT_LOG_OPTS) && option_bool(OPT_QUIET_DHCP))
       return;
     
   /* addr may be misaligned */    /* addr may be misaligned */
   if (addr)    if (addr)
     memcpy(&a, addr, sizeof(a));      memcpy(&a, addr, sizeof(a));
Line 1527  static void log_packet(char *type, void *addr, unsigne Line 1570  static void log_packet(char *type, void *addr, unsigne
   print_mac(daemon->namebuff, ext_mac, mac_len);    print_mac(daemon->namebuff, ext_mac, mac_len);
       
   if(option_bool(OPT_LOG_OPTS))    if(option_bool(OPT_LOG_OPTS))
     my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s",     my_syslog(MS_DHCP | LOG_INFO, "%u %s(%s) %s%s%s %s%s",
                ntohl(xid),                  ntohl(xid), 
                type,                 type,
                interface,                  interface, 
                addr ? inet_ntoa(a) : "",                 addr ? inet_ntoa(a) : "",
                addr ? " " : "",                 addr ? " " : "",
                daemon->namebuff,                 daemon->namebuff,
               string ? string : "");               string ? string : "",
                err ? err : "");
   else    else
    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s",    my_syslog(MS_DHCP | LOG_INFO, "%s(%s) %s%s%s %s%s",
               type,                type,
               interface,                 interface, 
               addr ? inet_ntoa(a) : "",                addr ? inet_ntoa(a) : "",
               addr ? " " : "",                addr ? " " : "",
               daemon->namebuff,                daemon->namebuff,
              string ? string : "");              string ? string : "",
               err ? err : "");
 }  }
   
 static void log_options(unsigned char *start, u32 xid)  static void log_options(unsigned char *start, u32 xid)
Line 1819  static int do_opt(struct dhcp_opt *opt, unsigned char  Line 1864  static int do_opt(struct dhcp_opt *opt, unsigned char 
             }              }
         }          }
       else        else
        memcpy(p, opt->val, len);        /* empty string may be extended to "\0" by null_term */
         memcpy(p, opt->val ? opt->val : (unsigned char *)"", len);
     }        }  
   return len;    return len;
 }  }
Line 1946  static int prune_vendor_opts(struct dhcp_netid *netid) Line 1992  static int prune_vendor_opts(struct dhcp_netid *netid)
   return force;    return force;
 }  }
   
   
   /* Many UEFI PXE implementations have badly broken menu code.
      If there's exactly one relevant menu item, we abandon the menu system,
      and jamb the data direct into the DHCP file, siaddr and sname fields.
      Note that in this case, we have to assume that layer zero would be requested
      by the client PXE stack. */
   static int pxe_uefi_workaround(int pxe_arch, struct dhcp_netid *netid, struct dhcp_packet *mess, struct in_addr local, time_t now, int pxe)
   {
     struct pxe_service *service, *found;
   
     /* Only workaround UEFI archs. */
     if (pxe_arch < 6)
       return 0;
     
     for (found = NULL, service = daemon->pxe_services; service; service = service->next)
       if (pxe_arch == service->CSA && service->basename && match_netid(service->netid, netid, 1))
         {
           if (found)
             return 0; /* More than one relevant menu item */
             
           found = service;
         }
   
     if (!found)
       return 0; /* No relevant menu items. */
     
     if (!pxe)
        return 1;
     
     if (found->sname)
       {
         mess->siaddr = a_record_from_hosts(found->sname, now);
         snprintf((char *)mess->sname, sizeof(mess->sname), "%s", found->sname);
       }
     else 
       {
         if (found->server.s_addr != 0)
           mess->siaddr = found->server; 
         else
           mess->siaddr = local;
     
         inet_ntop(AF_INET, &mess->siaddr, (char *)mess->sname, INET_ADDRSTRLEN);
       }
     
     snprintf((char *)mess->file, sizeof(mess->file), 
              strchr(found->basename, '.') ? "%s" : "%s.0", found->basename);
     
     return 1;
   }
   
 static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)  static struct dhcp_opt *pxe_opts(int pxe_arch, struct dhcp_netid *netid, struct in_addr local, time_t now)
 {  {
 #define NUM_OPTS 4    #define NUM_OPTS 4  
Line 2103  static void do_options(struct dhcp_context *context, Line 2199  static void do_options(struct dhcp_context *context,
                        int null_term, int pxe_arch,                         int null_term, int pxe_arch,
                        unsigned char *uuid,                         unsigned char *uuid,
                        int vendor_class_len,                         int vendor_class_len,
                       time_t now)                       time_t now,
                        unsigned int lease_time,
                        unsigned short fuzz)
 {  {
   struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;    struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
   struct dhcp_boot *boot;    struct dhcp_boot *boot;
Line 2227  static void do_options(struct dhcp_context *context, Line 2325  static void do_options(struct dhcp_context *context,
   /* rfc3011 says this doesn't need to be in the requested options list. */    /* rfc3011 says this doesn't need to be in the requested options list. */
   if (subnet_addr.s_addr)    if (subnet_addr.s_addr)
     option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));      option_put(mess, end, OPTION_SUBNET_SELECT, INADDRSZ, ntohl(subnet_addr.s_addr));
     
   if (lease_time != 0xffffffff)
     { 
       unsigned int t1val = lease_time/2; 
       unsigned int t2val = (lease_time*7)/8;
       unsigned int hval;
       
       /* If set by user, sanity check, so not longer than lease. */
       if ((opt = option_find2(OPTION_T1)))
         {
           hval = ntohl(*((unsigned int *)opt->val));
           if (hval < lease_time && hval > 2)
             t1val = hval;
         }
 
        if ((opt = option_find2(OPTION_T2)))
         {
           hval = ntohl(*((unsigned int *)opt->val));
           if (hval < lease_time && hval > 2)
             t2val = hval;
         }
           
        /* ensure T1 is still < T2 */
        if (t2val <= t1val)
          t1val = t2val - 1; 
 
        while (fuzz > (t1val/8))
          fuzz = fuzz/2;
          
        t1val -= fuzz;
        t2val -= fuzz;
        
        option_put(mess, end, OPTION_T1, 4, t1val);
        option_put(mess, end, OPTION_T2, 4, t2val);
     }
 
   /* replies to DHCPINFORM may not have a valid context */    /* replies to DHCPINFORM may not have a valid context */
   if (context)    if (context)
     {      {
Line 2275  static void do_options(struct dhcp_context *context, Line 2408  static void do_options(struct dhcp_context *context,
   
           if (domain)            if (domain)
             len += strlen(domain) + 1;              len += strlen(domain) + 1;
                    else if (fqdn_flags & 0x04)
             len--;
 
           if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))            if ((p = free_space(mess, end, OPTION_CLIENT_FQDN, len)))
             {              {
               *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */                 *(p++) = fqdn_flags & 0x0f; /* MBZ bits to zero */ 
Line 2286  static void do_options(struct dhcp_context *context, Line 2421  static void do_options(struct dhcp_context *context,
                 {                  {
                   p = do_rfc1035_name(p, hostname);                    p = do_rfc1035_name(p, hostname);
                   if (domain)                    if (domain)
                    p = do_rfc1035_name(p, domain);                    {
                  *p++ = 0;                      p = do_rfc1035_name(p, domain);
                       *p++ = 0;
                     }
                 }                  }
               else                else
                 {                  {
Line 2318  static void do_options(struct dhcp_context *context, Line 2455  static void do_options(struct dhcp_context *context,
       if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))        if (!(opt->flags & DHOPT_FORCE) && !in_list(req_options, optno))
         continue;          continue;
               
      /* prohibit some used-internally options */      /* prohibit some used-internally options. T1 and T2 already handled. */
       if (optno == OPTION_CLIENT_FQDN ||        if (optno == OPTION_CLIENT_FQDN ||
           optno == OPTION_MAXMESSAGE ||            optno == OPTION_MAXMESSAGE ||
           optno == OPTION_OVERLOAD ||            optno == OPTION_OVERLOAD ||
           optno == OPTION_PAD ||            optno == OPTION_PAD ||
          optno == OPTION_END)          optno == OPTION_END ||
           optno == OPTION_T1 ||
           optno == OPTION_T2)
         continue;          continue;
   
       if (optno == OPTION_SNAME && done_server)        if (optno == OPTION_SNAME && done_server)
Line 2437  static void do_options(struct dhcp_context *context, Line 2576  static void do_options(struct dhcp_context *context,
   if (context && pxe_arch != -1)    if (context && pxe_arch != -1)
     {      {
       pxe_misc(mess, end, uuid);        pxe_misc(mess, end, uuid);
      config_opts = pxe_opts(pxe_arch, tagif, context->local, now);      if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
         config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
     }      }
   
   if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&    if ((force_encap || in_list(req_options, OPTION_VENDOR_CLASS_OPT)) &&

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


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