Patch applied.

http://thekelleys.org.uk/gitweb/?p=dnsmasq.git;a=commit;h=4ded96209e8346711f9d0b9e13a835d42835853d

I've manually reviewed this, and done very minimal testing, please test
it to make sure it's OK.

Simon.



On 04/12/2020 02:17, Wang Shanker wrote:
> From 606d638918edb0e0ec07fe27eb68d06fb5ebd981 Mon Sep 17 00:00:00 2001
> From: Miao Wang <shankerwangm...@gmail.com>
> Date: Fri, 4 Dec 2020 09:59:37 +0800
> Subject: [PATCH v2] pxe: support pxe clients with custom vendor-class
> 
> According to UEFI[1] and PXE[2] specs, PXE clients are required to have
> `PXEClient` identfier in the vendor-class field of DHCP requests, and
> PXE servers should also include that identifier in their responses.
> However, the firmware of servers from a few vendors[3] are customized to
> include a different identifier. This patch adds an option named
> `dhcp-pxe-vendor` to provide a list of such identifiers. The identifier
> used in responses sent from dnsmasq is identical to that in the coresponding
> request.
> 
> [1]: 
> https://uefi.org/sites/default/files/resources/UEFI%20Spec%202.8B%20May%202020.pdf
> [2]: http://www.pix.net/software/pxeboot/archive/pxespec.pdf
> [3]: For instance, TaiShan servers from Huawei, which are Arm64-based,
>        send `HW-Client` in PXE requests up to now.
> 
> Signed-off-by: Miao Wang <shankerwangm...@gmail.com>
> ---
>  man/dnsmasq.8 | 16 +++++++++
>  src/dnsmasq.h |  9 +++++
>  src/option.c  | 27 ++++++++++++--
>  src/rfc2131.c | 97 +++++++++++++++++++++++++++++++++++++--------------
>  4 files changed, 121 insertions(+), 28 deletions(-)
> 
> diff --git a/man/dnsmasq.8 b/man/dnsmasq.8
> index 7b0e106..7c6b405 100644
> --- a/man/dnsmasq.8
> +++ b/man/dnsmasq.8
> @@ -1480,6 +1480,22 @@ to allow netbooting. This mode is enabled using the
>  .B proxy
>  keyword in
>  .B --dhcp-range.
> +.TP
> +.B --dhcp-pxe-vendor=<vendor>[,...]
> +According to UEFI and PXE specifications, DHCP packets between PXE clients 
> and
> +proxy PXE servers should have 
> +.I PXEClient 
> +in their vendor-class field. However, the firmware of computers from a few
> +vendors is customized to carry a different identifier in that field. This 
> option
> +is used to consider such identifiers valid for identifying PXE clients. For 
> +instance
> +
> +.B --dhcp-pxe-vendor=PXEClient,HW-Client
> +
> +will enable dnsmasq to also provide proxy PXE service to those PXE clients 
> with
> +.I HW-Client
> +in as their identifier.
> +>>>>>>> 907def3... pxe: support pxe clients with custom vendor-class
>  .TP  
>  .B \-X, --dhcp-lease-max=<number>
>  Limits dnsmasq to the specified maximum number of DHCP leases. The
> diff --git a/src/dnsmasq.h b/src/dnsmasq.h
> index 4220798..4d78c37 100644
> --- a/src/dnsmasq.h
> +++ b/src/dnsmasq.h
> @@ -829,6 +829,7 @@ struct dhcp_opt {
>  #define DHOPT_RFC3925         2048
>  #define DHOPT_TAGOK           4096
>  #define DHOPT_ADDR6           8192
> +#define DHOPT_VENDOR_PXE     16384
>  
>  struct dhcp_boot {
>    char *file, *sname, *tftp_sname;
> @@ -852,6 +853,8 @@ struct pxe_service {
>    struct pxe_service *next;
>  };
>  
> +#define DHCP_PXE_DEF_VENDOR      "PXEClient"
> +
>  #define MATCH_VENDOR     1
>  #define MATCH_USER       2
>  #define MATCH_CIRCUIT    3
> @@ -867,6 +870,11 @@ struct dhcp_vendor {
>    struct dhcp_vendor *next;
>  };
>  
> +struct dhcp_pxe_vendor {
> +  char *data;
> +  struct dhcp_pxe_vendor *next;
> +};
> +
>  struct dhcp_mac {
>    unsigned int mask;
>    int hwaddr_len, hwaddr_type;
> @@ -1040,6 +1048,7 @@ extern struct daemon {
>    struct dhcp_config *dhcp_conf;
>    struct dhcp_opt *dhcp_opts, *dhcp_match, *dhcp_opts6, *dhcp_match6;
>    struct dhcp_match_name *dhcp_name_match;
> +  struct dhcp_pxe_vendor *dhcp_pxe_vendors;
>    struct dhcp_vendor *dhcp_vendors;
>    struct dhcp_mac *dhcp_macs;
>    struct dhcp_boot *boot_config;
> diff --git a/src/option.c b/src/option.c
> index dbe5f90..316d9c8 100644
> --- a/src/option.c
> +++ b/src/option.c
> @@ -167,6 +167,7 @@ struct myoption {
>  #define LOPT_IGNORE_CLID   358
>  #define LOPT_SINGLE_PORT   359
>  #define LOPT_SCRIPT_TIME   360
> +#define LOPT_PXE_VENDOR    361
>   
>  #ifdef HAVE_GETOPT_LONG
>  static const struct option opts[] =  
> @@ -270,6 +271,7 @@ static const struct myoption opts[] =
>      { "dhcp-circuitid", 1, 0, LOPT_CIRCUIT },
>      { "dhcp-remoteid", 1, 0, LOPT_REMOTE },
>      { "dhcp-subscrid", 1, 0, LOPT_SUBSCR },
> +    { "dhcp-pxe-vendor", 1, 0, LOPT_PXE_VENDOR },
>      { "interface-name", 1, 0, LOPT_INTNAME },
>      { "dhcp-hostsfile", 1, 0, LOPT_DHCP_HOST },
>      { "dhcp-optsfile", 1, 0, LOPT_DHCP_OPTS },
> @@ -383,6 +385,7 @@ static struct {
>    { LOPT_CIRCUIT, ARG_DUP, "set:<tag>,<circuit>", gettext_noop("Map RFC3046 
> circuit-id to tag."), NULL },
>    { LOPT_REMOTE, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3046 
> remote-id to tag."), NULL },
>    { LOPT_SUBSCR, ARG_DUP, "set:<tag>,<remote>", gettext_noop("Map RFC3993 
> subscriber-id to tag."), NULL },
> +  { LOPT_PXE_VENDOR, ARG_DUP, "<vendor>[,...]", gettext_noop("Specify vendor 
> class to match for PXE requests."), NULL },
>    { 'J', ARG_DUP, "tag:<tag>...", gettext_noop("Don't do DHCP for hosts with 
> tag set."), NULL },
>    { LOPT_BROADCAST, ARG_DUP, "[=tag:<tag>...]", gettext_noop("Force 
> broadcast replies for hosts with tag set."), NULL }, 
>    { 'k', OPT_NO_FORK, NULL, gettext_noop("Do NOT fork into the background, 
> do NOT run in debug mode."), NULL },
> @@ -3672,8 +3675,8 @@ static int one_opt(int option, char *arg, char *errstr, 
> char *gen_err, int comma
>            new->val = opt_malloc(new->len);
>            memcpy(new->val + 1, arg, new->len - 1);
>            
> -          new->u.vendor_class = (unsigned char *)"PXEClient";
> -          new->flags = DHOPT_VENDOR;
> +          new->u.vendor_class = NULL;
> +          new->flags = DHOPT_VENDOR | DHOPT_VENDOR_PXE;
>            
>            if (comma && atoi_check(comma, &timeout))
>              *(new->val) = timeout;
> @@ -3935,6 +3938,19 @@ static int one_opt(int option, char *arg, char 
> *errstr, char *gen_err, int comma
>       new->next = daemon->override_relays;
>       daemon->override_relays = new;
>       arg = comma;
> +     }
> +       break;
> +
> +    case LOPT_PXE_VENDOR: /* --dhcp-pxe-vendor */
> +      {
> +        while (arg) {
> +       struct dhcp_pxe_vendor *new = opt_malloc(sizeof(struct 
> dhcp_pxe_vendor));
> +       comma = split(arg);
> +          new->data = opt_string_alloc(arg);
> +       new->next = daemon->dhcp_pxe_vendors;
> +       daemon->dhcp_pxe_vendors = new;
> +       arg = comma;
> +     }
>        }
>        break;
>  
> @@ -5212,6 +5228,13 @@ void read_opts(int argc, char **argv, char 
> *compile_opts)
>        strcat(buff, daemon->authserver);
>        daemon->hostmaster = opt_string_alloc(buff);
>      }
> +
> +  if (!daemon->dhcp_pxe_vendors)
> +    {
> +      daemon->dhcp_pxe_vendors = opt_malloc(sizeof(struct dhcp_pxe_vendor));
> +      daemon->dhcp_pxe_vendors->data = opt_string_alloc(DHCP_PXE_DEF_VENDOR);
> +      daemon->dhcp_pxe_vendors->next = NULL;
> +    }
>    
>    /* only one of these need be specified: the other defaults to the 
> host-name */
>    if (option_bool(OPT_LOCALMX) || daemon->mxnames || daemon->mxtarget)
> diff --git a/src/rfc2131.c b/src/rfc2131.c
> index fc54aab..d678068 100644
> --- a/src/rfc2131.c
> +++ b/src/rfc2131.c
> @@ -30,7 +30,7 @@ static struct in_addr server_id(struct dhcp_context 
> *context, struct in_addr ove
>  static unsigned int calc_time(struct dhcp_context *context, struct 
> dhcp_config *config, unsigned char *opt);
>  static void option_put(struct dhcp_packet *mess, unsigned char *end, int 
> opt, int len, unsigned int val);
>  static void option_put_string(struct dhcp_packet *mess, unsigned char *end, 
> -                           int opt, char *string, int null_term);
> +                           int opt, const char *string, int null_term);
>  static struct in_addr option_addr(unsigned char *opt);
>  static unsigned int option_uint(unsigned char *opt, int offset, int size);
>  static void log_packet(char *type, void *addr, unsigned char *ext_mac, 
> @@ -54,17 +54,19 @@ static void do_options(struct dhcp_context *context,
>                      int vendor_class_len,
>                      time_t now,
>                      unsigned int lease_time,
> -                    unsigned short fuzz);
> +                    unsigned short fuzz,
> +                    const char *pxevendor);
>  
>  
>  static void match_vendor_opts(unsigned char *opt, struct dhcp_opt *dopt); 
>  static int do_encap_opts(struct dhcp_opt *opt, int encap, int flag, struct 
> dhcp_packet *mess, unsigned char *end, int null_term);
> -static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned 
> char *uuid);
> +static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned 
> char *uuid, const char *pxevendor);
>  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);
>  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);
>  static void apply_delay(u32 xid, time_t recvtime, struct dhcp_netid *netid);
> +static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char 
> **pxe_vendor);
>  
>  size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int 
> int_index,
>                 size_t sz, time_t now, int unicast_dest, int loopback,
> @@ -76,6 +78,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
> *iface_name, int int_index,
>    struct dhcp_mac *mac;
>    struct dhcp_netid_list *id_list;
>    int clid_len = 0, ignore = 0, do_classes = 0, rapid_commit = 0, selecting 
> = 0, pxearch = -1;
> +  const char *pxevendor = NULL;
>    struct dhcp_packet *mess = (struct dhcp_packet 
> *)daemon->dhcp_packet.iov_base;
>    unsigned char *end = (unsigned char *)(mess + 1); 
>    unsigned char *real_end = (unsigned char *)(mess + 1); 
> @@ -647,7 +650,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
> *iface_name, int int_index,
>             
>             clear_packet(mess, end);
>             do_options(context, mess, end, NULL, hostname, 
> get_domain(mess->yiaddr), 
> -                      netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, 
> now, 0xffffffff, 0);
> +                      netid, subnet_addr, 0, 0, -1, NULL, vendor_class_len, 
> now, 0xffffffff, 0, NULL);
>           }
>       }
>        
> @@ -835,9 +838,8 @@ size_t dhcp_reply(struct dhcp_context *context, char 
> *iface_name, int int_index,
>      clid = NULL;
>            
>    /* Check if client is PXE client. */
> -  if (daemon->enable_pxe && 
> -      (opt = option_find(mess, sz, OPTION_VENDOR_ID, 9)) && 
> -      strncmp(option_ptr(opt, 0), "PXEClient", 9) == 0)
> +  if (daemon->enable_pxe &&
> +      is_pxe_client(mess, sz, &pxevendor))
>      {
>        if ((opt = option_find(mess, sz, OPTION_PXE_UUID, 17)))
>       {
> @@ -899,7 +901,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
> *iface_name, int int_index,
>         
>         option_put(mess, end, OPTION_MESSAGE_TYPE, 1, DHCPACK);
>         option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, 
> htonl(context->local.s_addr));
> -       pxe_misc(mess, end, uuid);
> +       pxe_misc(mess, end, uuid, pxevendor);
>         
>         prune_vendor_opts(tagif_netid);
>         opt71.val = save71;
> @@ -979,7 +981,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
> *iface_name, int int_index,
>                 option_put(mess, end, OPTION_MESSAGE_TYPE, 1, 
>                            mess_type == DHCPDISCOVER ? DHCPOFFER : DHCPACK);
>                 option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, 
> htonl(tmp->local.s_addr));
> -               pxe_misc(mess, end, uuid);
> +               pxe_misc(mess, end, uuid, pxevendor);
>                 prune_vendor_opts(tagif_netid);
>                 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);
> @@ -1150,7 +1152,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
> *iface_name, int int_index,
>        option_put(mess, end, OPTION_LEASE_TIME, 4, time);
>        /* T1 and T2 are required in DHCPOFFER by HP's wacky Jetdirect client. 
> */
>        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, time, fuzz);
> +              netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, 
> vendor_class_len, now, time, fuzz, pxevendor);
>        
>        return dhcp_packet_size(mess, agent_id, real_end);
>       
> @@ -1499,7 +1501,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
> *iface_name, int int_index,
>         if (rapid_commit)
>            option_put(mess, end, OPTION_RAPID_COMMIT, 0, 0);
>          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, time, fuzz);
> +                  netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, 
> vendor_class_len, now, time, fuzz, pxevendor);
>       }
>  
>        return dhcp_packet_size(mess, agent_id, real_end); 
> @@ -1566,7 +1568,7 @@ size_t dhcp_reply(struct dhcp_context *context, char 
> *iface_name, int int_index,
>       }
>  
>        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, 0xffffffff, 0);
> +              netid, subnet_addr, fqdn_flags, borken_opt, pxearch, uuid, 
> vendor_class_len, now, 0xffffffff, 0, pxevendor);
>        
>        *is_inform = 1; /* handle reply differently */
>        return dhcp_packet_size(mess, agent_id, real_end); 
> @@ -1948,7 +1950,7 @@ static void option_put(struct dhcp_packet *mess, 
> unsigned char *end, int opt, in
>  }
>  
>  static void option_put_string(struct dhcp_packet *mess, unsigned char *end, 
> int opt, 
> -                           char *string, int null_term)
> +                           const char *string, int null_term)
>  {
>    unsigned char *p;
>    size_t len = strlen(string);
> @@ -2026,15 +2028,32 @@ static void match_vendor_opts(unsigned char *opt, 
> struct dhcp_opt *dopt)
>        dopt->flags &= ~DHOPT_VENDOR_MATCH;
>        if (opt && (dopt->flags & DHOPT_VENDOR))
>       {
> -       int i, len = 0;
> -       if (dopt->u.vendor_class)
> -         len = strlen((char *)dopt->u.vendor_class);
> -       for (i = 0; i <= (option_len(opt) - len); i++)
> -         if (len == 0 || memcmp(dopt->u.vendor_class, option_ptr(opt, i), 
> len) == 0)
> -           {
> -             dopt->flags |= DHOPT_VENDOR_MATCH;
> -             break;
> -           }
> +       const struct dhcp_pxe_vendor *pv;
> +       struct dhcp_pxe_vendor dummy_vendor = {
> +         .data = (char *)dopt->u.vendor_class,
> +         .next = NULL,
> +       };
> +       if (dopt->flags & DHOPT_VENDOR_PXE)
> +         pv = daemon->dhcp_pxe_vendors;
> +       else
> +         pv = &dummy_vendor;
> +       for (; pv; pv = pv->next)
> +         {
> +           int i, len = 0, matched = 0;
> +           if (pv->data)
> +             len = strlen(pv->data);
> +           for (i = 0; i <= (option_len(opt) - len); i++)
> +             if (len == 0 || memcmp(pv->data, option_ptr(opt, i), len) == 0)
> +               {
> +                 matched = 1;
> +                 break;
> +               }
> +           if (matched)
> +             {
> +               dopt->flags |= DHOPT_VENDOR_MATCH;
> +               break;
> +             }
> +         }
>       }
>      }
>  }
> @@ -2087,11 +2106,13 @@ static int do_encap_opts(struct dhcp_opt *opt, int 
> encap, int flag,
>    return ret;
>  }
>  
> -static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned 
> char *uuid)
> +static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned 
> char *uuid, const char *pxevendor)
>  {
>    unsigned char *p;
>  
> -  option_put_string(mess, end, OPTION_VENDOR_ID, "PXEClient", 0);
> +  if (!pxevendor)
> +    pxevendor="PXEClient";
> +  option_put_string(mess, end, OPTION_VENDOR_ID, pxevendor, 0);
>    if (uuid && (p = free_space(mess, end, OPTION_PXE_UUID, 17)))
>      memcpy(p, uuid, 17);
>  }
> @@ -2308,6 +2329,29 @@ struct dhcp_boot *find_boot(struct dhcp_netid *netid)
>    return boot;
>  }
>  
> +static int is_pxe_client(struct dhcp_packet *mess, size_t sz, const char 
> **pxe_vendor)
> +{
> +  const unsigned char *opt = NULL;
> +  ssize_t conf_len = 0;
> +  const struct dhcp_pxe_vendor *conf = daemon->dhcp_pxe_vendors;
> +  opt = option_find(mess, sz, OPTION_VENDOR_ID, 0);
> +  if (!opt) 
> +    return 0;
> +  for (; conf; conf = conf->next)
> +    {
> +      conf_len = strlen(conf->data);
> +      if (option_len(opt) < conf_len)
> +        continue;
> +      if (strncmp(option_ptr(opt, 0), conf->data, conf_len) == 0)
> +        {
> +          if (pxe_vendor)
> +            *pxe_vendor = conf->data;
> +          return 1;
> +        }
> +    }
> +  return 0;
> +}
> +
>  static void do_options(struct dhcp_context *context,
>                      struct dhcp_packet *mess,
>                      unsigned char *end, 
> @@ -2322,7 +2366,8 @@ static void do_options(struct dhcp_context *context,
>                      int vendor_class_len,
>                      time_t now,
>                      unsigned int lease_time,
> -                    unsigned short fuzz)
> +                    unsigned short fuzz,
> +                    const char *pxevendor)
>  {
>    struct dhcp_opt *opt, *config_opts = daemon->dhcp_opts;
>    struct dhcp_boot *boot;
> @@ -2696,7 +2741,7 @@ static void do_options(struct dhcp_context *context,
>    
>    if (context && pxe_arch != -1)
>      {
> -      pxe_misc(mess, end, uuid);
> +      pxe_misc(mess, end, uuid, pxevendor);
>        if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 
> 0))
>       config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
>      }
> 


_______________________________________________
Dnsmasq-discuss mailing list
Dnsmasq-discuss@lists.thekelleys.org.uk
http://lists.thekelleys.org.uk/mailman/listinfo/dnsmasq-discuss

Reply via email to