Skip to content

Commit

Permalink
Workaround for UEFI PXE boot problems.
Browse files Browse the repository at this point in the history
  • Loading branch information
simonkelley committed May 10, 2016
1 parent ff32564 commit 8628cd6
Showing 1 changed file with 62 additions and 7 deletions.
69 changes: 62 additions & 7 deletions src/rfc2131.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 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);

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)
Expand Down Expand Up @@ -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)))
{
struct dhcp_context *tmp;
int workaround = 0;

for (tmp = context; tmp; tmp = tmp->current)
if ((tmp->flags & CONTEXT_PROXY) &&
Expand Down Expand Up @@ -878,10 +879,16 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,

clear_packet(mess, end);

/* Provide the bootfile here, for gPXE, and in case we have no menu items
and set discovery_control = 8 */
if (boot)
/* Only do workaround for replies to 4011 */
if (!pxe)
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)
mess->siaddr = boot->next_server;
else if (boot->tftp_sname)
Expand All @@ -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));
pxe_misc(mess, end, uuid);
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_tags(tagif_netid, ntohl(mess->xid));
return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
Expand Down Expand Up @@ -1975,6 +1983,52 @@ static int prune_vendor_opts(struct dhcp_netid *netid)
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)
{
#define NUM_OPTS 4
Expand Down Expand Up @@ -2509,7 +2563,8 @@ static void do_options(struct dhcp_context *context,
if (context && pxe_arch != -1)
{
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)) &&
Expand Down

0 comments on commit 8628cd6

Please sign in to comment.