summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Kelley <simon@thekelleys.org.uk>2016-05-10 17:31:48 +0100
committerSimon Kelley <simon@thekelleys.org.uk>2016-05-10 17:31:48 +0100
commit8628cd603fd0c55c7d41b84488446db44f58ff5b (patch)
tree8b6ddd6c30ff8dbe71674f99daae211923b100d2
parentff325644c7afae2588583f935f4ea9b9694eb52e (diff)
downloaddnsmasq-2.76rc1.tar.gz
Workaround for UEFI PXE boot problems.v2.76rc1
-rw-r--r--src/rfc2131.c69
1 files changed, 62 insertions, 7 deletions
diff --git a/src/rfc2131.c b/src/rfc2131.c
index f3c8895..42d1ba8 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -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)
@@ -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) &&
@@ -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)
@@ -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);
@@ -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
@@ -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)) &&