Commit 8628cd60 authored by Simon Kelley's avatar Simon Kelley

Workaround for UEFI PXE boot problems.

parent ff325644
...@@ -63,7 +63,7 @@ static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char ...@@ -63,7 +63,7 @@ static void pxe_misc(struct dhcp_packet *mess, unsigned char *end, unsigned char
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);
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)
...@@ -851,6 +851,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, ...@@ -851,6 +851,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
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) &&
...@@ -878,10 +879,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, ...@@ -878,10 +879,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
clear_packet(mess, end); clear_packet(mess, end);
/* Provide the bootfile here, for gPXE, and in case we have no menu items /* Only do workaround for replies to 4011 */
and set discovery_control = 8 */ if (!pxe)
if (boot) mess->siaddr = tmp->local;
else
workaround = pxe_uefi_workaround(pxearch, tagif_netid, mess, tmp->local, now);
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)
...@@ -896,8 +903,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index, ...@@ -896,8 +903,9 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
option_put(mess, end, OPTION_SERVER_IDENTIFIER, INADDRSZ, htonl(tmp->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, tmp->local, now), OPTION_VENDOR_CLASS_OPT, DHOPT_VENDOR_MATCH, mess, end, 0); if (!workaround)
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", NULL, 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);
...@@ -1975,6 +1983,52 @@ static int prune_vendor_opts(struct dhcp_netid *netid) ...@@ -1975,6 +1983,52 @@ 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)
{
struct pxe_service *service, *found;
/* Only workaround UEFI archs. */
if (pxe_arch != 6 && pxe_arch != 7 && pxe_arch != 8 && pxe_arch != 9)
return 0;
for (found = NULL, service = daemon->pxe_services; service; service = service->next)
if (pxe_arch == service->CSA && 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 (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), "%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
...@@ -2509,7 +2563,8 @@ static void do_options(struct dhcp_context *context, ...@@ -2509,7 +2563,8 @@ 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))
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)) &&
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment