I have made some attempts at PXE booting. I have to say, it is a mess.

Put my booting attempts at fedorapeople [1]. I have asked on #ipxe IRC
channel. It seems pxe-service works only on biospc, client-arch == 0. I
were able to make simple menu on my father's lenovo desktop and my work
Thinkpad 490s. One instance of Raspberry 3. In Legacy mode, it works
somehow well. You are even to make local boot menu entries. I made it
possible to boot to memtest just fine.

However, any my attempt in EFI mode to boot using menus failed. There is
special function pxe_uefi_workaround, but to me it did not work. Current
code did never return reply from pxe port request. Because my laptop
does not send option 43 stuff in ipxe.efi request and I have not used
proxy, it just does not answer. I were able to make it return something.
It seems not well supported and should be avoided.

Guys at ipxe channel told me EFI does not include option 43 menu
support, which seems to be true. At that results, I think pxe-service
should be in general avoided if you want to support EFI. Just use tags
to offer first boot-file as ipxe.efi, then use ipxe script with possible
menus inside. That seems to be more reliable and well documented way.

I have fixed previous patch, it has to offer just based on boot item
supplied type. Client arch is not always sent in a request, even when it
is always present in discover, as I have noticed in Shrenik's dumps. I
think that patch makes improvement and allows pxe-service work just for
platforms related. Others should use dhcp-file with tags, depending on
their clients.

Custom setting of tags depending on option:client-arch seems to be more
understandable and reliable.

I have had enough of PXE today.

Cheers,
Petr

1. https://pemensik.fedorapeople.org/dnsmasq/

On 10/7/21 23:10, Simon Kelley wrote:
> As an aside the the discussion, can I just point out that I don't have
> any way to test any of this dnsmasq functionality at the moment, and I'm
> very rusty on the PXE spec, especially as it relates to EFI.
>
> I don't therefore have much to contribute to this discussion, but I do
> think this is valuable work, and when you find a solution, I'll give the
> resulting patchset my full attention.
>
>
> Cheers,
>
> Simon.
>
> _______________________________________________
> Dnsmasq-discuss mailing list
> [email protected]
> https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss
>
-- 
Petr Menšík
Software Engineer
Red Hat, http://www.redhat.com/
email: [email protected]
PGP: DFCF908DB7C87E8E529925BC4931CA5B6C9FC5CB
From f2dba8d571107ea7e65e24df0e283dcf49186e72 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= <[email protected]>
Date: Thu, 7 Oct 2021 00:40:54 +0200
Subject: [PATCH] Offer PXE services to if tags match

When pxe-service appeared in configuration, PXE structures were offered
in DHCPOFFER always. Even if those pxe-services were set for selected
tags only. Try to improve it and require matching pxe_service first,
before anything is offered.

Require matching architecture also in direct PXE requests to PXE port,
do not rely just on passed type.
---
 src/rfc2131.c | 169 +++++++++++++++++++++++++-------------------------
 1 file changed, 85 insertions(+), 84 deletions(-)

diff --git a/src/rfc2131.c b/src/rfc2131.c
index c902eb7..6c131dc 100644
--- a/src/rfc2131.c
+++ b/src/rfc2131.c
@@ -64,10 +64,21 @@ 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, int pxe);
+static int pxe_uefi_workaround(struct pxe_service *found, 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);
 
+static struct pxe_service *pxe_service_find(int pxe_arch, struct dhcp_netid *netid, struct pxe_service *start, int reqbn)
+{
+  struct pxe_service *service;
+
+  for (service = start; service; service = service->next)
+    if (pxe_arch == service->CSA && (!reqbn || service->basename) && match_netid(service->netid, netid, 1))
+      return service;
+
+  return NULL;
+}
+
 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,
 		  int *is_inform, int pxe, struct in_addr fallback, time_t recvtime)
@@ -871,7 +882,7 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
 	  (opt = option_find(mess, sz, OPTION_VENDOR_CLASS_OPT, 1)) &&
 	  (opt = option_find1(option_ptr(opt, 0), option_ptr(opt, option_len(opt)), SUBOPT_PXE_BOOT_ITEM, 4)))
 	{
-	  struct pxe_service *service;
+	  struct pxe_service *service = daemon->pxe_services;
 	  int type = option_uint(opt, 0, 2);
 	  int layer = option_uint(opt, 2, 2);
 	  unsigned char save71[4];
@@ -887,11 +898,11 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
 	    }
 
 	  memcpy(save71, option_ptr(opt, 0), 4);
-	  
+
 	  for (service = daemon->pxe_services; service; service = service->next)
 	    if (service->type == type)
 	      break;
-	  
+
 	  for (; context; context = context->current)
 	    if (match_netid(context->filter, tagif_netid, 1) &&
 		is_same_net(mess->ciaddr, context->start, context->netmask))
@@ -936,58 +947,59 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
 	  return dhcp_packet_size(mess, agent_id, real_end);	  
 	}
       
+      /* proxy DHCP here. */
       if ((opt = option_find(mess, sz, OPTION_ARCH, 2)))
-	{
 	  pxearch = option_uint(opt, 0, 2);
 
-	  /* proxy DHCP here. */
-	  if ((mess_type == DHCPDISCOVER || (pxe && mess_type == DHCPREQUEST)))
-	    {
-	      struct dhcp_context *tmp;
-	      int workaround = 0;
+      if (opt && (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) &&
-		    match_netid(tmp->filter, tagif_netid, 1))
-		  break;
+	  for (tmp = context; tmp; tmp = tmp->current)
+	    if ((tmp->flags & CONTEXT_PROXY) &&
+		match_netid(tmp->filter, tagif_netid, 1))
+	      break;
 	      
-	      if (tmp)
+	  if (tmp)
+	    {
+	      struct dhcp_boot *boot;
+	      int redirect4011 = 0;
+	      struct pxe_service *service;
+
+	      if (tmp->netid.net)
 		{
-		  struct dhcp_boot *boot;
-		  int redirect4011 = 0;
+		  tmp->netid.next = netid;
+		  tagif_netid = run_tag_if(&tmp->netid);
+		}
 
-		  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;
-		  if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
-		    {
-		      mess->ciaddr.s_addr = 0;
-		      mess->flags |= htons(0x8000); /* broadcast */
-		    }
-		  
-		  clear_packet(mess, end);
-		  
-		  /* Redirect EFI clients to port 4011 */
-		  if (pxearch >= 6)
-		    {
-		      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 iPXE, and in case we have no menu items
-			 and set discovery_control = 8 */
+	      boot = find_boot(tagif_netid);
+
+	      mess->yiaddr.s_addr = 0;
+	      if  (mess_type == DHCPDISCOVER || mess->ciaddr.s_addr == 0)
+		{
+		  mess->ciaddr.s_addr = 0;
+		  mess->flags |= htons(0x8000); /* broadcast */
+		}
+
+	      clear_packet(mess, end);
+
+	      /* Redirect EFI clients to port 4011 */
+	      if (pxearch >= 6)
+		{
+		  redirect4011 = 1;
+		  mess->siaddr = tmp->local;
+		}
+
+	      service = pxe_service_find(pxearch, tagif_netid, daemon->pxe_services, 0);
+	      /* Returns true if only one matching service is available. On port 4011,
+		  it also inserts the boot file and server name. */
+	      if (boot && service &&
+		  !(workaround = pxe_uefi_workaround(service, tagif_netid, mess, tmp->local, now, pxe)))
+		{
+		  /* Provide the bootfile here, for iPXE, 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) 
@@ -995,23 +1007,22 @@ size_t dhcp_reply(struct dhcp_context *context, char *iface_name, int int_index,
 		      
 		      if (boot->file)
 			safe_strncpy((char *)mess->file, boot->file, sizeof(mess->file));
-		    }
-		  
-		  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, 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);
-	    
-		  daemon->metrics[METRIC_PXE]++;
-		  log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
-		  log_tags(tagif_netid, ntohl(mess->xid));
-		  if (!ignore)
-		    apply_delay(mess->xid, recvtime, tagif_netid);
-		  return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);	  
 		}
+
+	      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, 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);
+
+	      daemon->metrics[METRIC_PXE]++;
+	      log_packet("PXE", NULL, emac, emac_len, iface_name, ignore ? "proxy-ignored" : "proxy", NULL, mess->xid);
+	      log_tags(tagif_netid, ntohl(mess->xid));
+	      if (!ignore)
+		apply_delay(mess->xid, recvtime, tagif_netid);
+	      return ignore ? 0 : dhcp_packet_size(mess, agent_id, real_end);
 	    }
 	}
     }
@@ -2156,31 +2167,20 @@ 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, int pxe)
+static int pxe_uefi_workaround(struct pxe_service *found, 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;
-      }
-
+  found = pxe_service_find(found->CSA, netid, found, 1);
   if (!found)
     return 0; /* No relevant menu items. */
+  else if (found->CSA < 6)
+    return 0; /* Only workaround UEFI archs. */
+  else if (pxe_service_find(found->CSA, netid, found->next, 1))
+    return 0; /* More than one relevant menu item */
   
   if (!pxe)
      return 1;
@@ -2400,6 +2400,7 @@ static void do_options(struct dhcp_context *context,
   int done_vendor_class = 0;
   struct dhcp_netid *tagif;
   struct dhcp_netid_list *id_list;
+  struct pxe_service *pxe_service;
 
   /* filter options based on tags, those we want get DHOPT_TAGOK bit set */
   if (context)
@@ -2761,10 +2762,10 @@ static void do_options(struct dhcp_context *context,
 
   force_encap = prune_vendor_opts(tagif);
   
-  if (context && pxe_arch != -1)
+  if (context && pxe_arch != -1 && (pxe_service = pxe_service_find(pxe_arch, tagif, daemon->pxe_services, 0)))
     {
       pxe_misc(mess, end, uuid, pxevendor);
-      if (!pxe_uefi_workaround(pxe_arch, tagif, mess, context->local, now, 0))
+      if (!pxe_uefi_workaround(pxe_service, tagif, mess, context->local, now, 0))
 	config_opts = pxe_opts(pxe_arch, tagif, context->local, now);
     }
 
-- 
2.31.1

_______________________________________________
Dnsmasq-discuss mailing list
[email protected]
https://lists.thekelleys.org.uk/cgi-bin/mailman/listinfo/dnsmasq-discuss

Reply via email to