All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] UEFI IPv6 and DHCPv6 support
@ 2020-06-04  7:33 Javier Martinez Canillas
  2020-06-04  7:33 ` [PATCH 1/4] net: read bracketed ipv6 addrs and port numbers Javier Martinez Canillas
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Javier Martinez Canillas @ 2020-06-04  7:33 UTC (permalink / raw)
  To: grub-devel; +Cc: Daniel Kiper, Michael Chang, Javier Martinez Canillas

Hello,

This series contains patches that come from the SUSE package that adds a
net_bootp6 command to auto configure the network interfaces by using the
DHCPv6 protocol. They also add support to do the auto configuration when
GRUB is booted from UEFI IPv6 PXE.

We have been carrying these for a long time in our grub2 package and is
also used by other distros such as Debian and Ubuntu. Michael Chang posted
them in 2016 but were never merged:

https://lists.gnu.org/archive/html/grub-devel/2016-08/msg00000.html

Without these patches, booting GRUB from a UEFI PXEv6 boot entry leads to
a wrong network configuration and GRUB not being able to fetch its config
file over TFTP. For example, the following variables are incorrectly set:

cmdpath=(tftp,0.5.0.24)d:beef:a::1]/netboot
net_default_ip=0.0.0.0
net_default_mac=be:ef:00:0a:00:00
net_default_server=0.5.0.24
net_efinet0_boot_file=d:beef:a::1]/netboot/grubx64.efi
net_efinet0_ip=0.0.0.0
net_efinet0_mac=be:ef:00:0a:00:00
net_efinet0_next_server=0.5.0.24
prefix=(tftp,0.5.0.24)/netboot
pxe_default_server=0.5.0.24
root=tftp,0.5.0.24

With these changes, the network interface is correctly configured and GRUB
is able to fetch its config file. The network variables are correctly set:

cmdpath=(tftp,2000:dead:beef:a::1)/netboot
net_default_ip=2000:dead:beef:a:0:0:0:2000
net_default_mac=52:54:00:2c:dc:9e
net_default_server=2000:dead:beef:a::1
net_efinet0_boot_file=/netboot/grubx64.efi
net_efinet0_ip=2000:dead:beef:a:0:0:0:2000
net_efinet0_mac=52:54:00:2c:dc:9e
prefix=(tftp,2000:dead:beef:a::1)/netboot
pxe_default_server=2000:dead:beef:a::1
root=tftp,2000:dead:beef:a::1


Aaron Miller (1):
  net: read bracketed ipv6 addrs and port numbers

Michael Chang (3):
  bootp: New net_bootp6 command
  efinet: UEFI IPv6 PXE support
  grub.texi: Add net_bootp6 document

 docs/grub.texi                     |  19 +
 grub-core/net/bootp.c              | 914 ++++++++++++++++++++++++++++-
 grub-core/net/drivers/efi/efinet.c |  45 +-
 grub-core/net/http.c               |  25 +-
 grub-core/net/ip.c                 |  39 ++
 grub-core/net/net.c                | 159 ++++-
 grub-core/net/tftp.c               |  10 +-
 include/grub/efi/api.h             | 146 ++++-
 include/grub/net.h                 |  80 +++
 9 files changed, 1411 insertions(+), 26 deletions(-)

-- 
2.26.2



^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/4] net: read bracketed ipv6 addrs and port numbers
  2020-06-04  7:33 [PATCH 0/4] UEFI IPv6 and DHCPv6 support Javier Martinez Canillas
@ 2020-06-04  7:33 ` Javier Martinez Canillas
  2020-06-04  7:33 ` [PATCH 2/4] bootp: New net_bootp6 command Javier Martinez Canillas
  2020-06-04  7:33 ` [PATCH 3/4] efinet: UEFI IPv6 PXE support Javier Martinez Canillas
  2 siblings, 0 replies; 9+ messages in thread
From: Javier Martinez Canillas @ 2020-06-04  7:33 UTC (permalink / raw)
  To: grub-devel
  Cc: Daniel Kiper, Michael Chang, Aaron Miller, Peter Jones,
	Javier Martinez Canillas

From: Aaron Miller <aaronmiller@fb.com>

Allow specifying port numbers for http and tftp paths, and allow ipv6 addresses
to be recognized with brackets around them, which is required to specify a port
number

Signed-off-by: Aaron Miller <aaronmiller@fb.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---

 grub-core/net/http.c | 25 ++++++++++---
 grub-core/net/net.c  | 87 +++++++++++++++++++++++++++++++++++++++++---
 grub-core/net/tftp.c |  7 +++-
 include/grub/net.h   |  1 +
 4 files changed, 109 insertions(+), 11 deletions(-)

diff --git a/grub-core/net/http.c b/grub-core/net/http.c
index b616cf40b1e..12a2632ea55 100644
--- a/grub-core/net/http.c
+++ b/grub-core/net/http.c
@@ -289,7 +289,9 @@ http_receive (grub_net_tcp_socket_t sock __attribute__ ((unused)),
 	  nb2 = grub_netbuff_alloc (data->chunk_rem);
 	  if (!nb2)
 	    return grub_errno;
-	  grub_netbuff_put (nb2, data->chunk_rem);
+	  err = grub_netbuff_put (nb2, data->chunk_rem);
+	  if (err)
+	    return grub_errno;
 	  grub_memcpy (nb2->data, nb->data, data->chunk_rem);
 	  if (file->device->net->packs.count >= 20)
 	    {
@@ -312,12 +314,14 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
   int i;
   struct grub_net_buff *nb;
   grub_err_t err;
+  char* server = file->device->net->server;
+  int port = file->device->net->port;
 
   nb = grub_netbuff_alloc (GRUB_NET_TCP_RESERVE_SIZE
 			   + sizeof ("GET ") - 1
 			   + grub_strlen (data->filename)
 			   + sizeof (" HTTP/1.1\r\nHost: ") - 1
-			   + grub_strlen (file->device->net->server)
+			   + grub_strlen (server) + sizeof (":XXXXXXXXXX")
 			   + sizeof ("\r\nUser-Agent: " PACKAGE_STRING
 				     "\r\n") - 1
 			   + sizeof ("Range: bytes=XXXXXXXXXXXXXXXXXXXX"
@@ -356,7 +360,7 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
 	       sizeof (" HTTP/1.1\r\nHost: ") - 1);
 
   ptr = nb->tail;
-  err = grub_netbuff_put (nb, grub_strlen (file->device->net->server));
+  err = grub_netbuff_put (nb, grub_strlen (server));
   if (err)
     {
       grub_netbuff_free (nb);
@@ -365,6 +369,15 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
   grub_memcpy (ptr, file->device->net->server,
 	       grub_strlen (file->device->net->server));
 
+  if (port)
+    {
+      ptr = nb->tail;
+      grub_snprintf ((char *) ptr,
+	  sizeof (":XXXXXXXXXX"),
+	  ":%d",
+	  port);
+    }
+
   ptr = nb->tail;
   err = grub_netbuff_put (nb, 
 			  sizeof ("\r\nUser-Agent: " PACKAGE_STRING "\r\n")
@@ -390,8 +403,10 @@ http_establish (struct grub_file *file, grub_off_t offset, int initial)
   grub_netbuff_put (nb, 2);
   grub_memcpy (ptr, "\r\n", 2);
 
-  data->sock = grub_net_tcp_open (file->device->net->server,
-				  HTTP_PORT, http_receive,
+  grub_dprintf ("http", "opening path %s on host %s TCP port %d\n",
+		data->filename, server, port ? port : HTTP_PORT);
+  data->sock = grub_net_tcp_open (server,
+				  port ? port : HTTP_PORT, http_receive,
 				  http_err, NULL,
 				  file);
   if (!data->sock)
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
index 3a310c939b5..36bca041ac2 100644
--- a/grub-core/net/net.c
+++ b/grub-core/net/net.c
@@ -437,6 +437,13 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest)
   grub_uint16_t newip[8];
   const char *ptr = val;
   int word, quaddot = -1;
+  int bracketed = 0;
+
+  if (ptr[0] == '[')
+    {
+      bracketed = 1;
+      ptr++;
+    }
 
   if (ptr[0] == ':' && ptr[1] != ':')
     return 0;
@@ -475,6 +482,8 @@ parse_ip6 (const char *val, grub_uint64_t *ip, const char **rest)
       grub_memset (&newip[quaddot], 0, (7 - word) * sizeof (newip[0]));
     }
   grub_memcpy (ip, newip, 16);
+  if (bracketed && *ptr == ']')
+    ptr++;
   if (rest)
     *rest = ptr;
   return 1;
@@ -1260,8 +1269,10 @@ grub_net_open_real (const char *name)
 {
   grub_net_app_level_t proto;
   const char *protname, *server;
+  char *host;
   grub_size_t protnamelen;
   int try;
+  int port = 0;
 
   if (grub_strncmp (name, "pxe:", sizeof ("pxe:") - 1) == 0)
     {
@@ -1299,6 +1310,72 @@ grub_net_open_real (const char *name)
       return NULL;
     }  
 
+  char* port_start;
+  /* ipv6 or port specified? */
+  if ((port_start = grub_strchr (server, ':')))
+    {
+      char* ipv6_begin;
+      if((ipv6_begin = grub_strchr (server, '[')))
+	{
+	  char* ipv6_end = grub_strchr (server, ']');
+	  if(!ipv6_end)
+	    {
+	      grub_error (GRUB_ERR_NET_BAD_ADDRESS,
+		      N_("mismatched [ in address"));
+	      return NULL;
+	    }
+	  /* port number after bracketed ipv6 addr */
+	  if(ipv6_end[1] == ':')
+	    {
+	      port = grub_strtoul (ipv6_end + 2, NULL, 10);
+	      if(port > 65535)
+		{
+		  grub_error (GRUB_ERR_NET_BAD_ADDRESS,
+			  N_("bad port number"));
+		  return NULL;
+		}
+	    }
+	  host = grub_strndup (ipv6_begin, (ipv6_end - ipv6_begin) + 1);
+	}
+      else
+	{
+	  if (grub_strchr (port_start + 1, ':'))
+	    {
+	      int iplen = grub_strlen (server);
+	      /* bracket bare ipv6 addrs */
+	      host = grub_malloc (iplen + 3);
+	      if(!host)
+		{
+		  return NULL;
+		}
+	      host[0] = '[';
+	      grub_memcpy (host + 1, server, iplen);
+	      host[iplen + 1] = ']';
+	      host[iplen + 2] = '\0';
+	    }
+	  else
+	    {
+	      /* hostname:port or ipv4:port */
+	      port = grub_strtol (port_start + 1, NULL, 10);
+	      if(port > 65535)
+		{
+		  grub_error (GRUB_ERR_NET_BAD_ADDRESS,
+			  N_("bad port number"));
+		  return NULL;
+		}
+	      host = grub_strndup (server, port_start - server);
+	    }
+	}
+    }
+  else
+    {
+      host = grub_strdup (server);
+    }
+  if (!host)
+    {
+      return NULL;
+    }
+
   for (try = 0; try < 2; try++)
     {
       FOR_NET_APP_LEVEL (proto)
@@ -1308,14 +1385,13 @@ grub_net_open_real (const char *name)
 	  {
 	    grub_net_t ret = grub_zalloc (sizeof (*ret));
 	    if (!ret)
-	      return NULL;
-	    ret->protocol = proto;
-	    ret->server = grub_strdup (server);
-	    if (!ret->server)
 	      {
-		grub_free (ret);
+		grub_free (host);
 		return NULL;
 	      }
+	    ret->protocol = proto;
+	    ret->port = port;
+	    ret->server = host;
 	    ret->fs = &grub_net_fs;
 	    return ret;
 	  }
@@ -1390,6 +1466,7 @@ grub_net_open_real (const char *name)
   grub_error (GRUB_ERR_UNKNOWN_DEVICE, N_("disk `%s' not found"),
 	      name);
 
+  grub_free (host);
   return NULL;
 }
 
diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
index b26d2ed7aa2..c7873465662 100644
--- a/grub-core/net/tftp.c
+++ b/grub-core/net/tftp.c
@@ -334,6 +334,7 @@ tftp_open (struct grub_file *file, const char *filename)
   grub_err_t err;
   grub_uint8_t *nbd;
   grub_net_network_level_address_t addr;
+  int port = file->device->net->port;
 
   data = grub_zalloc (sizeof (*data));
   if (!data)
@@ -407,13 +408,17 @@ tftp_open (struct grub_file *file, const char *filename)
   err = grub_net_resolve_address (file->device->net->server, &addr);
   if (err)
     {
+      grub_dprintf ("tftp", "Address resolution failed: %d\n", err);
+      grub_dprintf ("tftp", "file_size is %llu, block_size is %llu\n",
+		    (unsigned long long)data->file_size,
+		    (unsigned long long)data->block_size);
       destroy_pq (data);
       grub_free (data);
       return err;
     }
 
   data->sock = grub_net_udp_open (addr,
-				  TFTP_SERVER_PORT, tftp_receive,
+				  port ? port : TFTP_SERVER_PORT, tftp_receive,
 				  file);
   if (!data->sock)
     {
diff --git a/include/grub/net.h b/include/grub/net.h
index 7ae4b6bd805..69bfe0947b2 100644
--- a/include/grub/net.h
+++ b/include/grub/net.h
@@ -270,6 +270,7 @@ typedef struct grub_net
 {
   char *server;
   char *name;
+  int port;
   grub_net_app_level_t protocol;
   grub_net_packets_t packs;
   grub_off_t offset;
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/4] bootp: New net_bootp6 command
  2020-06-04  7:33 [PATCH 0/4] UEFI IPv6 and DHCPv6 support Javier Martinez Canillas
  2020-06-04  7:33 ` [PATCH 1/4] net: read bracketed ipv6 addrs and port numbers Javier Martinez Canillas
@ 2020-06-04  7:33 ` Javier Martinez Canillas
  2020-06-04  7:33 ` [PATCH 3/4] efinet: UEFI IPv6 PXE support Javier Martinez Canillas
  2 siblings, 0 replies; 9+ messages in thread
From: Javier Martinez Canillas @ 2020-06-04  7:33 UTC (permalink / raw)
  To: grub-devel
  Cc: Daniel Kiper, Michael Chang, Ken Lin, Peter Jones,
	Javier Martinez Canillas

From: Michael Chang <mchang@suse.com>

Implement new net_bootp6 command for IPv6 network auto configuration via the
DHCPv6 protocol (RFC3315).

Signed-off-by: Michael Chang <mchang@suse.com>
Signed-off-by: Ken Lin <ken.lin@hpe.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---

 grub-core/net/bootp.c | 914 +++++++++++++++++++++++++++++++++++++++++-
 grub-core/net/ip.c    |  39 ++
 grub-core/net/net.c   |  72 ++++
 grub-core/net/tftp.c  |   3 +
 include/grub/net.h    |  79 ++++
 5 files changed, 1106 insertions(+), 1 deletion(-)

diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
index 351ec2b4565..28441026569 100644
--- a/grub-core/net/bootp.c
+++ b/grub-core/net/bootp.c
@@ -24,6 +24,98 @@
 #include <grub/net/netbuff.h>
 #include <grub/net/udp.h>
 #include <grub/datetime.h>
+#include <grub/time.h>
+#include <grub/list.h>
+
+static int
+dissect_url (const char *url, char **proto, char **host, char **path)
+{
+  const char *p, *ps;
+  grub_size_t l;
+
+  *proto = *host = *path = NULL;
+  ps = p = url;
+
+  while ((p = grub_strchr (p, ':')))
+    {
+      if (grub_strlen (p) < sizeof ("://") - 1)
+	break;
+      if (grub_memcmp (p, "://", sizeof ("://") - 1) == 0)
+	{
+	  l = p - ps;
+	  *proto = grub_malloc (l + 1);
+	  if (!*proto)
+	    {
+	      grub_print_error ();
+	      return 0;
+	    }
+
+	  grub_memcpy (*proto, ps, l);
+	  (*proto)[l] = '\0';
+	  p +=  sizeof ("://") - 1;
+	  break;
+	}
+      ++p;
+    }
+
+  if (!*proto)
+    {
+      grub_dprintf ("bootp", "url: %s is not valid, protocol not found\n", url);
+      return 0;
+    }
+
+  ps = p;
+  p = grub_strchr (p, '/');
+
+  if (!p)
+    {
+      grub_dprintf ("bootp", "url: %s is not valid, host/path not found\n", url);
+      grub_free (*proto);
+      *proto = NULL;
+      return 0;
+    }
+
+  l = p - ps;
+
+  if (l > 2 && ps[0] == '[' && ps[l - 1] == ']')
+    {
+      *host = grub_malloc (l - 1);
+      if (!*host)
+	{
+	  grub_print_error ();
+	  grub_free (*proto);
+	  *proto = NULL;
+	  return 0;
+	}
+      grub_memcpy (*host, ps + 1, l - 2);
+      (*host)[l - 2] = 0;
+    }
+  else
+    {
+      *host = grub_malloc (l + 1);
+      if (!*host)
+	{
+	  grub_print_error ();
+	  grub_free (*proto);
+	  *proto = NULL;
+	  return 0;
+	}
+      grub_memcpy (*host, ps, l);
+      (*host)[l] = 0;
+    }
+
+  *path = grub_strdup (p);
+  if (!*path)
+    {
+      grub_print_error ();
+      grub_free (*host);
+      grub_free (*proto);
+      *host = NULL;
+      *proto = NULL;
+      return 0;
+    }
+  return 1;
+}
 
 struct grub_dhcp_discover_options
 {
@@ -607,6 +699,584 @@ out:
   return err;
 }
 
+/* The default netbuff size for sending DHCPv6 packets which should be
+   large enough to hold the information */
+#define GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE 512
+
+struct grub_dhcp6_options
+{
+  grub_uint8_t *client_duid;
+  grub_uint16_t client_duid_len;
+  grub_uint8_t *server_duid;
+  grub_uint16_t server_duid_len;
+  grub_uint32_t iaid;
+  grub_uint32_t t1;
+  grub_uint32_t t2;
+  grub_net_network_level_address_t *ia_addr;
+  grub_uint32_t preferred_lifetime;
+  grub_uint32_t valid_lifetime;
+  grub_net_network_level_address_t *dns_server_addrs;
+  grub_uint16_t num_dns_server;
+  char *boot_file_proto;
+  char *boot_file_server_ip;
+  char *boot_file_path;
+};
+
+typedef struct grub_dhcp6_options *grub_dhcp6_options_t;
+
+struct grub_dhcp6_session
+{
+  struct grub_dhcp6_session *next;
+  struct grub_dhcp6_session **prev;
+  grub_uint32_t iaid;
+  grub_uint32_t transaction_id:24;
+  grub_uint64_t start_time;
+  struct grub_net_dhcp6_option_duid_ll duid;
+  struct grub_net_network_level_interface *iface;
+
+  /* The associated dhcpv6 options */
+  grub_dhcp6_options_t adv;
+  grub_dhcp6_options_t reply;
+};
+
+typedef struct grub_dhcp6_session *grub_dhcp6_session_t;
+
+typedef void (*dhcp6_option_hook_fn) (const struct grub_net_dhcp6_option *opt, void *data);
+
+static void
+foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size,
+		      dhcp6_option_hook_fn hook, void *hook_data);
+
+static void
+parse_dhcp6_iaaddr (const struct grub_net_dhcp6_option *opt, void *data)
+{
+  grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t )data;
+
+  grub_uint16_t code = grub_be_to_cpu16 (opt->code);
+  grub_uint16_t len = grub_be_to_cpu16 (opt->len);
+
+  if (code == GRUB_NET_DHCP6_OPTION_IAADDR)
+    {
+      const struct grub_net_dhcp6_option_iaaddr *iaaddr;
+      iaaddr = (const struct grub_net_dhcp6_option_iaaddr *)opt->data;
+
+      if (len < sizeof (*iaaddr))
+	{
+	  grub_dprintf ("bootp", "DHCPv6: code %u with insufficient length %u\n", code, len);
+	  return;
+	}
+      if (!dhcp6->ia_addr)
+	{
+	  dhcp6->ia_addr = grub_malloc (sizeof(*dhcp6->ia_addr));
+	  dhcp6->ia_addr->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+	  dhcp6->ia_addr->ipv6[0] = grub_get_unaligned64 (iaaddr->addr);
+	  dhcp6->ia_addr->ipv6[1] = grub_get_unaligned64 (iaaddr->addr + 8);
+	  dhcp6->preferred_lifetime = grub_be_to_cpu32 (iaaddr->preferred_lifetime);
+	  dhcp6->valid_lifetime = grub_be_to_cpu32 (iaaddr->valid_lifetime);
+	}
+    }
+}
+
+static void
+parse_dhcp6_option (const struct grub_net_dhcp6_option *opt, void *data)
+{
+  grub_dhcp6_options_t dhcp6 = (grub_dhcp6_options_t)data;
+  grub_uint16_t code = grub_be_to_cpu16 (opt->code);
+  grub_uint16_t len = grub_be_to_cpu16 (opt->len);
+
+  switch (code)
+    {
+      case GRUB_NET_DHCP6_OPTION_CLIENTID:
+
+	if (dhcp6->client_duid || !len)
+	  {
+	    grub_dprintf ("bootp", "Skipped DHCPv6 CLIENTID with length %u\n", len);
+	    break;
+	  }
+	dhcp6->client_duid = grub_malloc (len);
+	grub_memcpy (dhcp6->client_duid, opt->data, len);
+	dhcp6->client_duid_len = len;
+	break;
+
+      case GRUB_NET_DHCP6_OPTION_SERVERID:
+
+	if (dhcp6->server_duid || !len)
+	  {
+	    grub_dprintf ("bootp", "Skipped DHCPv6 SERVERID with length %u\n", len);
+	    break;
+	  }
+	dhcp6->server_duid = grub_malloc (len);
+	grub_memcpy (dhcp6->server_duid, opt->data, len);
+	dhcp6->server_duid_len = len;
+	break;
+
+      case GRUB_NET_DHCP6_OPTION_IA_NA:
+	{
+	  const struct grub_net_dhcp6_option_iana *ia_na;
+	  grub_uint16_t data_len;
+
+	  if (dhcp6->iaid || len < sizeof (*ia_na))
+	    {
+	      grub_dprintf ("bootp", "Skipped DHCPv6 IA_NA with length %u\n", len);
+	      break;
+	    }
+	  ia_na = (const struct grub_net_dhcp6_option_iana *)opt->data;
+	  dhcp6->iaid = grub_be_to_cpu32 (ia_na->iaid);
+	  dhcp6->t1 = grub_be_to_cpu32 (ia_na->t1);
+	  dhcp6->t2 = grub_be_to_cpu32 (ia_na->t2);
+
+	  data_len = len - sizeof (*ia_na);
+	  if (data_len)
+	    foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)ia_na->data, data_len, parse_dhcp6_iaaddr, dhcp6);
+	}
+	break;
+
+      case GRUB_NET_DHCP6_OPTION_DNS_SERVERS:
+	{
+	  const grub_uint8_t *po;
+	  grub_uint16_t ln;
+	  grub_net_network_level_address_t *la;
+
+	  if (!len || len & 0xf)
+	    {
+	      grub_dprintf ("bootp", "Skip invalid length DHCPv6 DNS_SERVERS \n");
+	      break;
+	    }
+	  dhcp6->num_dns_server = ln = len >> 4;
+	  dhcp6->dns_server_addrs = la = grub_zalloc (ln * sizeof (*la));
+
+	  for (po = opt->data; ln > 0; po += 0x10, la++, ln--)
+	    {
+	      la->type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+	      la->ipv6[0] = grub_get_unaligned64 (po);
+	      la->ipv6[1] = grub_get_unaligned64 (po + 8);
+	      la->option = DNS_OPTION_PREFER_IPV6;
+	    }
+	}
+	break;
+
+      case GRUB_NET_DHCP6_OPTION_BOOTFILE_URL:
+	dissect_url ((const char *)opt->data,
+		      &dhcp6->boot_file_proto,
+		      &dhcp6->boot_file_server_ip,
+		      &dhcp6->boot_file_path);
+	break;
+
+      default:
+	break;
+    }
+}
+
+static void
+foreach_dhcp6_option (const struct grub_net_dhcp6_option *opt, grub_size_t size, dhcp6_option_hook_fn hook, void *hook_data)
+{
+  while (size)
+    {
+      grub_uint16_t code, len;
+
+      if (size < sizeof (*opt))
+	{
+	  grub_dprintf ("bootp", "DHCPv6: Options stopped with remaining size %" PRIxGRUB_SIZE "\n", size);
+	  break;
+	}
+      size -= sizeof (*opt);
+      len = grub_be_to_cpu16 (opt->len);
+      code = grub_be_to_cpu16 (opt->code);
+      if (size < len)
+	{
+	  grub_dprintf ("bootp", "DHCPv6: Options stopped at out of bound length %u for option %u\n", len, code);
+	  break;
+	}
+      if (!len)
+	{
+	  grub_dprintf ("bootp", "DHCPv6: Options stopped at zero length option %u\n", code);
+	  break;
+	}
+      else
+	{
+	  if (hook)
+	    hook (opt, hook_data);
+	  size -= len;
+	  opt = (const struct grub_net_dhcp6_option *)((grub_uint8_t *)opt + len + sizeof (*opt));
+	}
+    }
+}
+
+static grub_dhcp6_options_t
+grub_dhcp6_options_get (const struct grub_net_dhcp6_packet *v6h,
+			grub_size_t size)
+{
+  grub_dhcp6_options_t options;
+
+  if (size < sizeof (*v6h))
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE, N_("DHCPv6 packet size too small"));
+      return NULL;
+    }
+
+  options = grub_zalloc (sizeof(*options));
+  if (!options)
+    return NULL;
+
+  foreach_dhcp6_option ((const struct grub_net_dhcp6_option *)v6h->dhcp_options,
+		       size - sizeof (*v6h), parse_dhcp6_option, options);
+
+  return options;
+}
+
+static void
+grub_dhcp6_options_free (grub_dhcp6_options_t options)
+{
+  if (options->client_duid)
+    grub_free (options->client_duid);
+  if (options->server_duid)
+    grub_free (options->server_duid);
+  if (options->ia_addr)
+    grub_free (options->ia_addr);
+  if (options->dns_server_addrs)
+    grub_free (options->dns_server_addrs);
+  if (options->boot_file_proto)
+    grub_free (options->boot_file_proto);
+  if (options->boot_file_server_ip)
+    grub_free (options->boot_file_server_ip);
+  if (options->boot_file_path)
+    grub_free (options->boot_file_path);
+
+  grub_free (options);
+}
+
+static grub_dhcp6_session_t grub_dhcp6_sessions;
+#define FOR_DHCP6_SESSIONS_SAFE(var, next) FOR_LIST_ELEMENTS_SAFE (var, next, grub_dhcp6_sessions)
+#define FOR_DHCP6_SESSIONS(var) FOR_LIST_ELEMENTS (var, grub_dhcp6_sessions)
+
+static void
+grub_net_configure_by_dhcp6_info (const char *name,
+	  struct grub_net_card *card,
+	  grub_dhcp6_options_t dhcp6,
+	  int is_def,
+	  int flags,
+	  struct grub_net_network_level_interface **ret_inf)
+{
+  grub_net_network_level_netaddress_t netaddr;
+  struct grub_net_network_level_interface *inf;
+
+  if (dhcp6->ia_addr)
+    {
+      inf = grub_net_add_addr (name, card, dhcp6->ia_addr, &card->default_address, flags);
+
+      netaddr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+      netaddr.ipv6.base[0] = dhcp6->ia_addr->ipv6[0];
+      netaddr.ipv6.base[1] = 0;
+      netaddr.ipv6.masksize = 64;
+      grub_net_add_route (name, netaddr, inf);
+
+      if (ret_inf)
+	*ret_inf = inf;
+    }
+
+  if (dhcp6->dns_server_addrs)
+    {
+      grub_uint16_t i;
+
+      for (i = 0; i < dhcp6->num_dns_server; ++i)
+	grub_net_add_dns_server (dhcp6->dns_server_addrs + i);
+    }
+
+  if (dhcp6->boot_file_path)
+    grub_env_set_net_property (name, "boot_file", dhcp6->boot_file_path,
+			  grub_strlen (dhcp6->boot_file_path));
+
+  if (is_def && dhcp6->boot_file_server_ip)
+    {
+      grub_net_default_server = grub_strdup (dhcp6->boot_file_server_ip);
+      grub_env_set ("net_default_interface", name);
+      grub_env_export ("net_default_interface");
+    }
+}
+
+static void
+grub_dhcp6_session_add (struct grub_net_network_level_interface *iface,
+			grub_uint32_t iaid)
+{
+  grub_dhcp6_session_t se;
+  struct grub_datetime date;
+  grub_err_t err;
+  grub_int32_t t = 0;
+
+  se = grub_malloc (sizeof (*se));
+
+  err = grub_get_datetime (&date);
+  if (err || !grub_datetime2unixtime (&date, &t))
+    {
+      grub_errno = GRUB_ERR_NONE;
+      t = 0;
+    }
+
+  se->iface = iface;
+  se->iaid = iaid;
+  se->transaction_id = t;
+  se->start_time = grub_get_time_ms ();
+  se->duid.type = grub_cpu_to_be16_compile_time (3) ;
+  se->duid.hw_type = grub_cpu_to_be16_compile_time (1);
+  grub_memcpy (&se->duid.hwaddr, &iface->hwaddress.mac, sizeof (se->duid.hwaddr));
+  se->adv = NULL;
+  se->reply = NULL;
+  grub_list_push (GRUB_AS_LIST_P (&grub_dhcp6_sessions), GRUB_AS_LIST (se));
+}
+
+static void
+grub_dhcp6_session_remove (grub_dhcp6_session_t se)
+{
+  grub_list_remove (GRUB_AS_LIST (se));
+  if (se->adv)
+    grub_dhcp6_options_free (se->adv);
+  if (se->reply)
+    grub_dhcp6_options_free (se->reply);
+  grub_free (se);
+}
+
+static void
+grub_dhcp6_session_remove_all (void)
+{
+  grub_dhcp6_session_t se, next;
+
+  FOR_DHCP6_SESSIONS_SAFE (se, next)
+    {
+      grub_dhcp6_session_remove (se);
+    }
+  grub_dhcp6_sessions = NULL;
+}
+
+static grub_err_t
+grub_dhcp6_session_configure_network (grub_dhcp6_session_t se)
+{
+  char *name;
+
+  name = grub_xasprintf ("%s:dhcp6", se->iface->card->name);
+  if (!name)
+    return grub_errno;
+
+  grub_net_configure_by_dhcp6_info (name, se->iface->card, se->reply, 1, 0, 0);
+  grub_free (name);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_dhcp6_session_send_request (grub_dhcp6_session_t se)
+{
+  struct grub_net_buff *nb;
+  struct grub_net_dhcp6_option *opt;
+  struct grub_net_dhcp6_packet *v6h;
+  struct grub_net_dhcp6_option_iana *ia_na;
+  struct grub_net_dhcp6_option_iaaddr *iaaddr;
+  struct udphdr *udph;
+  grub_net_network_level_address_t multicast;
+  grub_net_link_level_address_t ll_multicast;
+  grub_uint64_t elapsed;
+  struct grub_net_network_level_interface *inf = se->iface;
+  grub_dhcp6_options_t dhcp6 = se->adv;
+  grub_err_t err = GRUB_ERR_NONE;
+
+  multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+  multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48);
+  multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL);
+
+  err = grub_net_link_layer_resolve (inf, &multicast, &ll_multicast);
+  if (err)
+    return err;
+
+  nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
+
+  if (!nb)
+    return grub_errno;
+
+  err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
+  if (err)
+    {
+      grub_netbuff_free (nb);
+      return err;
+    }
+
+  err = grub_netbuff_push (nb, dhcp6->client_duid_len + sizeof (*opt));
+  if (err)
+    {
+      grub_netbuff_free (nb);
+      return err;
+    }
+  opt = (struct grub_net_dhcp6_option *)nb->data;
+  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID);
+  opt->len = grub_cpu_to_be16 (dhcp6->client_duid_len);
+  grub_memcpy (opt->data, dhcp6->client_duid , dhcp6->client_duid_len);
+
+  err = grub_netbuff_push (nb, dhcp6->server_duid_len + sizeof (*opt));
+  if (err)
+    {
+      grub_netbuff_free (nb);
+      return err;
+    }
+  opt = (struct grub_net_dhcp6_option *)nb->data;
+  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_SERVERID);
+  opt->len = grub_cpu_to_be16 (dhcp6->server_duid_len);
+  grub_memcpy (opt->data, dhcp6->server_duid , dhcp6->server_duid_len);
+
+  err = grub_netbuff_push (nb, sizeof (*ia_na) + sizeof (*opt));
+  if (err)
+    {
+      grub_netbuff_free (nb);
+      return err;
+    }
+
+  if (dhcp6->ia_addr)
+    {
+      err = grub_netbuff_push (nb, sizeof(*iaaddr) + sizeof (*opt));
+      if (err)
+	{
+	  grub_netbuff_free (nb);
+	  return err;
+	}
+    }
+  opt = (struct grub_net_dhcp6_option *)nb->data;
+  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
+  opt->len = grub_cpu_to_be16 (sizeof (*ia_na));
+  if (dhcp6->ia_addr)
+    opt->len += grub_cpu_to_be16 (sizeof(*iaaddr) + sizeof (*opt));
+
+  ia_na = (struct grub_net_dhcp6_option_iana *)opt->data;
+  ia_na->iaid = grub_cpu_to_be32 (dhcp6->iaid);
+
+  ia_na->t1 = grub_cpu_to_be32 (dhcp6->t1);
+  ia_na->t2 = grub_cpu_to_be32 (dhcp6->t2);
+
+  if (dhcp6->ia_addr)
+    {
+      opt = (struct grub_net_dhcp6_option *)ia_na->data;
+      opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IAADDR);
+      opt->len = grub_cpu_to_be16 (sizeof (*iaaddr));
+      iaaddr = (struct grub_net_dhcp6_option_iaaddr *)opt->data;
+      grub_set_unaligned64 (iaaddr->addr, dhcp6->ia_addr->ipv6[0]);
+      grub_set_unaligned64 (iaaddr->addr + 8, dhcp6->ia_addr->ipv6[1]);
+
+      iaaddr->preferred_lifetime = grub_cpu_to_be32 (dhcp6->preferred_lifetime);
+      iaaddr->valid_lifetime = grub_cpu_to_be32 (dhcp6->valid_lifetime);
+    }
+
+  err = grub_netbuff_push (nb, sizeof (*opt) + 2 * sizeof (grub_uint16_t));
+  if (err)
+    {
+      grub_netbuff_free (nb);
+      return err;
+    }
+
+  opt = (struct grub_net_dhcp6_option*) nb->data;
+  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ORO);
+  opt->len = grub_cpu_to_be16_compile_time (2 * sizeof (grub_uint16_t));
+  grub_set_unaligned16 (opt->data, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_BOOTFILE_URL));
+  grub_set_unaligned16 (opt->data + 2, grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_DNS_SERVERS));
+
+  err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t));
+  if (err)
+    {
+      grub_netbuff_free (nb);
+      return err;
+    }
+  opt = (struct grub_net_dhcp6_option*) nb->data;
+  opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME);
+  opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t));
+
+  /* the time is expressed in hundredths of a second */
+  elapsed = grub_divmod64 (grub_get_time_ms () - se->start_time, 10, 0);
+
+  if (elapsed > 0xffff)
+    elapsed = 0xffff;
+
+  grub_set_unaligned16 (opt->data,  grub_cpu_to_be16 ((grub_uint16_t)elapsed));
+
+  err = grub_netbuff_push (nb, sizeof (*v6h));
+  if (err)
+    {
+      grub_netbuff_free (nb);
+      return err;
+    }
+
+  v6h = (struct grub_net_dhcp6_packet *) nb->data;
+  v6h->message_type = GRUB_NET_DHCP6_REQUEST;
+  v6h->transaction_id = se->transaction_id;
+
+  err = grub_netbuff_push (nb, sizeof (*udph));
+  if (err)
+    {
+      grub_netbuff_free (nb);
+      return err;
+    }
+
+  udph = (struct udphdr *) nb->data;
+  udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT);
+  udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT);
+  udph->chksum = 0;
+  udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
+
+  udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
+						 &inf->address,
+						 &multicast);
+  err = grub_net_send_ip_packet (inf, &multicast, &ll_multicast, nb,
+				 GRUB_NET_IP_UDP);
+
+  grub_netbuff_free (nb);
+
+  return err;
+}
+
+struct grub_net_network_level_interface *
+grub_net_configure_by_dhcpv6_reply (const char *name,
+	struct grub_net_card *card,
+	grub_net_interface_flags_t flags,
+	const struct grub_net_dhcp6_packet *v6h,
+	grub_size_t size,
+	int is_def,
+	char **device, char **path)
+{
+  struct grub_net_network_level_interface *inf;
+  grub_dhcp6_options_t dhcp6;
+  int mask = -1;
+
+  dhcp6 = grub_dhcp6_options_get (v6h, size);
+  if (!dhcp6)
+    {
+      grub_print_error ();
+      return NULL;
+    }
+
+  grub_net_configure_by_dhcp6_info (name, card, dhcp6, is_def, flags, &inf);
+
+  if (device && dhcp6->boot_file_proto && dhcp6->boot_file_server_ip)
+    {
+      *device = grub_xasprintf ("%s,%s", dhcp6->boot_file_proto, dhcp6->boot_file_server_ip);
+      grub_print_error ();
+    }
+  if (path && dhcp6->boot_file_path)
+    {
+      *path = grub_strdup (dhcp6->boot_file_path);
+      grub_print_error ();
+      if (*path)
+	{
+	  char *slash;
+	  slash = grub_strrchr (*path, '/');
+	  if (slash)
+	    *slash = 0;
+	  else
+	    **path = 0;
+	}
+    }
+
+  grub_dhcp6_options_free (dhcp6);
+
+  if (inf)
+    grub_net_add_ipv6_local (inf, mask);
+
+  return inf;
+}
+
 /*
  * This is called directly from net/ip.c:handle_dgram(), because those
  * BOOTP/DHCP packets are a bit special due to their improper
@@ -675,6 +1345,77 @@ grub_net_process_dhcp (struct grub_net_buff *nb,
     }
 }
 
+grub_err_t
+grub_net_process_dhcp6 (struct grub_net_buff *nb,
+                       struct grub_net_card *card __attribute__ ((unused)))
+{
+  const struct grub_net_dhcp6_packet *v6h;
+  grub_dhcp6_session_t se;
+  grub_size_t size;
+  grub_dhcp6_options_t options;
+
+  v6h = (const struct grub_net_dhcp6_packet *) nb->data;
+  size = nb->tail - nb->data;
+
+  options = grub_dhcp6_options_get (v6h, size);
+  if (!options)
+    return grub_errno;
+
+  if (!options->client_duid || !options->server_duid || !options->ia_addr)
+    {
+      grub_dhcp6_options_free (options);
+      return grub_error (GRUB_ERR_BAD_ARGUMENT, "Bad DHCPv6 Packet");
+    }
+
+  FOR_DHCP6_SESSIONS (se)
+    {
+      if (se->transaction_id == v6h->transaction_id &&
+         grub_memcmp (options->client_duid, &se->duid, sizeof (se->duid)) == 0 &&
+         se->iaid == options->iaid)
+       break;
+    }
+
+  if (!se)
+    {
+      grub_dprintf ("bootp", "DHCPv6 session not found\n");
+      grub_dhcp6_options_free (options);
+      return GRUB_ERR_NONE;
+    }
+
+  if (v6h->message_type == GRUB_NET_DHCP6_ADVERTISE)
+    {
+      if (se->adv)
+       {
+         grub_dprintf ("bootp", "Skipped DHCPv6 Advertised .. \n");
+         grub_dhcp6_options_free (options);
+         return GRUB_ERR_NONE;
+       }
+
+      se->adv = options;
+      return grub_dhcp6_session_send_request (se);
+    }
+  else if (v6h->message_type == GRUB_NET_DHCP6_REPLY)
+    {
+      if (!se->adv)
+       {
+         grub_dprintf ("bootp", "Skipped DHCPv6 Reply .. \n");
+         grub_dhcp6_options_free (options);
+         return GRUB_ERR_NONE;
+       }
+
+      se->reply = options;
+      grub_dhcp6_session_configure_network (se);
+      grub_dhcp6_session_remove (se);
+      return GRUB_ERR_NONE;
+    }
+  else
+    {
+      grub_dhcp6_options_free (options);
+    }
+
+  return GRUB_ERR_NONE;
+}
+
 static grub_err_t
 grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
 		  int argc, char **args)
@@ -900,7 +1641,174 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
   return err;
 }
 
-static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp;
+static grub_err_t
+grub_cmd_bootp6 (struct grub_command *cmd __attribute__ ((unused)),
+                 int argc, char **args)
+{
+  struct grub_net_card *card;
+  grub_uint32_t iaid = 0;
+  int interval;
+  grub_err_t err;
+  grub_dhcp6_session_t se;
+
+  err = GRUB_ERR_NONE;
+
+  FOR_NET_CARDS (card)
+  {
+    struct grub_net_network_level_interface *iface;
+
+    if (argc > 0 && grub_strcmp (card->name, args[0]) != 0)
+      continue;
+
+    iface = grub_net_ipv6_get_link_local (card, &card->default_address);
+    if (!iface)
+      {
+       grub_dhcp6_session_remove_all ();
+       return grub_errno;
+      }
+
+    grub_dhcp6_session_add (iface, iaid++);
+  }
+
+  for (interval = 200; interval < 10000; interval *= 2)
+    {
+      int done = 1;
+
+      FOR_DHCP6_SESSIONS (se)
+       {
+         struct grub_net_buff *nb;
+         struct grub_net_dhcp6_option *opt;
+         struct grub_net_dhcp6_packet *v6h;
+         struct grub_net_dhcp6_option_duid_ll *duid;
+         struct grub_net_dhcp6_option_iana *ia_na;
+         grub_net_network_level_address_t multicast;
+         grub_net_link_level_address_t ll_multicast;
+         struct udphdr *udph;
+
+         multicast.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+         multicast.ipv6[0] = grub_cpu_to_be64_compile_time (0xff02ULL << 48);
+         multicast.ipv6[1] = grub_cpu_to_be64_compile_time (0x10002ULL);
+
+         err = grub_net_link_layer_resolve (se->iface,
+                   &multicast, &ll_multicast);
+         if (err)
+           {
+             grub_dhcp6_session_remove_all ();
+             return err;
+           }
+
+         nb = grub_netbuff_alloc (GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
+
+         if (!nb)
+           {
+             grub_dhcp6_session_remove_all ();
+             return grub_errno;
+           }
+
+         err = grub_netbuff_reserve (nb, GRUB_DHCP6_DEFAULT_NETBUFF_ALLOC_SIZE);
+         if (err)
+           {
+             grub_dhcp6_session_remove_all ();
+             grub_netbuff_free (nb);
+             return err;
+           }
+
+         err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (grub_uint16_t));
+         if (err)
+           {
+             grub_dhcp6_session_remove_all ();
+             grub_netbuff_free (nb);
+             return err;
+           }
+
+         opt = (struct grub_net_dhcp6_option *)nb->data;
+         opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_ELAPSED_TIME);
+         opt->len = grub_cpu_to_be16_compile_time (sizeof (grub_uint16_t));
+         grub_set_unaligned16 (opt->data, 0);
+
+         err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*duid));
+         if (err)
+           {
+             grub_dhcp6_session_remove_all ();
+             grub_netbuff_free (nb);
+             return err;
+           }
+
+         opt = (struct grub_net_dhcp6_option *)nb->data;
+         opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_CLIENTID);
+         opt->len = grub_cpu_to_be16 (sizeof (*duid));
+
+         duid = (struct grub_net_dhcp6_option_duid_ll *) opt->data;
+         grub_memcpy (duid, &se->duid, sizeof (*duid));
+
+         err = grub_netbuff_push (nb, sizeof (*opt) + sizeof (*ia_na));
+         if (err)
+           {
+             grub_dhcp6_session_remove_all ();
+             grub_netbuff_free (nb);
+             return err;
+           }
+
+         opt = (struct grub_net_dhcp6_option *)nb->data;
+         opt->code = grub_cpu_to_be16_compile_time (GRUB_NET_DHCP6_OPTION_IA_NA);
+         opt->len = grub_cpu_to_be16 (sizeof (*ia_na));
+         ia_na = (struct grub_net_dhcp6_option_iana *)opt->data;
+         ia_na->iaid = grub_cpu_to_be32 (se->iaid);
+         ia_na->t1 = 0;
+         ia_na->t2 = 0;
+
+         err = grub_netbuff_push (nb, sizeof (*v6h));
+         if (err)
+           {
+             grub_dhcp6_session_remove_all ();
+             grub_netbuff_free (nb);
+             return err;
+           }
+
+         v6h = (struct grub_net_dhcp6_packet *)nb->data;
+         v6h->message_type = GRUB_NET_DHCP6_SOLICIT;
+         v6h->transaction_id = se->transaction_id;
+
+         grub_netbuff_push (nb, sizeof (*udph));
+
+         udph = (struct udphdr *) nb->data;
+         udph->src = grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT);
+         udph->dst = grub_cpu_to_be16_compile_time (DHCP6_SERVER_PORT);
+         udph->chksum = 0;
+         udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
+
+         udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
+                           &se->iface->address, &multicast);
+
+         err = grub_net_send_ip_packet (se->iface, &multicast,
+                   &ll_multicast, nb, GRUB_NET_IP_UDP);
+         done = 0;
+         grub_netbuff_free (nb);
+
+         if (err)
+           {
+             grub_dhcp6_session_remove_all ();
+             return err;
+           }
+       }
+      if (!done)
+       grub_net_poll_cards (interval, 0);
+    }
+
+  FOR_DHCP6_SESSIONS (se)
+    {
+      grub_error_push ();
+      err = grub_error (GRUB_ERR_FILE_NOT_FOUND,
+                       N_("couldn't autoconfigure %s"),
+                       se->iface->card->name);
+    }
+
+  grub_dhcp6_session_remove_all ();
+
+  return err;
+}
+
+static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp, cmd_bootp6;
 
 void
 grub_bootp_init (void)
@@ -914,11 +1822,15 @@ grub_bootp_init (void)
   cmd_getdhcp = grub_register_command ("net_get_dhcp_option", grub_cmd_dhcpopt,
 				       N_("VAR INTERFACE NUMBER DESCRIPTION"),
 				       N_("retrieve DHCP option and save it into VAR. If VAR is - then print the value."));
+  cmd_bootp6 = grub_register_command ("net_bootp6", grub_cmd_bootp6,
+				     N_("[CARD]"),
+				     N_("perform a DHCPv6 autoconfiguration"));
 }
 
 void
 grub_bootp_fini (void)
 {
+  grub_unregister_command (cmd_bootp6);
   grub_unregister_command (cmd_getdhcp);
   grub_unregister_command (cmd_bootp);
   grub_unregister_command (cmd_dhcp);
diff --git a/grub-core/net/ip.c b/grub-core/net/ip.c
index ea5edf8f1f6..01410798b32 100644
--- a/grub-core/net/ip.c
+++ b/grub-core/net/ip.c
@@ -239,6 +239,45 @@ handle_dgram (struct grub_net_buff *nb,
   {
     struct udphdr *udph;
     udph = (struct udphdr *) nb->data;
+
+    if (proto == GRUB_NET_IP_UDP && udph->dst == grub_cpu_to_be16_compile_time (DHCP6_CLIENT_PORT))
+      {
+	if (udph->chksum)
+	  {
+	    grub_uint16_t chk, expected;
+	    chk = udph->chksum;
+	    udph->chksum = 0;
+	    expected = grub_net_ip_transport_checksum (nb,
+						       GRUB_NET_IP_UDP,
+						       source,
+						       dest);
+	    if (expected != chk)
+	      {
+		grub_dprintf ("net", "Invalid UDP checksum. "
+			      "Expected %x, got %x\n",
+			      grub_be_to_cpu16 (expected),
+			      grub_be_to_cpu16 (chk));
+		grub_netbuff_free (nb);
+		return GRUB_ERR_NONE;
+	      }
+	    udph->chksum = chk;
+	  }
+
+	err = grub_netbuff_pull (nb, sizeof (*udph));
+	if (err)
+	  {
+	    grub_netbuff_free (nb);
+	    return err;
+	  }
+
+	err = grub_net_process_dhcp6 (nb, card);
+	if (err)
+	  grub_print_error ();
+
+	grub_netbuff_free (nb);
+	return GRUB_ERR_NONE;
+      }
+
     if (proto == GRUB_NET_IP_UDP && grub_be_to_cpu16 (udph->dst) == 68)
       {
 	const struct grub_net_bootp_packet *bootp;
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
index 36bca041ac2..8e39c557e64 100644
--- a/grub-core/net/net.c
+++ b/grub-core/net/net.c
@@ -964,6 +964,78 @@ grub_net_network_level_interface_register (struct grub_net_network_level_interfa
   grub_net_network_level_interfaces = inter;
 }
 
+int
+grub_ipv6_get_masksize (grub_uint16_t be_mask[8])
+{
+  grub_uint8_t *mask;
+  grub_uint16_t mask16[8];
+  int x, y;
+  int ret = 128;
+
+  grub_memcpy (mask16, be_mask, sizeof (mask16));
+  for (x = 0; x < 8; x++)
+    mask16[x] = grub_be_to_cpu16 (mask16[x]);
+
+  mask = (grub_uint8_t *)mask16;
+
+  for (x = 15; x >= 0; x--)
+    {
+      grub_uint8_t octet = mask[x];
+      if (!octet)
+	{
+	  ret -= 8;
+	  continue;
+	}
+      for (y = 0; y < 8; y++)
+	{
+	  if (octet & (1 << y))
+	    break;
+	  else
+	    ret--;
+	}
+      break;
+    }
+
+  return ret;
+}
+
+grub_err_t
+grub_net_add_ipv6_local (struct grub_net_network_level_interface *inter,
+			 int mask)
+{
+  struct grub_net_route *route;
+
+  if (inter->address.type != GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6)
+    return 0;
+
+  if (mask == -1)
+      mask = grub_ipv6_get_masksize ((grub_uint16_t *)inter->address.ipv6);
+
+  if (mask == -1)
+    return 0;
+
+  route = grub_zalloc (sizeof (*route));
+  if (!route)
+    return grub_errno;
+
+  route->name = grub_xasprintf ("%s:local", inter->name);
+  if (!route->name)
+    {
+      grub_free (route);
+      return grub_errno;
+    }
+
+  route->target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+  grub_memcpy (route->target.ipv6.base, inter->address.ipv6,
+	       sizeof (inter->address.ipv6));
+  route->target.ipv6.masksize = mask;
+  route->is_gateway = 0;
+  route->interface = inter;
+
+  grub_net_route_register (route);
+
+  return 0;
+}
 
 grub_err_t
 grub_net_add_ipv4_local (struct grub_net_network_level_interface *inter,
diff --git a/grub-core/net/tftp.c b/grub-core/net/tftp.c
index c7873465662..4516447c5b8 100644
--- a/grub-core/net/tftp.c
+++ b/grub-core/net/tftp.c
@@ -405,6 +405,7 @@ tftp_open (struct grub_file *file, const char *filename)
       return grub_errno;
     }
 
+  grub_dprintf("tftp", "resolving address for %s\n", file->device->net->server);
   err = grub_net_resolve_address (file->device->net->server, &addr);
   if (err)
     {
@@ -417,11 +418,13 @@ tftp_open (struct grub_file *file, const char *filename)
       return err;
     }
 
+  grub_dprintf("tftp", "opening connection\n");
   data->sock = grub_net_udp_open (addr,
 				  port ? port : TFTP_SERVER_PORT, tftp_receive,
 				  file);
   if (!data->sock)
     {
+      grub_dprintf("tftp", "connection failed\n");
       destroy_pq (data);
       grub_free (data);
       return grub_errno;
diff --git a/include/grub/net.h b/include/grub/net.h
index 69bfe0947b2..848bbeae00c 100644
--- a/include/grub/net.h
+++ b/include/grub/net.h
@@ -448,6 +448,66 @@ struct grub_net_bootp_packet
   grub_uint8_t vendor[0];
 } GRUB_PACKED;
 
+struct grub_net_dhcp6_packet
+{
+  grub_uint32_t message_type:8;
+  grub_uint32_t transaction_id:24;
+  grub_uint8_t dhcp_options[0];
+} GRUB_PACKED;
+
+struct grub_net_dhcp6_option {
+  grub_uint16_t code;
+  grub_uint16_t len;
+  grub_uint8_t data[0];
+} GRUB_PACKED;
+
+struct grub_net_dhcp6_option_iana {
+  grub_uint32_t iaid;
+  grub_uint32_t t1;
+  grub_uint32_t t2;
+  grub_uint8_t data[0];
+} GRUB_PACKED;
+
+struct grub_net_dhcp6_option_iaaddr {
+  grub_uint8_t addr[16];
+  grub_uint32_t preferred_lifetime;
+  grub_uint32_t valid_lifetime;
+  grub_uint8_t data[0];
+} GRUB_PACKED;
+
+struct grub_net_dhcp6_option_duid_ll
+{
+  grub_uint16_t type;
+  grub_uint16_t hw_type;
+  grub_uint8_t hwaddr[6];
+} GRUB_PACKED;
+
+enum
+  {
+    GRUB_NET_DHCP6_SOLICIT = 1,
+    GRUB_NET_DHCP6_ADVERTISE = 2,
+    GRUB_NET_DHCP6_REQUEST = 3,
+    GRUB_NET_DHCP6_REPLY = 7
+  };
+
+enum
+  {
+    DHCP6_CLIENT_PORT = 546,
+    DHCP6_SERVER_PORT = 547
+  };
+
+enum
+  {
+    GRUB_NET_DHCP6_OPTION_CLIENTID = 1,
+    GRUB_NET_DHCP6_OPTION_SERVERID = 2,
+    GRUB_NET_DHCP6_OPTION_IA_NA = 3,
+    GRUB_NET_DHCP6_OPTION_IAADDR = 5,
+    GRUB_NET_DHCP6_OPTION_ORO = 6,
+    GRUB_NET_DHCP6_OPTION_ELAPSED_TIME = 8,
+    GRUB_NET_DHCP6_OPTION_DNS_SERVERS = 23,
+    GRUB_NET_DHCP6_OPTION_BOOTFILE_URL = 59
+  };
+
 #define	GRUB_NET_BOOTP_RFC1048_MAGIC_0	0x63
 #define	GRUB_NET_BOOTP_RFC1048_MAGIC_1	0x82
 #define	GRUB_NET_BOOTP_RFC1048_MAGIC_2	0x53
@@ -483,6 +543,21 @@ grub_net_configure_by_dhcp_ack (const char *name,
 				grub_size_t size,
 				int is_def, char **device, char **path);
 
+struct grub_net_network_level_interface *
+grub_net_configure_by_dhcpv6_reply (const char *name,
+				    struct grub_net_card *card,
+				    grub_net_interface_flags_t flags,
+				    const struct grub_net_dhcp6_packet *v6,
+				    grub_size_t size,
+				    int is_def, char **device, char **path);
+
+int
+grub_ipv6_get_masksize(grub_uint16_t *mask);
+
+grub_err_t
+grub_net_add_ipv6_local (struct grub_net_network_level_interface *inf,
+			 int mask);
+
 grub_err_t
 grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf,
 			 int mask);
@@ -491,6 +566,10 @@ void
 grub_net_process_dhcp (struct grub_net_buff *nb,
 		       struct grub_net_network_level_interface *iface);
 
+grub_err_t
+grub_net_process_dhcp6 (struct grub_net_buff *nb,
+			struct grub_net_card *card);
+
 int
 grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,
 		     const grub_net_link_level_address_t *b);
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/4] efinet: UEFI IPv6 PXE support
  2020-06-04  7:33 [PATCH 0/4] UEFI IPv6 and DHCPv6 support Javier Martinez Canillas
  2020-06-04  7:33 ` [PATCH 1/4] net: read bracketed ipv6 addrs and port numbers Javier Martinez Canillas
  2020-06-04  7:33 ` [PATCH 2/4] bootp: New net_bootp6 command Javier Martinez Canillas
@ 2020-06-04  7:33 ` Javier Martinez Canillas
  2020-06-04 11:37   ` Thomas Frauendorfer
  2 siblings, 1 reply; 9+ messages in thread
From: Javier Martinez Canillas @ 2020-06-04  7:33 UTC (permalink / raw)
  To: grub-devel
  Cc: Daniel Kiper, Michael Chang, Ken Lin, Peter Jones,
	Javier Martinez Canillas

From: Michael Chang <mchang@suse.com>

When grub2 image is booted from UEFI IPv6 PXE, the DHCPv6 Reply packet is
cached in firmware buffer which can be obtained by PXE Base Code protocol. The
network interface can be setup through the parameters in that obtained packet.

Signed-off-by: Michael Chang <mchang@suse.com>
Signed-off-by: Ken Lin <ken.lin@hpe.com>
Signed-off-by: Peter Jones <pjones@redhat.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
---

 grub-core/net/drivers/efi/efinet.c |  45 +++++++--
 include/grub/efi/api.h             | 146 +++++++++++++++++++++++++++--
 2 files changed, 177 insertions(+), 14 deletions(-)

diff --git a/grub-core/net/drivers/efi/efinet.c b/grub-core/net/drivers/efi/efinet.c
index 5388f952ba9..7dbd353af5a 100644
--- a/grub-core/net/drivers/efi/efinet.c
+++ b/grub-core/net/drivers/efi/efinet.c
@@ -329,7 +329,7 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
 			  char **path)
 {
   struct grub_net_card *card;
-  grub_efi_device_path_t *dp;
+  grub_efi_device_path_t *dp, *ldp = NULL;
 
   dp = grub_efi_get_device_path (hnd);
   if (! dp)
@@ -345,9 +345,12 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
     cdp = grub_efi_get_device_path (card->efi_handle);
     if (! cdp)
       continue;
+
+    ldp = grub_efi_find_last_device_path (dp);
+
     if (grub_efi_compare_device_paths (dp, cdp) != 0)
       {
-	grub_efi_device_path_t *ldp, *dup_dp, *dup_ldp;
+	grub_efi_device_path_t *dup_dp, *dup_ldp;
 	int match;
 
 	/* EDK2 UEFI PXE driver creates pseudo devices with type IPv4/IPv6
@@ -356,7 +359,6 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
 	   devices. We skip them when enumerating cards, so here we need to
 	   find matching MAC 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))
@@ -378,11 +380,38 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char **device,
     if (! pxe)
       continue;
     pxe_mode = pxe->mode;
-    grub_net_configure_by_dhcp_ack (card->name, card, 0,
-				    (struct grub_net_bootp_packet *)
-				    &pxe_mode->dhcp_ack,
-				    sizeof (pxe_mode->dhcp_ack),
-				    1, device, path);
+
+    if (pxe_mode->using_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;
+
+	grub_net_configure_by_dhcpv6_reply (card->name, card, 0,
+					    (struct grub_net_dhcp6_packet *)
+					    &pxe_mode->dhcp_ack,
+					    sizeof (pxe_mode->dhcp_ack),
+					    1, device, path);
+	if (grub_errno)
+	  grub_print_error ();
+	if (device && path)
+	  grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
+	if (grub_errno)
+	  grub_print_error ();
+      }
+    else
+      {
+	grub_dprintf ("efinet", "using ipv4 and dhcp\n");
+	grub_net_configure_by_dhcp_ack (card->name, card, 0,
+					(struct grub_net_bootp_packet *)
+					&pxe_mode->dhcp_ack,
+					sizeof (pxe_mode->dhcp_ack),
+					1, device, path);
+	grub_dprintf ("efinet", "device: `%s' path: `%s'\n", *device, *path);
+      }
     return;
   }
 }
diff --git a/include/grub/efi/api.h b/include/grub/efi/api.h
index 937058d6899..db2a8395c78 100644
--- a/include/grub/efi/api.h
+++ b/include/grub/efi/api.h
@@ -577,10 +577,16 @@ typedef void *grub_efi_handle_t;
 typedef void *grub_efi_event_t;
 typedef grub_efi_uint64_t grub_efi_lba_t;
 typedef grub_efi_uintn_t grub_efi_tpl_t;
-typedef grub_uint8_t grub_efi_mac_address_t[32];
-typedef grub_uint8_t grub_efi_ipv4_address_t[4];
-typedef grub_uint16_t grub_efi_ipv6_address_t[8];
-typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4)));
+typedef grub_efi_uint8_t grub_efi_mac_address_t[32];
+typedef grub_efi_uint8_t grub_efi_ipv4_address_t[4];
+typedef grub_efi_uint8_t grub_efi_ipv6_address_t[16];
+typedef union
+{
+  grub_efi_uint32_t addr[4];
+  grub_efi_ipv4_address_t v4;
+  grub_efi_ipv6_address_t v6;
+} grub_efi_ip_address_t __attribute__ ((aligned(4)));
+
 typedef grub_efi_uint64_t grub_efi_physical_address_t;
 typedef grub_efi_uint64_t grub_efi_virtual_address_t;
 
@@ -1455,16 +1461,144 @@ struct grub_efi_simple_text_output_interface
 };
 typedef struct grub_efi_simple_text_output_interface grub_efi_simple_text_output_interface_t;
 
-typedef grub_uint8_t grub_efi_pxe_packet_t[1472];
+typedef struct grub_efi_pxe_dhcpv4_packet
+{
+  grub_efi_uint8_t bootp_opcode;
+  grub_efi_uint8_t bootp_hwtype;
+  grub_efi_uint8_t bootp_hwaddr_len;
+  grub_efi_uint8_t bootp_gate_hops;
+  grub_efi_uint32_t bootp_ident;
+  grub_efi_uint16_t bootp_seconds;
+  grub_efi_uint16_t bootp_flags;
+  grub_efi_uint8_t bootp_ci_addr[4];
+  grub_efi_uint8_t bootp_yi_addr[4];
+  grub_efi_uint8_t bootp_si_addr[4];
+  grub_efi_uint8_t bootp_gi_addr[4];
+  grub_efi_uint8_t bootp_hw_addr[16];
+  grub_efi_uint8_t bootp_srv_name[64];
+  grub_efi_uint8_t bootp_boot_file[128];
+  grub_efi_uint32_t dhcp_magik;
+  grub_efi_uint8_t dhcp_options[56];
+} grub_efi_pxe_dhcpv4_packet_t;
+
+struct grub_efi_pxe_dhcpv6_packet
+{
+  grub_efi_uint32_t message_type:8;
+  grub_efi_uint32_t transaction_id:24;
+  grub_efi_uint8_t dhcp_options[1024];
+} GRUB_PACKED;
+typedef struct grub_efi_pxe_dhcpv6_packet grub_efi_pxe_dhcpv6_packet_t;
+
+typedef union
+{
+  grub_efi_uint8_t raw[1472];
+  grub_efi_pxe_dhcpv4_packet_t dhcpv4;
+  grub_efi_pxe_dhcpv6_packet_t dhcpv6;
+} grub_efi_pxe_packet_t;
+
+typedef struct grub_efi_pxe_icmp_error
+{
+  grub_efi_uint8_t type;
+  grub_efi_uint8_t code;
+  grub_efi_uint16_t checksum;
+  union
+    {
+      grub_efi_uint32_t reserved;
+      grub_efi_uint32_t mtu;
+      grub_efi_uint32_t pointer;
+      struct
+	{
+	  grub_efi_uint16_t identifier;
+	  grub_efi_uint16_t sequence;
+	} echo;
+    } u;
+  grub_efi_uint8_t data[494];
+} grub_efi_pxe_icmp_error_t;
+
+typedef struct grub_efi_pxe_tftp_error
+{
+  grub_efi_uint8_t error_code;
+  grub_efi_char8_t error_string[127];
+} grub_efi_pxe_tftp_error_t;
+
+typedef struct {
+  grub_uint8_t addr[4];
+} grub_efi_pxe_ipv4_address_t;
+
+typedef struct {
+  grub_uint8_t addr[16];
+} grub_efi_pxe_ipv6_address_t;
+
+typedef struct {
+  grub_uint8_t addr[32];
+} grub_efi_pxe_mac_address_t;
+
+typedef union {
+  grub_uint32_t addr[4];
+  grub_efi_pxe_ipv4_address_t v4;
+  grub_efi_pxe_ipv6_address_t v6;
+} grub_efi_pxe_ip_address_t;
+
+#define GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT 8
+typedef struct grub_efi_pxe_ip_filter
+{
+  grub_efi_uint8_t filters;
+  grub_efi_uint8_t ip_count;
+  grub_efi_uint16_t reserved;
+  grub_efi_ip_address_t ip_list[GRUB_EFI_PXE_BASE_CODE_MAX_IPCNT];
+} grub_efi_pxe_ip_filter_t;
+
+typedef struct {
+  grub_efi_pxe_ip_address_t ip_addr;
+  grub_efi_pxe_mac_address_t mac_addr;
+} grub_efi_pxe_arp_entry_t;
+
+typedef struct {
+  grub_efi_pxe_ip_address_t ip_addr;
+  grub_efi_pxe_ip_address_t subnet_mask;
+  grub_efi_pxe_ip_address_t gw_addr;
+} grub_efi_pxe_route_entry_t;
+
+
+#define GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES 8
+#define GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES 8
 
 typedef struct grub_efi_pxe_mode
 {
-  grub_uint8_t unused[52];
+  grub_efi_boolean_t started;
+  grub_efi_boolean_t ipv6_available;
+  grub_efi_boolean_t ipv6_supported;
+  grub_efi_boolean_t using_ipv6;
+  grub_efi_boolean_t bis_supported;
+  grub_efi_boolean_t bis_detected;
+  grub_efi_boolean_t auto_arp;
+  grub_efi_boolean_t send_guid;
+  grub_efi_boolean_t dhcp_discover_valid;
+  grub_efi_boolean_t dhcp_ack_received;
+  grub_efi_boolean_t proxy_offer_received;
+  grub_efi_boolean_t pxe_discover_valid;
+  grub_efi_boolean_t pxe_reply_received;
+  grub_efi_boolean_t pxe_bis_reply_received;
+  grub_efi_boolean_t icmp_error_received;
+  grub_efi_boolean_t tftp_error_received;
+  grub_efi_boolean_t make_callbacks;
+  grub_efi_uint8_t ttl;
+  grub_efi_uint8_t tos;
+  grub_efi_ip_address_t station_ip;
+  grub_efi_ip_address_t subnet_mask;
   grub_efi_pxe_packet_t dhcp_discover;
   grub_efi_pxe_packet_t dhcp_ack;
   grub_efi_pxe_packet_t proxy_offer;
   grub_efi_pxe_packet_t pxe_discover;
   grub_efi_pxe_packet_t pxe_reply;
+  grub_efi_pxe_packet_t pxe_bis_reply;
+  grub_efi_pxe_ip_filter_t ip_filter;
+  grub_efi_uint32_t arp_cache_entries;
+  grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES];
+  grub_efi_uint32_t route_table_entries;
+  grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES];
+  grub_efi_pxe_icmp_error_t icmp_error;
+  grub_efi_pxe_tftp_error_t tftp_error;
 } grub_efi_pxe_mode_t;
 
 typedef struct grub_efi_pxe
-- 
2.26.2



^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/4] efinet: UEFI IPv6 PXE support
  2020-06-04  7:33 ` [PATCH 3/4] efinet: UEFI IPv6 PXE support Javier Martinez Canillas
@ 2020-06-04 11:37   ` Thomas Frauendorfer
  2020-06-05  2:15     ` Michael Chang
  0 siblings, 1 reply; 9+ messages in thread
From: Thomas Frauendorfer @ 2020-06-04 11:37 UTC (permalink / raw)
  To: The development of GNU GRUB

Hi,

You replace the 'unused[52]' field before dhcp_discover with 51 bytes.
While the UEFI spec also defines the EFI_PXE_BASE_CODE_MODE struct in
this way it also specifies that an EFI_IP_ADDRESS is 16-byte buffer
aligned on a 4-byte boundary.
So there should probably be a grub_efi_uint8_t between tos and
station_ip to ensure the correct alignment

On Thu, Jun 4, 2020 at 9:34 AM Javier Martinez Canillas
<javierm@redhat.com> wrote:
>

>  typedef struct grub_efi_pxe_mode
>  {
> -  grub_uint8_t unused[52];
> +  grub_efi_boolean_t started;
> +  grub_efi_boolean_t ipv6_available;
> +  grub_efi_boolean_t ipv6_supported;
> +  grub_efi_boolean_t using_ipv6;
> +  grub_efi_boolean_t bis_supported;
> +  grub_efi_boolean_t bis_detected;
> +  grub_efi_boolean_t auto_arp;
> +  grub_efi_boolean_t send_guid;
> +  grub_efi_boolean_t dhcp_discover_valid;
> +  grub_efi_boolean_t dhcp_ack_received;
> +  grub_efi_boolean_t proxy_offer_received;
> +  grub_efi_boolean_t pxe_discover_valid;
> +  grub_efi_boolean_t pxe_reply_received;
> +  grub_efi_boolean_t pxe_bis_reply_received;
> +  grub_efi_boolean_t icmp_error_received;
> +  grub_efi_boolean_t tftp_error_received;
> +  grub_efi_boolean_t make_callbacks;
> +  grub_efi_uint8_t ttl;
> +  grub_efi_uint8_t tos;
> +  grub_efi_ip_address_t station_ip;
> +  grub_efi_ip_address_t subnet_mask;
>    grub_efi_pxe_packet_t dhcp_discover;
>    grub_efi_pxe_packet_t dhcp_ack;
>    grub_efi_pxe_packet_t proxy_offer;
>    grub_efi_pxe_packet_t pxe_discover;
>    grub_efi_pxe_packet_t pxe_reply;
> +  grub_efi_pxe_packet_t pxe_bis_reply;
> +  grub_efi_pxe_ip_filter_t ip_filter;
> +  grub_efi_uint32_t arp_cache_entries;
> +  grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES];
> +  grub_efi_uint32_t route_table_entries;
> +  grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES];
> +  grub_efi_pxe_icmp_error_t icmp_error;
> +  grub_efi_pxe_tftp_error_t tftp_error;
>  } grub_efi_pxe_mode_t;
>
>  typedef struct grub_efi_pxe


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/4] efinet: UEFI IPv6 PXE support
  2020-06-04 11:37   ` Thomas Frauendorfer
@ 2020-06-05  2:15     ` Michael Chang
  2020-06-05 11:39       ` Thomas Frauendorfer
  2020-06-05 12:05       ` Javier Martinez Canillas
  0 siblings, 2 replies; 9+ messages in thread
From: Michael Chang @ 2020-06-05  2:15 UTC (permalink / raw)
  To: The development of GNU GRUB

On Thu, Jun 04, 2020 at 01:37:52PM +0200, Thomas Frauendorfer wrote:
> Hi,
> 
> You replace the 'unused[52]' field before dhcp_discover with 51 bytes.
> While the UEFI spec also defines the EFI_PXE_BASE_CODE_MODE struct in
> this way it also specifies that an EFI_IP_ADDRESS is 16-byte buffer
> aligned on a 4-byte boundary.

True.

> So there should probably be a grub_efi_uint8_t between tos and
> station_ip to ensure the correct alignment

You're probably right if the data type for `station_ip` is
`grub_efi_pxe_ip_address_t`, but here it is `grub_efi_ip_address_t` declared
as:

  typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4)));

So the compiler would have been taking care of the alignment already ...

By the way, I found the mentioned hunk is different to what was posted on the
list[1], which had relevant fields like this:

  grub_uint8_t using_ipv6;
  grub_uint8_t unused[16];
  grub_efi_pxe_ip_address_t station_ip;
  grub_efi_pxe_ip_address_t subnet_mask;

Maybe Javier could help to shed some light on why the change was made ? Though
I'm not against it, I'm still interested to know about it if they have any
other concern or in case anything could be missing here. :)

[1] https://lists.gnu.org/archive/html/grub-devel/2016-08/msg00003.html

Thanks,
Michael

> 
> On Thu, Jun 4, 2020 at 9:34 AM Javier Martinez Canillas
> <javierm@redhat.com> wrote:
> >
> 
> >  typedef struct grub_efi_pxe_mode
> >  {
> > -  grub_uint8_t unused[52];
> > +  grub_efi_boolean_t started;
> > +  grub_efi_boolean_t ipv6_available;
> > +  grub_efi_boolean_t ipv6_supported;
> > +  grub_efi_boolean_t using_ipv6;
> > +  grub_efi_boolean_t bis_supported;
> > +  grub_efi_boolean_t bis_detected;
> > +  grub_efi_boolean_t auto_arp;
> > +  grub_efi_boolean_t send_guid;
> > +  grub_efi_boolean_t dhcp_discover_valid;
> > +  grub_efi_boolean_t dhcp_ack_received;
> > +  grub_efi_boolean_t proxy_offer_received;
> > +  grub_efi_boolean_t pxe_discover_valid;
> > +  grub_efi_boolean_t pxe_reply_received;
> > +  grub_efi_boolean_t pxe_bis_reply_received;
> > +  grub_efi_boolean_t icmp_error_received;
> > +  grub_efi_boolean_t tftp_error_received;
> > +  grub_efi_boolean_t make_callbacks;
> > +  grub_efi_uint8_t ttl;
> > +  grub_efi_uint8_t tos;
> > +  grub_efi_ip_address_t station_ip;
> > +  grub_efi_ip_address_t subnet_mask;
> >    grub_efi_pxe_packet_t dhcp_discover;
> >    grub_efi_pxe_packet_t dhcp_ack;
> >    grub_efi_pxe_packet_t proxy_offer;
> >    grub_efi_pxe_packet_t pxe_discover;
> >    grub_efi_pxe_packet_t pxe_reply;
> > +  grub_efi_pxe_packet_t pxe_bis_reply;
> > +  grub_efi_pxe_ip_filter_t ip_filter;
> > +  grub_efi_uint32_t arp_cache_entries;
> > +  grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES];
> > +  grub_efi_uint32_t route_table_entries;
> > +  grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES];
> > +  grub_efi_pxe_icmp_error_t icmp_error;
> > +  grub_efi_pxe_tftp_error_t tftp_error;
> >  } grub_efi_pxe_mode_t;
> >
> >  typedef struct grub_efi_pxe
> 
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> https://lists.gnu.org/mailman/listinfo/grub-devel


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/4] efinet: UEFI IPv6 PXE support
  2020-06-05  2:15     ` Michael Chang
@ 2020-06-05 11:39       ` Thomas Frauendorfer
  2020-06-05 12:05       ` Javier Martinez Canillas
  1 sibling, 0 replies; 9+ messages in thread
From: Thomas Frauendorfer @ 2020-06-05 11:39 UTC (permalink / raw)
  To: The development of GNU GRUB

On Fri, Jun 5, 2020 at 4:16 AM Michael Chang <mchang@suse.com> wrote:
>
> You're probably right if the data type for `station_ip` is
> `grub_efi_pxe_ip_address_t`, but here it is `grub_efi_ip_address_t` declared
> as:
>
>   typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4)));
>
> So the compiler would have been taking care of the alignment already ...

Oh, right.
I only ran into an alignment problem wenn I combined your patches to use the
UEFI Managed Network Protocol together with a patch to handle proxy dhcp.
Sorry for the noise

> By the way, I found the mentioned hunk is different to what was posted on the
> list[1], which had relevant fields like this:
>
>   grub_uint8_t using_ipv6;
>   grub_uint8_t unused[16];
>   grub_efi_pxe_ip_address_t station_ip;
>   grub_efi_pxe_ip_address_t subnet_mask;
>
> Maybe Javier could help to shed some light on why the change was made ? Though
> I'm not against it, I'm still interested to know about it if they have any
> other concern or in case anything could be missing here. :)
>
> [1] https://lists.gnu.org/archive/html/grub-devel/2016-08/msg00003.html
>
> Thanks,
> Michael
>
> >
> > On Thu, Jun 4, 2020 at 9:34 AM Javier Martinez Canillas
> > <javierm@redhat.com> wrote:
> > >
> >
> > >  typedef struct grub_efi_pxe_mode
> > >  {
> > > -  grub_uint8_t unused[52];
> > > +  grub_efi_boolean_t started;
> > > +  grub_efi_boolean_t ipv6_available;
> > > +  grub_efi_boolean_t ipv6_supported;
> > > +  grub_efi_boolean_t using_ipv6;
> > > +  grub_efi_boolean_t bis_supported;
> > > +  grub_efi_boolean_t bis_detected;
> > > +  grub_efi_boolean_t auto_arp;
> > > +  grub_efi_boolean_t send_guid;
> > > +  grub_efi_boolean_t dhcp_discover_valid;
> > > +  grub_efi_boolean_t dhcp_ack_received;
> > > +  grub_efi_boolean_t proxy_offer_received;
> > > +  grub_efi_boolean_t pxe_discover_valid;
> > > +  grub_efi_boolean_t pxe_reply_received;
> > > +  grub_efi_boolean_t pxe_bis_reply_received;
> > > +  grub_efi_boolean_t icmp_error_received;
> > > +  grub_efi_boolean_t tftp_error_received;
> > > +  grub_efi_boolean_t make_callbacks;
> > > +  grub_efi_uint8_t ttl;
> > > +  grub_efi_uint8_t tos;
> > > +  grub_efi_ip_address_t station_ip;
> > > +  grub_efi_ip_address_t subnet_mask;
> > >    grub_efi_pxe_packet_t dhcp_discover;
> > >    grub_efi_pxe_packet_t dhcp_ack;
> > >    grub_efi_pxe_packet_t proxy_offer;
> > >    grub_efi_pxe_packet_t pxe_discover;
> > >    grub_efi_pxe_packet_t pxe_reply;
> > > +  grub_efi_pxe_packet_t pxe_bis_reply;
> > > +  grub_efi_pxe_ip_filter_t ip_filter;
> > > +  grub_efi_uint32_t arp_cache_entries;
> > > +  grub_efi_pxe_arp_entry_t arp_cache[GRUB_EFI_PXE_BASE_CODE_MAX_ARP_ENTRIES];
> > > +  grub_efi_uint32_t route_table_entries;
> > > +  grub_efi_pxe_route_entry_t route_table[GRUB_EFI_PXE_BASE_CODE_MAX_ROUTE_ENTRIES];
> > > +  grub_efi_pxe_icmp_error_t icmp_error;
> > > +  grub_efi_pxe_tftp_error_t tftp_error;
> > >  } grub_efi_pxe_mode_t;
> > >
> > >  typedef struct grub_efi_pxe

Those changes look like the api.h changes in a patch from Joseph
Mroczek to support proxy dhcp

https://lists.gnu.org/archive/html/grub-devel/2014-05/msg00002.html


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/4] efinet: UEFI IPv6 PXE support
  2020-06-05  2:15     ` Michael Chang
  2020-06-05 11:39       ` Thomas Frauendorfer
@ 2020-06-05 12:05       ` Javier Martinez Canillas
  2020-06-08  3:15         ` Michael Chang
  1 sibling, 1 reply; 9+ messages in thread
From: Javier Martinez Canillas @ 2020-06-05 12:05 UTC (permalink / raw)
  To: The development of GNU GRUB, Michael Chang

[adding Peter Jones as Cc that I forgot when sending the patches]

On 6/5/20 4:15 AM, Michael Chang wrote:
> On Thu, Jun 04, 2020 at 01:37:52PM +0200, Thomas Frauendorfer wrote:
>> Hi,
>>
>> You replace the 'unused[52]' field before dhcp_discover with 51 bytes.
>> While the UEFI spec also defines the EFI_PXE_BASE_CODE_MODE struct in
>> this way it also specifies that an EFI_IP_ADDRESS is 16-byte buffer
>> aligned on a 4-byte boundary.
> 
> True.
> 
>> So there should probably be a grub_efi_uint8_t between tos and
>> station_ip to ensure the correct alignment
>
> You're probably right if the data type for `station_ip` is
> `grub_efi_pxe_ip_address_t`, but here it is `grub_efi_ip_address_t` declared
> as:
> 
>   typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4)));
> 
> So the compiler would have been taking care of the alignment already ...
>

Right, I don't think we need an explicit padding here for that reason.

> By the way, I found the mentioned hunk is different to what was posted on the
> list[1], which had relevant fields like this:
> 
>   grub_uint8_t using_ipv6;
>   grub_uint8_t unused[16];
>   grub_efi_pxe_ip_address_t station_ip;
>   grub_efi_pxe_ip_address_t subnet_mask;
> 
> Maybe Javier could help to shed some light on why the change was made ? Though
> I'm not against it, I'm still interested to know about it if they have any
> other concern or in case anything could be missing here. :)
>
> [1] https://lists.gnu.org/archive/html/grub-devel/2016-08/msg00003.html
>

Yes, I decided to post the patches that we were carrying in our package, even
when they diverge a little bit from your original patches. I did that because
is what we have been testing for years and also contain some fixes for issues
found by Peter.

He was also the one who changed the definitions of these structures. I think
that was to make them more complete and closer to the spec, but he can
comment if I got the intention wrong.

I can post your original patches and then the squashed changes separately as
a follow-up if you prefer it that way.

Best regards,
-- 
Javier Martinez Canillas
Software Engineer - Desktop Hardware Enablement
Red Hat



^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/4] efinet: UEFI IPv6 PXE support
  2020-06-05 12:05       ` Javier Martinez Canillas
@ 2020-06-08  3:15         ` Michael Chang
  0 siblings, 0 replies; 9+ messages in thread
From: Michael Chang @ 2020-06-08  3:15 UTC (permalink / raw)
  To: Javier Martinez Canillas; +Cc: The development of GNU GRUB

On Fri, Jun 05, 2020 at 02:05:24PM +0200, Javier Martinez Canillas wrote:
> [adding Peter Jones as Cc that I forgot when sending the patches]
> 
> On 6/5/20 4:15 AM, Michael Chang wrote:
> > On Thu, Jun 04, 2020 at 01:37:52PM +0200, Thomas Frauendorfer wrote:
> >> Hi,
> >>
> >> You replace the 'unused[52]' field before dhcp_discover with 51 bytes.
> >> While the UEFI spec also defines the EFI_PXE_BASE_CODE_MODE struct in
> >> this way it also specifies that an EFI_IP_ADDRESS is 16-byte buffer
> >> aligned on a 4-byte boundary.
> > 
> > True.
> > 
> >> So there should probably be a grub_efi_uint8_t between tos and
> >> station_ip to ensure the correct alignment
> >
> > You're probably right if the data type for `station_ip` is
> > `grub_efi_pxe_ip_address_t`, but here it is `grub_efi_ip_address_t` declared
> > as:
> > 
> >   typedef grub_uint8_t grub_efi_ip_address_t[8] __attribute__ ((aligned(4)));
> > 
> > So the compiler would have been taking care of the alignment already ...
> >
> 
> Right, I don't think we need an explicit padding here for that reason.
> 
> > By the way, I found the mentioned hunk is different to what was posted on the
> > list[1], which had relevant fields like this:
> > 
> >   grub_uint8_t using_ipv6;
> >   grub_uint8_t unused[16];
> >   grub_efi_pxe_ip_address_t station_ip;
> >   grub_efi_pxe_ip_address_t subnet_mask;
> > 
> > Maybe Javier could help to shed some light on why the change was made ? Though
> > I'm not against it, I'm still interested to know about it if they have any
> > other concern or in case anything could be missing here. :)
> >
> > [1] https://lists.gnu.org/archive/html/grub-devel/2016-08/msg00003.html
> >
> 
> Yes, I decided to post the patches that we were carrying in our package, even
> when they diverge a little bit from your original patches. I did that because
> is what we have been testing for years and also contain some fixes for issues
> found by Peter.

OK. And thanks for doing it.

> He was also the one who changed the definitions of these structures. I think
> that was to make them more complete and closer to the spec, but he can
> comment if I got the intention wrong.

OK.
 
> I can post your original patches and then the squashed changes separately as
> a follow-up if you prefer it that way.

No. It is absoutely fine with me if you prefer to post the one used in your
package. On the other hand if you find any change that is worth to be mentioned
by a separate patch, please feel free to do so.

Thanks,
Michael

> 
> Best regards,
> -- 
> Javier Martinez Canillas
> Software Engineer - Desktop Hardware Enablement
> Red Hat
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2020-06-08  3:16 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-06-04  7:33 [PATCH 0/4] UEFI IPv6 and DHCPv6 support Javier Martinez Canillas
2020-06-04  7:33 ` [PATCH 1/4] net: read bracketed ipv6 addrs and port numbers Javier Martinez Canillas
2020-06-04  7:33 ` [PATCH 2/4] bootp: New net_bootp6 command Javier Martinez Canillas
2020-06-04  7:33 ` [PATCH 3/4] efinet: UEFI IPv6 PXE support Javier Martinez Canillas
2020-06-04 11:37   ` Thomas Frauendorfer
2020-06-05  2:15     ` Michael Chang
2020-06-05 11:39       ` Thomas Frauendorfer
2020-06-05 12:05       ` Javier Martinez Canillas
2020-06-08  3:15         ` Michael Chang

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.