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)) && |