Blame SOURCES/0255-efinet-Setting-network-from-UEFI-device-path.patch

a85e8e
From 083e05daa723dcb8ddca934341d523e80c63aba4 Mon Sep 17 00:00:00 2001
a85e8e
From: Michael Chang <mchang@suse.com>
a85e8e
Date: Sun, 10 Jul 2016 23:46:31 +0800
a85e8e
Subject: [PATCH 255/260] efinet: Setting network from UEFI device path
a85e8e
a85e8e
The PXE Base Code protocol used to obtain cached PXE DHCPACK packet is no
a85e8e
longer provided for HTTP Boot. Instead, we have to get the HTTP boot
a85e8e
information from the device path nodes defined in following UEFI Specification
a85e8e
sections.
a85e8e
a85e8e
 9.3.5.12 IPv4 Device Path
a85e8e
 9.3.5.13 IPv6 Device Path
a85e8e
 9.3.5.23 Uniform Resource Identifiers (URI) Device Path
a85e8e
a85e8e
This patch basically does:
a85e8e
a85e8e
include/grub/efi/api.h:
a85e8e
Add new structure of Uniform Resource Identifiers (URI) Device Path
a85e8e
a85e8e
grub-core/net/drivers/efi/efinet.c:
a85e8e
Check if PXE Base Code is available, if not it will try to obtain the netboot
a85e8e
information from the device path where the image booted from. The DHCPACK
a85e8e
packet is recoverd from the information in device patch and feed into the same
a85e8e
DHCP packet processing functions to ensure the network interface is setting up
a85e8e
the same way it used to be.
a85e8e
a85e8e
Signed-off-by: Michael Chang <mchang@suse.com>
a85e8e
Signed-off-by: Ken Lin <ken.lin@hpe.com>
a85e8e
---
a85e8e
 grub-core/net/drivers/efi/efinet.c | 293 +++++++++++++++++++++++++++++++++++--
a85e8e
 include/grub/efi/api.h             |  11 ++
a85e8e
 2 files changed, 292 insertions(+), 12 deletions(-)
a85e8e
a85e8e
diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
a85e8e
index c00ac2e64..08e9d7aa3 100644
a85e8e
--- a/grub-core/net/drivers/efi/efinet.c
a85e8e
+++ b/grub-core/net/drivers/efi/efinet.c
a85e8e
@@ -27,6 +27,7 @@
a85e8e
 #include <grub/i18n.h>
a85e8e
 #include <grub/lib/hexdump.h>
a85e8e
 #include <grub/types.h>
a85e8e
+#include <grub/net/netbuff.h>
a85e8e
 
a85e8e
 GRUB_MOD_LICENSE ("GPLv3+");
a85e8e
 
a85e8e
@@ -342,6 +343,244 @@ grub_efinet_findcards (void)
a85e8e
   grub_free (handles);
a85e8e
 }
a85e8e
 
a85e8e
+static struct grub_net_buff *
a85e8e
+grub_efinet_create_dhcp_ack_from_device_path (grub_efi_device_path_t *dp, int *use_ipv6)
a85e8e
+{
a85e8e
+  grub_efi_uint16_t uri_len;
a85e8e
+  grub_efi_device_path_t *ldp, *ddp;
a85e8e
+  grub_efi_uri_device_path_t *uri_dp;
a85e8e
+  struct grub_net_buff *nb;
a85e8e
+  grub_err_t err;
a85e8e
+
a85e8e
+  ddp = grub_efi_duplicate_device_path (dp);
a85e8e
+  if (!ddp)
a85e8e
+    return NULL;
a85e8e
+
a85e8e
+  ldp = grub_efi_find_last_device_path (ddp);
a85e8e
+
a85e8e
+  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
a85e8e
+      || GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)
a85e8e
+    {
a85e8e
+      grub_free (ddp);
a85e8e
+      grub_free (ldp);
a85e8e
+      return NULL;
a85e8e
+    }
a85e8e
+
a85e8e
+  uri_len = GRUB_EFI_DEVICE_PATH_LENGTH (ldp) > 4 ? GRUB_EFI_DEVICE_PATH_LENGTH (ldp) - 4  : 0;
a85e8e
+
a85e8e
+  if (!uri_len)
a85e8e
+    {
a85e8e
+      grub_free (ddp);
a85e8e
+      grub_free (ldp);
a85e8e
+      return NULL;
a85e8e
+    }
a85e8e
+
a85e8e
+  uri_dp = (grub_efi_uri_device_path_t *) ldp;
a85e8e
+
a85e8e
+  ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
a85e8e
+  ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
a85e8e
+  ldp->length = sizeof (*ldp);
a85e8e
+
a85e8e
+  ldp = grub_efi_find_last_device_path (ddp);
a85e8e
+
a85e8e
+  if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
a85e8e
+      || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
a85e8e
+          && GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
a85e8e
+    {
a85e8e
+      grub_free (ddp);
a85e8e
+      grub_free (ldp);
a85e8e
+      return NULL;
a85e8e
+    }
a85e8e
+
a85e8e
+  nb = grub_netbuff_alloc (512);
a85e8e
+  if (!nb)
a85e8e
+    {
a85e8e
+      grub_free (ddp);
a85e8e
+      grub_free (ldp);
a85e8e
+      return NULL;
a85e8e
+    }
a85e8e
+
a85e8e
+  if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE)
a85e8e
+    {
a85e8e
+      grub_efi_ipv4_device_path_t *ipv4 = (grub_efi_ipv4_device_path_t *) ldp;
a85e8e
+      struct grub_net_bootp_packet *bp;
a85e8e
+      grub_uint8_t *ptr;
a85e8e
+
a85e8e
+      bp = (struct grub_net_bootp_packet *) nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof (*bp) + 4);
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+
a85e8e
+      if (sizeof(bp->boot_file) < uri_len)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      grub_memcpy (bp->boot_file, uri_dp->uri, uri_len);
a85e8e
+      grub_memcpy (&bp->your_ip, ipv4->local_ip_address, sizeof (bp->your_ip));
a85e8e
+      grub_memcpy (&bp->server_ip, ipv4->remote_ip_address, sizeof (bp->server_ip));
a85e8e
+
a85e8e
+      bp->vendor[0] = GRUB_NET_BOOTP_RFC1048_MAGIC_0;
a85e8e
+      bp->vendor[1] = GRUB_NET_BOOTP_RFC1048_MAGIC_1;
a85e8e
+      bp->vendor[2] = GRUB_NET_BOOTP_RFC1048_MAGIC_2;
a85e8e
+      bp->vendor[3] = GRUB_NET_BOOTP_RFC1048_MAGIC_3;
a85e8e
+
a85e8e
+      ptr = nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof (ipv4->subnet_mask) + 2);
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      *ptr++ = GRUB_NET_BOOTP_NETMASK;
a85e8e
+      *ptr++ = sizeof (ipv4->subnet_mask);
a85e8e
+      grub_memcpy (ptr, ipv4->subnet_mask, sizeof (ipv4->subnet_mask));
a85e8e
+
a85e8e
+      ptr = nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof (ipv4->gateway_ip_address) + 2);
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      *ptr++ = GRUB_NET_BOOTP_ROUTER;
a85e8e
+      *ptr++ = sizeof (ipv4->gateway_ip_address);
a85e8e
+      grub_memcpy (ptr, ipv4->gateway_ip_address, sizeof (ipv4->gateway_ip_address));
a85e8e
+
a85e8e
+      ptr = nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof ("HTTPClient") + 1);
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      *ptr++ = GRUB_NET_BOOTP_VENDOR_CLASS_IDENTIFIER;
a85e8e
+      *ptr++ = sizeof ("HTTPClient") - 1;
a85e8e
+      grub_memcpy (ptr, "HTTPClient", sizeof ("HTTPClient") - 1);
a85e8e
+
a85e8e
+      ptr = nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, 1);
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      *ptr = GRUB_NET_BOOTP_END;
a85e8e
+      *use_ipv6 = 0;
a85e8e
+
a85e8e
+      ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
a85e8e
+      ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
a85e8e
+      ldp->length = sizeof (*ldp);
a85e8e
+      ldp = grub_efi_find_last_device_path (ddp);
a85e8e
+
a85e8e
+      if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) ==  GRUB_EFI_MAC_ADDRESS_DEVICE_PATH_SUBTYPE)
a85e8e
+	{
a85e8e
+	  grub_efi_mac_address_device_path_t *mac = (grub_efi_mac_address_device_path_t *) ldp;
a85e8e
+	  bp->hw_type = mac->if_type;
a85e8e
+	  bp->hw_len = sizeof (bp->mac_addr);
a85e8e
+	  grub_memcpy (bp->mac_addr, mac->mac_address, bp->hw_len);
a85e8e
+	}
a85e8e
+    }
a85e8e
+  else
a85e8e
+    {
a85e8e
+      grub_efi_ipv6_device_path_t *ipv6 = (grub_efi_ipv6_device_path_t *) ldp;
a85e8e
+
a85e8e
+      struct grub_net_dhcp6_packet *d6p;
a85e8e
+      struct grub_net_dhcp6_option *opt;
a85e8e
+      struct grub_net_dhcp6_option_iana *iana;
a85e8e
+      struct grub_net_dhcp6_option_iaaddr *iaaddr;
a85e8e
+
a85e8e
+      d6p = (struct grub_net_dhcp6_packet *)nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof(*d6p));
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      d6p->message_type = GRUB_NET_DHCP6_REPLY;
a85e8e
+
a85e8e
+      opt = (struct grub_net_dhcp6_option *)nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof(*opt));
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
a85e8e
+      opt->len = grub_cpu_to_be16_compile_time (sizeof(*iana) + sizeof(*opt) + sizeof(*iaaddr));
a85e8e
+
a85e8e
+      err = grub_netbuff_put (nb, sizeof(*iana));
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+
a85e8e
+      opt = (struct grub_net_dhcp6_option *)nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof(*opt));
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR);
a85e8e
+      opt->len = grub_cpu_to_be16_compile_time (sizeof (*iaaddr));
a85e8e
+
a85e8e
+      iaaddr = (struct grub_net_dhcp6_option_iaaddr *)nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof(*iaaddr));
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      grub_memcpy (iaaddr->addr, ipv6->local_ip_address, sizeof(ipv6->local_ip_address));
a85e8e
+
a85e8e
+      opt = (struct grub_net_dhcp6_option *)nb->tail;
a85e8e
+      err = grub_netbuff_put (nb, sizeof(*opt) + uri_len);
a85e8e
+      if (err)
a85e8e
+	{
a85e8e
+	  grub_free (ddp);
a85e8e
+	  grub_free (ldp);
a85e8e
+	  grub_netbuff_free (nb);
a85e8e
+	  return NULL;
a85e8e
+	}
a85e8e
+      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL);
a85e8e
+      opt->len = grub_cpu_to_be16 (uri_len);
a85e8e
+      grub_memcpy (opt->data, uri_dp->uri, uri_len);
a85e8e
+
a85e8e
+      *use_ipv6 = 1;
a85e8e
+    }
a85e8e
+
a85e8e
+  grub_free (ldp);
a85e8e
+  grub_free (ddp);
a85e8e
+  return nb;
a85e8e
+}
a85e8e
+
a85e8e
 static void
a85e8e
 grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
a85e8e
 			  char **path)
a85e8e
@@ -358,6 +597,10 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
a85e8e
     grub_efi_device_path_t *cdp;
a85e8e
     struct grub_efi_pxe *pxe;
a85e8e
     struct grub_efi_pxe_mode *pxe_mode;
a85e8e
+    grub_uint8_t *packet_buf;
a85e8e
+    grub_size_t packet_bufsz ;
a85e8e
+    int ipv6;
a85e8e
+    struct grub_net_buff *nb = NULL;
a85e8e
 
a85e8e
     if (card->driver != &efidriver)
a85e8e
       continue;
a85e8e
@@ -381,11 +624,21 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
a85e8e
          */
a85e8e
 	if (GRUB_EFI_DEVICE_PATH_TYPE (ldp) != GRUB_EFI_MESSAGING_DEVICE_PATH_TYPE
a85e8e
 	    || (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV4_DEVICE_PATH_SUBTYPE
a85e8e
-		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE))
a85e8e
+		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_IPV6_DEVICE_PATH_SUBTYPE
a85e8e
+		&& GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) != GRUB_EFI_URI_DEVICE_PATH_SUBTYPE))
a85e8e
 	  continue;
a85e8e
 	dup_dp = grub_efi_duplicate_device_path (dp);
a85e8e
 	if (!dup_dp)
a85e8e
 	  continue;
a85e8e
+
a85e8e
+	if (GRUB_EFI_DEVICE_PATH_SUBTYPE (ldp) == GRUB_EFI_URI_DEVICE_PATH_SUBTYPE)
a85e8e
+	  {
a85e8e
+	    dup_ldp = grub_efi_find_last_device_path (dup_dp);
a85e8e
+	    dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
a85e8e
+	    dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
a85e8e
+	    dup_ldp->length = sizeof (*dup_ldp);
a85e8e
+	  }
a85e8e
+
a85e8e
 	dup_ldp = grub_efi_find_last_device_path (dup_dp);
a85e8e
 	dup_ldp->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
a85e8e
 	dup_ldp->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
a85e8e
@@ -398,20 +651,32 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
a85e8e
 
a85e8e
     pxe = grub_efi_open_protocol (hnd, &pxe_io_guid,
a85e8e
 				  GRUB_EFI_OPEN_PROTOCOL_GET_PROTOCOL);
a85e8e
-    if (! pxe)
a85e8e
-      continue;
a85e8e
+    if (!pxe)
a85e8e
+      {
a85e8e
+	nb = grub_efinet_create_dhcp_ack_from_device_path (dp, &ipv6);
a85e8e
+	if (!nb)
a85e8e
+	  {
a85e8e
+	    grub_print_error ();
a85e8e
+	    continue;
a85e8e
+	  }
a85e8e
+	packet_buf = nb->head;
a85e8e
+	packet_bufsz = nb->tail - nb->head;
a85e8e
+      }
a85e8e
+    else
a85e8e
+      {
a85e8e
+	pxe_mode = pxe->mode;
a85e8e
+	packet_buf = (grub_uint8_t *) &pxe_mode->dhcp_ack;
a85e8e
+	packet_bufsz = sizeof (pxe_mode->dhcp_ack);
a85e8e
+	ipv6 = pxe_mode->using_ipv6;
a85e8e
+      }
a85e8e
 
a85e8e
-    pxe_mode = pxe->mode;
a85e8e
-    if (pxe_mode->using_ipv6)
a85e8e
+    if (ipv6)
a85e8e
       {
a85e8e
 	grub_dprintf ("efinet", "using ipv6 and dhcpv6\n");
a85e8e
-	grub_dprintf ("efinet", "dhcp_ack_received: %s%s\n",
a85e8e
-		      pxe_mode->dhcp_ack_received ? "yes" : "no",
a85e8e
-		      pxe_mode->dhcp_ack_received ? "" : " cannot continue");
a85e8e
 	grub_net_configure_by_dhcpv6_reply (card->name, card, 0,
a85e8e
 					    (struct grub_net_dhcp6_packet *)
a85e8e
-					    &pxe_mode->dhcp_ack,
a85e8e
-					    sizeof (pxe_mode->dhcp_ack),
a85e8e
+					    packet_buf,
a85e8e
+					    packet_bufsz,
a85e8e
 					    1, device, path);
a85e8e
 	if (device && path)
a85e8e
 	  grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
a85e8e
@@ -423,11 +688,15 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
a85e8e
 	grub_dprintf ("efinet", "using ipv4 and dhcp\n");
a85e8e
 	grub_net_configure_by_dhcp_ack (card->name, card, 0,
a85e8e
 					(struct grub_net_bootp_packet *)
a85e8e
-					&pxe_mode->dhcp_ack,
a85e8e
-					sizeof (pxe_mode->dhcp_ack),
a85e8e
+					packet_buf,
a85e8e
+					packet_bufsz,
a85e8e
 					1, device, path);
a85e8e
 	grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
a85e8e
       }
a85e8e
+
a85e8e
+    if (nb)
a85e8e
+      grub_netbuff_free (nb);
a85e8e
+
a85e8e
     return;
a85e8e
   }
a85e8e
 }
a85e8e
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
a85e8e
index 0821e3364..a37fb50de 100644
a85e8e
--- a/include/grub/efi/api.h
a85e8e
+++ b/include/grub/efi/api.h
a85e8e
@@ -786,6 +786,8 @@ struct grub_efi_ipv4_device_path
a85e8e
   grub_efi_uint16_t remote_port;
a85e8e
   grub_efi_uint16_t protocol;
a85e8e
   grub_efi_uint8_t static_ip_address;
a85e8e
+  grub_efi_ipv4_address_t gateway_ip_address;
a85e8e
+  grub_efi_ipv4_address_t subnet_mask;
a85e8e
 } GRUB_PACKED;
a85e8e
 typedef struct grub_efi_ipv4_device_path grub_efi_ipv4_device_path_t;
a85e8e
 
a85e8e
@@ -840,6 +842,15 @@ struct grub_efi_sata_device_path
a85e8e
 } GRUB_PACKED;
a85e8e
 typedef struct grub_efi_sata_device_path grub_efi_sata_device_path_t;
a85e8e
 
a85e8e
+#define GRUB_EFI_URI_DEVICE_PATH_SUBTYPE		24
a85e8e
+
a85e8e
+struct grub_efi_uri_device_path
a85e8e
+{
a85e8e
+  grub_efi_device_path_t header;
a85e8e
+  grub_efi_uint8_t uri[0];
a85e8e
+} GRUB_PACKED;
a85e8e
+typedef struct grub_efi_uri_device_path grub_efi_uri_device_path_t;
a85e8e
+
a85e8e
 #define GRUB_EFI_VENDOR_MESSAGING_DEVICE_PATH_SUBTYPE	10
a85e8e
 
a85e8e
 /* Media Device Path.  */
a85e8e
-- 
a85e8e
2.13.0
a85e8e