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