The PXE Base Code protocol, which was previously used to obtain the
cached PXE DHCPACK packet, is no longer available for HTTP Boot. HTTP
boot configuration must now be retrieved from the device path nodes
defined in the following UEFI Specification sections:

    9.3.5.12 IPv4 Device Path
    9.3.5.13 IPv6 Device Path
    9.3.5.23 Uniform Resource Identifiers (URI) Device Path

This patch introduces the following changes:

include/grub/efi/api.h:
Added new structures for the Uniform Resource Identifiers (URI) and DNS
Device Path. The DNS Device Path is not currently used and is skipped.

grub-core/net/drivers/efi/efinet.c:
Checks if the PXE Base Code is available. If not, it retrieves the
network boot configuration from the device path representing the current
setup. The DHCPACK packet is reconstructed from the device path and
passed into the common DHCP packet processing functions to ensure the
network interface is set up as it was previously.

Signed-off-by: Ken Lin <ken....@hpe.com>
Signed-off-by: Robbie Harwood <rharw...@redhat.com>
Signed-off-by: Michael Chang <mch...@suse.com>
---
 grub-core/net/drivers/efi/efinet.c | 306 +++++++++++++++++++++++++++--
 include/grub/efi/api.h             |  20 ++
 2 files changed, 311 insertions(+), 15 deletions(-)

diff --git a/grub-core/net/drivers/efi/efinet.c 
b/grub-core/net/drivers/efi/efinet.c
index 9c34230b3..5bafd9fe8 100644
--- a/grub-core/net/drivers/efi/efinet.c
+++ b/grub-core/net/drivers/efi/efinet.c
@@ -23,6 +23,7 @@
 #include <grub/efi/api.h>
 #include <grub/efi/efi.h>
 #include <grub/i18n.h>
+#include <grub/net/netbuff.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
@@ -342,6 +343,238 @@ grub_efinet_findcards (void)
   grub_free (handles);
 }
 
+static struct grub_net_buff *
+grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int 
*use_ipv6)
+{
+  grub_efi_uint16_t uri_len;
+  grub_efi_device_path_t *ldp, *ddp;
+  grub_efi_uri_device_path_t *uri_dp;
+  struct grub_net_buff *nb;
+  grub_err_t err;
+
+  ddp = grub_efi_duplicate_device_path (dp);
+  if (!ddp)
+    return NULL;
+
+  ldp = grub_efi_find_last_device_path (ddp);
+
+  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+      || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != 
GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)
+    {
+      grub_free (ddp);
+      return NULL;
+    }
+
+  uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? 
GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4  : 0;
+
+  if (!uri_len)
+    {
+      grub_free (ddp);
+      return NULL;
+    }
+
+  uri_dp = (grub_efi_uri_device_path_t *) ldp;
+
+  ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+  ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+  ldp->length = sizeof (*ldp);
+
+  ldp = grub_efi_find_last_device_path (ddp);
+
+  /* Skip the DNS Device */
+  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) == GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+      && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == 
GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE)
+    {
+      ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+      ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+      ldp->length = sizeof (*ldp);
+
+      ldp = grub_efi_find_last_device_path (ddp);
+    }
+
+  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
+      || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != 
GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
+          && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != 
GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
+    {
+      grub_free (ddp);
+      return NULL;
+    }
+
+  nb = grub_netbuff_alloc (512);
+  if (!nb)
+    {
+      grub_free (ddp);
+      return NULL;
+    }
+
+  if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
+    {
+      grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp;
+      struct grub_net_bootp_packet *bp;
+      grub_uint8_t *ptr;
+
+      bp = (struct grub_net_bootp_packet *) nb->tail;
+      err = grub_netbuff_put (nb, sizeof (*bp) + 4);
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+
+      if (sizeof (bp->boot_file) < uri_len)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      grub_memcpy (bp->boot_file, uri_dp->uri, uri_len);
+      grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip));
+      grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof 
(bp->server_ip));
+
+      bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0;
+      bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1;
+      bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2;
+      bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3;
+
+      ptr = nb->tail;
+      err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2);
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      *ptr++ = GRUB_NET_BOOTP_NETMASK;
+      *ptr++ = sizeof (ipv4->subnet_mask);
+      grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask));
+
+      ptr = nb->tail;
+      err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2);
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      *ptr++ = GRUB_NET_BOOTP_ROUTER;
+      *ptr++ = sizeof (ipv4->gateway_ip_address);
+      grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof 
(ipv4->gateway_ip_address));
+
+      ptr = nb->tail;
+      err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1);
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER;
+      *ptr++ = sizeof ("HTTPClient") - 1;
+      grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1);
+
+      ptr = nb->tail;
+      err = grub_netbuff_put (nb, 1);
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      *ptr = GRUB_NET_BOOTP_END;
+      *use_ipv6 = 0;
+
+      ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+      ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+      ldp->length = sizeof (*ldp);
+      ldp = grub_efi_find_last_device_path (ddp);
+
+      if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) ==  
GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)
+       {
+         grub_efi_mac_address_device_path_t *mac = 
(grub_efi_mac_address_device_path_t *) ldp;
+         bp->hw_type = mac->if_type;
+         bp->hw_len = sizeof (bp->mac_addr);
+         grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len);
+       }
+    }
+  else
+    {
+      grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp;
+
+      struct grub_net_dhcp6_packet *d6p;
+      struct grub_net_dhcp6_option *opt;
+      struct grub_net_dhcp6_option_iana *iana;
+      struct grub_net_dhcp6_option_iaaddr *iaaddr;
+
+      d6p = (struct grub_net_dhcp6_packet *)nb->tail;
+      err = grub_netbuff_put (nb, sizeof(*d6p));
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      d6p->message_type = GRUB_NET_DHCP6_REPLY;
+
+      opt = (struct grub_net_dhcp6_option *) nb->tail;
+      err = grub_netbuff_put (nb, sizeof(*opt));
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
+      opt->len = grub_cpu_to_be16_compile_time (sizeof (*iana) + sizeof (*opt) 
+ sizeof (*iaaddr));
+
+      err = grub_netbuff_put (nb, sizeof (*iana));
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+
+      opt = (struct grub_net_dhcp6_option *) nb->tail;
+      err = grub_netbuff_put (nb, sizeof (*opt));
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR);
+      opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr));
+
+      iaaddr = (struct grub_net_dhcp6_option_iaaddr *) nb->tail;
+      err = grub_netbuff_put (nb, sizeof (*iaaddr));
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof 
(ipv6->local_ip_address));
+
+      opt = (struct grub_net_dhcp6_option *)nb->tail;
+      err = grub_netbuff_put (nb, sizeof (*opt) + uri_len);
+      if (err)
+       {
+         grub_free (ddp);
+         grub_netbuff_free (nb);
+         return NULL;
+       }
+      opt->code = grub_cpu_to_be16_compile_time 
(GRUB_NET_DHCP6_OPTION_BOOTFILE_URL);
+      opt->len = grub_cpu_to_be16 (uri_len);
+      grub_memcpy (opt->data, uri_dp->uri, uri_len);
+
+      *use_ipv6 = 1;
+    }
+
+  grub_free (ddp);
+  return nb;
+}
+
 static void
 grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
                          char **path)
@@ -361,7 +594,12 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char 
**device,
   {
     grub_efi_device_path_t *cdp;
     struct grub_efi_pxe *pxe;
-    struct grub_efi_pxe_mode *pxe_mode;
+    struct grub_efi_pxe_mode *pxe_mode = NULL;
+    grub_uint8_t *packet_buf;
+    grub_size_t packet_bufsz ;
+    int ipv6;
+    struct grub_net_buff *nb = NULL;
+
     if (card->driver != &efidriver)
       continue;
     cdp = grub_efi_get_device_path (card->efi_handle);
@@ -381,11 +619,31 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char 
**device,
        ldp = grub_efi_find_last_device_path (dp);
        if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != 
GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
            || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != 
GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
-               && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != 
GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
+               && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != 
GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE
+               && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != 
GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE
+               && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != 
GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
          continue;
        dup_dp = grub_efi_duplicate_device_path (dp);
        if (!dup_dp)
          continue;
+
+       if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == 
GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)
+         {
+           dup_ldp = grub_efi_find_last_device_path (dup_dp);
+           dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+           dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+           dup_ldp->length = sizeof (*dup_ldp);
+         }
+
+       dup_ldp = grub_efi_find_last_device_path (dup_dp);
+       if (GRUB_EFI_DEVICE_PATH_SUBTYPE (dup_ldp) == 
GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE)
+         {
+           dup_ldp = grub_efi_find_last_device_path (dup_dp);
+           dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+           dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+           dup_ldp->length = sizeof (*dup_ldp);
+         }
+
        dup_ldp = grub_efi_find_last_device_path (dup_dp);
        dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
        dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
@@ -406,23 +664,37 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char 
**device,
       }
     pxe = grub_efi_open_protocol (hnd, &pxe_io_guid,
                                  GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
-    if (! pxe)
-      continue;
-    pxe_mode = pxe->mode;
+    if (!pxe)
+      {
+       nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6);
+       if (!nb)
+         {
+           grub_print_error ();
+           continue;
+         }
+       packet_buf = nb->head;
+       packet_bufsz = nb->tail - nb->head;
+      }
+    else
+      {
+       pxe_mode = pxe->mode;
+       packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack;
+       packet_bufsz = sizeof (pxe_mode->dhcp_ack);
+       ipv6 = pxe_mode->using_ipv6;
+      }
 
-    if (pxe_mode->using_ipv6)
+    if (ipv6)
       {
        grub_dprintf ("efinet", "using ipv6 and dhcpv6\n");
-       grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
-                     pxe_mode->dhcp_ack_received ? "yes" : "no",
-                     pxe_mode->dhcp_ack_received ? "" : " cannot continue");
-       if (!pxe_mode->dhcp_ack_received)
-         continue;
+       if (pxe_mode)
+         grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
+                       pxe_mode->dhcp_ack_received ? "yes" : "no",
+                       pxe_mode->dhcp_ack_received ? "" : " cannot continue");
 
        inter = grub_net_configure_by_dhcpv6_reply (card->name, card, 0,
                                                    (struct 
grub_net_dhcp6_packet *)
-                                                   &pxe_mode->dhcp_ack,
-                                                   sizeof (pxe_mode->dhcp_ack),
+                                                   packet_buf,
+                                                   packet_bufsz,
                                                    1, device, path);
        if (grub_errno)
          grub_print_error ();
@@ -434,8 +706,8 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char 
**device,
        grub_dprintf ("efinet", "using ipv4 and dhcp\n");
        inter = grub_net_configure_by_dhcp_ack (card->name, card, 0,
                                                (struct grub_net_bootp_packet *)
-                                               &pxe_mode->dhcp_ack,
-                                               sizeof (pxe_mode->dhcp_ack),
+                                               packet_buf,
+                                               packet_bufsz,
                                                1, device, path);
        grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
       }
@@ -462,6 +734,10 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char 
**device,
            vlan_dp = (grub_efi_device_path_t *) ((grub_efi_uint8_t *) vlan_dp 
+ vlan_dp_len);
          }
       }
+
+    if (nb)
+      grub_netbuff_free (nb);
+
     return;
   }
 }
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
index 6b1ba8a5e..58a15a9bf 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -904,6 +904,8 @@ struct grub_efi_ipv4_device_path
   grub_efi_uint16_t remote_port;
   grub_efi_uint16_t protocol;
   grub_efi_uint8_t static_ip_address;
+  grub_efi_ipv4_address_t gateway_ip_address;
+  grub_efi_ipv4_address_t subnet_mask;
 } GRUB_PACKED;
 typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t;
 
@@ -967,6 +969,24 @@ struct grub_efi_vlan_device_path
 } GRUB_PACKED;
 typedef struct grub_efi_vlan_device_path grub_efi_vlan_device_path_t;
 
+#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE               24
+
+struct grub_efi_uri_device_path
+{
+  grub_efi_device_path_t header;
+  grub_efi_char8_t uri[0];
+} GRUB_PACKED;
+typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t;
+
+#define GRUB_EFI_DNS_DEVICE_PATH_SUBTYPE                31
+struct grub_efi_dns_device_path
+{
+  grub_efi_device_path_t header;
+  grub_efi_uint8_t is_ipv6;
+  grub_efi_pxe_ip_address_t dns_server_ip[0];
+} GRUB_PACKED;
+typedef struct grub_efi_dns_device_path grub_efi_dns_device_path_t;
+
 #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE  10
 
 /* Media Device Path.  */
-- 
2.47.1


_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
https://lists.gnu.org/mailman/listinfo/grub-devel

Reply via email to