All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] bootp: add native DHCPv4 support
@ 2016-03-20  7:29 Andrei Borzenkov
  2016-03-22 18:26 ` [PATCH v2] " Andrei Borzenkov
  0 siblings, 1 reply; 7+ messages in thread
From: Andrei Borzenkov @ 2016-03-20  7:29 UTC (permalink / raw)
  To: grub-devel; +Cc: mark.rutland, matthew.t.rivard

This patch adds support for native DHCPv4 and removes requirement for
BOOTP compatibility support in DHCP server.

There is no provision for selecting preferred server. We take the first
OFFER and try to REQUEST configuration from it. If NAK was received,
transaction is restarted, but if we hit timeout, configuration fails.

It also handles pure BOOTP reply (detected by lack of DHCP message type
option) and proceeds with configuration immedately. I could not test it
because I do not have pure BOOTP server.

Because we need access to DHCP options in multiple places now, it also
factors out DHCP option processing, adding support for option overload.

Timeout handling is now per-interface, with independent timeouts for
both DISCOVER and REQUEST. It should make it more responsive if answer
was delayed initially. Total timeout remains the same as originally, as
well as backoff algorithm, but we poll in fixed 200ms ticks so notice
(successful) reply earlier.

Failure to send packet to interface now does not terminate command (and
leaks memory) but rather simply ignores this interface and continues with
remaining ones if present.

Finally it adds net_dhcp alias with intention to deprecate net_bootp
completely (it would be possible to make net_bootp to actually send BOOTP
packet if someone thinks it is required).

Features not implements:

- DHCP server selection. We take first DHCPOFFER or BOOTPREPLY. No plans
to implement.

- DHCP option concatenation (RFC3396). I do not expect to hit it in real
life and it rather complicates implementation, so let's wait for actual
use case.

- client identifier (RFC6842). So far we expect valid MAC address, which
should be enough to uniquely identify client. It is also not clear how to
generate unique client identifier if we ever need one.

---
 grub-core/net/bootp.c | 747 ++++++++++++++++++++++++++++++++++----------------
 grub-core/net/ip.c    |   2 +-
 include/grub/net.h    |  14 +-
 3 files changed, 520 insertions(+), 243 deletions(-)

diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
index 09c6f76..2fd8b2b 100644
--- a/grub-core/net/bootp.c
+++ b/grub-core/net/bootp.c
@@ -25,20 +25,98 @@
 #include <grub/net/udp.h>
 #include <grub/datetime.h>
 
-static void
-parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask)
+struct grub_dhcp_discover_options
+  {
+  grub_uint8_t magic[4];
+  struct
+    {
+      grub_uint8_t code;
+      grub_uint8_t len;
+      grub_uint8_t data;
+    } GRUB_PACKED  message_type;
+    grub_uint8_t end;
+  } GRUB_PACKED;
+
+struct grub_dhcp_request_options
+  {
+    grub_uint8_t magic[4];
+    struct
+      {
+	grub_uint8_t code;
+	grub_uint8_t len;
+	grub_uint8_t data;
+      } GRUB_PACKED  message_type;
+    struct
+      {
+	grub_uint8_t type;
+	grub_uint8_t len;
+	grub_uint32_t data;
+      } GRUB_PACKED 	server_identifier;
+    struct
+      {
+	grub_uint8_t type;
+	grub_uint8_t len;
+	grub_uint32_t data;
+      } GRUB_PACKED 	requested_ip;
+    struct
+      {
+	grub_uint8_t type;
+	grub_uint8_t len;
+	grub_uint8_t data[7];
+      } GRUB_PACKED 	parameter_request;
+    grub_uint8_t end;
+  } GRUB_PACKED;
+
+enum
+  {
+    GRUB_DHCP_MESSAGE_UNKNOWN,
+    GRUB_DHCP_MESSAGE_DISCOVER,
+    GRUB_DHCP_MESSAGE_OFFER,
+    GRUB_DHCP_MESSAGE_REQUEST,
+    GRUB_DHCP_MESSAGE_DECLINE,
+    GRUB_DHCP_MESSAGE_ACK,
+    GRUB_DHCP_MESSAGE_NAK,
+    GRUB_DHCP_MESSAGE_RELEASE,
+    GRUB_DHCP_MESSAGE_INFORM,
+  };
+
+enum
+  {
+    GRUB_DHCP_OPT_OVERLOAD_FILE = 1,
+    GRUB_DHCP_OPT_OVERLOAD_SNAME = 2,
+  };
+
+#define GRUB_BOOTP_MAX_OPTIONS_SIZE 64
+#define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y)))
+
+/* Max timeout when waiting for BOOTP/DHCP reply */
+#define GRUB_DHCP_MAX_PACKET_TIMEOUT 32
+
+static const void *
+find_dhcp_option (const struct grub_net_bootp_packet *bp, unsigned size,
+		 grub_uint8_t opt_code, grub_uint8_t *opt_len)
 {
   const grub_uint8_t *ptr, *ptr0;
+  grub_uint8_t overload = 0;
+  int end = 0;
+
+  /* Magic */
+  if (size < sizeof (*bp) + sizeof (grub_uint32_t))
+    goto out;
 
-  ptr = ptr0 = vend;
+  ptr = ptr0 = (grub_uint8_t *) (bp + 1);
 
   if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
       || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
       || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
       || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
-    return;
+    goto out;
+
   ptr = ptr + sizeof (grub_uint32_t);
-  while (ptr - ptr0 < limit)
+  size -= (sizeof (*bp) + sizeof (grub_uint32_t));
+
+again:
+  while (ptr - ptr0 < size)
     {
       grub_uint8_t tagtype;
       grub_uint8_t taglength;
@@ -51,84 +129,76 @@ parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask)
 
       /* End tag.  */
       if (tagtype == GRUB_NET_BOOTP_END)
-	return;
+	{
+	  end = 1;
+	  break;
+	}
+
+      if (ptr - ptr0 >= size)
+	goto out;
 
       taglength = *ptr++;
 
-      switch (tagtype)
-	{
-	case GRUB_NET_BOOTP_NETMASK:
-	  if (taglength == 4)
-	    {
-	      int i;
-	      for (i = 0; i < 32; i++)
-		if (!(ptr[i / 8] & (1 << (7 - (i % 8)))))
-		  break;
-	      *mask = i;
-	    }
-	  break;
+      if (ptr + taglength - ptr0 >= size)
+	goto out;
 
-	case GRUB_NET_BOOTP_ROUTER:
-	  if (taglength == 4)
-	    {
-	      grub_net_network_level_netaddress_t target;
-	      grub_net_network_level_address_t gw;
-	      char *rname;
-	      
-	      target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-	      target.ipv4.base = 0;
-	      target.ipv4.masksize = 0;
-	      gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-	      grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4));
-	      rname = grub_xasprintf ("%s:default", name);
-	      if (rname)
-		grub_net_add_route_gw (rname, target, gw, NULL);
-	      grub_free (rname);
-	    }
-	  break;
-	case GRUB_NET_BOOTP_DNS:
-	  {
-	    int i;
-	    for (i = 0; i < taglength / 4; i++)
-	      {
-		struct grub_net_network_level_address s;
-		s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-		s.ipv4 = grub_get_unaligned32 (ptr);
-		s.option = DNS_OPTION_PREFER_IPV4;
-		grub_net_add_dns_server (&s);
-		ptr += 4;
-	      }
-	  }
-	  continue;
-	case GRUB_NET_BOOTP_HOSTNAME:
-          grub_env_set_net_property (name, "hostname", (const char *) ptr,
-                                     taglength);
-          break;
-
-	case GRUB_NET_BOOTP_DOMAIN:
-          grub_env_set_net_property (name, "domain", (const char *) ptr,
-                                     taglength);
-          break;
-
-	case GRUB_NET_BOOTP_ROOT_PATH:
-          grub_env_set_net_property (name, "rootpath", (const char *) ptr,
-                                     taglength);
-          break;
-
-	case GRUB_NET_BOOTP_EXTENSIONS_PATH:
-          grub_env_set_net_property (name, "extensionspath", (const char *) ptr,
-                                     taglength);
-          break;
-
-	  /* If you need any other options please contact GRUB
-	     development team.  */
+      /* FIXME RFC 3396 options concatentation */
+      if (tagtype == opt_code)
+	{
+	  if (opt_len)
+	    *opt_len = taglength;
+	  return ptr;
 	}
 
+      if (tagtype == GRUB_NET_DHCP_OVERLOAD && taglength == 1)
+	overload = *ptr;
+
       ptr += taglength;
     }
-}
 
-#define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y)))
+    /* RFC2131, 4.1, 23ff:
+       If the options in a DHCP message extend into the 'sname' and 'file'
+       fields, the 'option overload' option MUST appear in the 'options'
+       field, with value 1, 2 or 3, as specified in RFC 1533.  If the
+       'option overload' option is present in the 'options' field, the
+       options in the 'options' field MUST be terminated by an 'end' option,
+       and MAY contain one or more 'pad' options to fill the options field.
+       The options in the 'sname' and 'file' fields (if in use as indicated
+       by the 'options overload' option) MUST begin with the first octet of
+       the field, MUST be terminated by an 'end' option, and MUST be
+       followed by 'pad' options to fill the remainder of the field.  Any
+       individual option in the 'options', 'sname' and 'file' fields MUST be
+       entirely contained in that field.  The options in the 'options' field
+       MUST be interpreted first, so that any 'option overload' options may
+       be interpreted.  The 'file' field MUST be interpreted next (if the
+       'option overload' option indicates that the 'file' field contains
+       DHCP options), followed by the 'sname' field.
+
+       FIXME: We do not explicitly check for trailing 'pad' options here.
+     */
+    if (end)
+      {
+	end = 0;
+	if (overload & GRUB_DHCP_OPT_OVERLOAD_FILE)
+	  {
+	    overload &= ~GRUB_DHCP_OPT_OVERLOAD_FILE;
+	    ptr = ptr0 = (grub_uint8_t *) &bp->boot_file[0];
+	    size = sizeof (bp->boot_file);
+	    goto again;
+	  }
+
+	if (overload & GRUB_DHCP_OPT_OVERLOAD_SNAME)
+	  {
+	    overload &= ~GRUB_DHCP_OPT_OVERLOAD_SNAME;
+	    ptr = ptr0 = (grub_uint8_t *) &bp->server_name[0];
+	    size = sizeof (bp->server_name);
+	    goto again;
+	  }
+      }
+
+out:
+  return 0;
+}
 
 struct grub_net_network_level_interface *
 grub_net_configure_by_dhcp_ack (const char *name,
@@ -142,6 +212,10 @@ grub_net_configure_by_dhcp_ack (const char *name,
   grub_net_link_level_address_t hwaddr;
   struct grub_net_network_level_interface *inter;
   int mask = -1;
+  const grub_uint8_t *opt;
+  grub_uint8_t opt_len = 0, overload = 0;
+  const char *boot_file = 0, *server_name = 0;
+  grub_size_t boot_file_len, server_name_len;
 
   addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
   addr.ipv4 = bp->your_ip;
@@ -157,38 +231,37 @@ grub_net_configure_by_dhcp_ack (const char *name,
   hwaddr.type = GRUB_NET_LINK_LEVEL_PROTOCOL_ETHERNET;
 
   inter = grub_net_add_addr (name, card, &addr, &hwaddr, flags);
-#if 0
-  /* This is likely based on misunderstanding. gateway_ip refers to
-     address of BOOTP relay and should not be used after BOOTP transaction
-     is complete.
-     See RFC1542, 3.4 Interpretation of the 'giaddr' field
-   */
-  if (bp->gateway_ip)
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_OVERLOAD, &opt_len);
+  if (opt && opt_len == 1)
+    overload = *opt;
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_TFTP_SERVER_NAME, &opt_len);
+  if (opt && opt_len)
     {
-      grub_net_network_level_netaddress_t target;
-      grub_net_network_level_address_t gw;
-      char *rname;
-	  
-      target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-      target.ipv4.base = bp->server_ip;
-      target.ipv4.masksize = 32;
-      gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-      gw.ipv4 = bp->gateway_ip;
-      rname = grub_xasprintf ("%s:gw", name);
-      if (rname)
-	grub_net_add_route_gw (rname, target, gw);
-      grub_free (rname);
+      server_name = (const char *) opt;
+      server_name_len = opt_len;
+    }
+  else if (size > OFFSET_OF (server_name, bp) && !(overload & GRUB_DHCP_OPT_OVERLOAD_SNAME) &&
+	   bp->server_name[0])
+    {
+      server_name = bp->server_name;
+      server_name_len = sizeof (bp->server_name);
+    }
 
-      target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-      target.ipv4.base = bp->gateway_ip;
-      target.ipv4.masksize = 32;
-      grub_net_add_route (name, target, inter);
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_BOOTFILE_NAME, &opt_len);
+  if (opt && opt_len)
+    {
+      boot_file = (const char *) opt;
+      boot_file_len = opt_len;
+    }
+  else if (size > OFFSET_OF (boot_file, bp) && !(overload && GRUB_DHCP_OPT_OVERLOAD_FILE) &&
+	   bp->boot_file[0])
+    {
+      boot_file = bp->boot_file;
+      boot_file_len = sizeof (bp->boot_file);
     }
-#endif
 
-  if (size > OFFSET_OF (boot_file, bp))
-    grub_env_set_net_property (name, "boot_file", bp->boot_file,
-                               sizeof (bp->boot_file));
   if (is_def)
     grub_net_default_server = 0;
   if (is_def && !grub_net_default_server && bp->server_ip)
@@ -216,40 +289,103 @@ grub_net_configure_by_dhcp_ack (const char *name,
 				((grub_uint8_t *) &bp->server_ip)[3]);
       grub_print_error ();
     }
-  if (size > OFFSET_OF (server_name, bp)
-      && bp->server_name[0])
+
+  if (server_name)
     {
-      grub_env_set_net_property (name, "dhcp_server_name", bp->server_name,
-                                 sizeof (bp->server_name));
+      grub_env_set_net_property (name, "dhcp_server_name", server_name, server_name_len);
       if (is_def && !grub_net_default_server)
 	{
-	  grub_net_default_server = grub_strdup (bp->server_name);
+	  grub_net_default_server = grub_strdup (server_name);
 	  grub_print_error ();
 	}
       if (device && !*device)
 	{
-	  *device = grub_xasprintf ("tftp,%s", bp->server_name);
+	  *device = grub_xasprintf ("tftp,%s", server_name);
 	  grub_print_error ();
 	}
     }
 
-  if (size > OFFSET_OF (boot_file, bp) && path)
+  if (boot_file)
     {
-      *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
-      grub_print_error ();
-      if (*path)
+      grub_env_set_net_property (name, "boot_file", boot_file, boot_file_len);
+      if (path)
 	{
-	  char *slash;
-	  slash = grub_strrchr (*path, '/');
-	  if (slash)
-	    *slash = 0;
-	  else
-	    **path = 0;
+	  *path = grub_strndup (boot_file, boot_file_len);
+	  grub_print_error ();
+	  if (*path)
+	    {
+	      char *slash;
+	      slash = grub_strrchr (*path, '/');
+	      if (slash)
+		*slash = 0;
+	      else
+		**path = 0;
+	    }
 	}
     }
-  if (size > OFFSET_OF (vendor, bp))
-    parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp), &mask);
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_NETMASK, &opt_len);
+  if (opt && opt_len == 4)
+    {
+      int i;
+      for (i = 0; i < 32; i++)
+	if (!(opt[i / 8] & (1 << (7 - (i % 8)))))
+	  break;
+      mask = i;
+    }
   grub_net_add_ipv4_local (inter, mask);
+
+  /* We do not implement dead gateway detection and the first entry SHOULD
+     be preferred one */
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_ROUTER, &opt_len);
+  if (opt && opt_len && !(opt_len & 3))
+    {
+      grub_net_network_level_netaddress_t target;
+      grub_net_network_level_address_t gw;
+      char *rname;
+
+      target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+      target.ipv4.base = 0;
+      target.ipv4.masksize = 0;
+      gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+      gw.ipv4 = grub_get_unaligned32 (opt);
+      rname = grub_xasprintf ("%s:default", name);
+      if (rname)
+	grub_net_add_route_gw (rname, target, gw, 0);
+      grub_free (rname);
+    }
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_DNS, &opt_len);
+  if (opt && opt_len && !(opt_len & 3))
+    {
+      int i;
+      for (i = 0; i < opt_len / 4; i++)
+	{
+	  struct grub_net_network_level_address s;
+
+	  s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+	  s.ipv4 = grub_get_unaligned32 (opt);
+	  s.option = DNS_OPTION_PREFER_IPV4;
+	  grub_net_add_dns_server (&s);
+	  opt += 4;
+	}
+    }
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_HOSTNAME, &opt_len);
+  if (opt && opt_len)
+    grub_env_set_net_property (name, "hostname", (const char *) opt, opt_len);
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_DOMAIN, &opt_len);
+  if (opt && opt_len)
+    grub_env_set_net_property (name, "domain", (const char *) opt, opt_len);
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_ROOT_PATH, &opt_len);
+  if (opt && opt_len)
+    grub_env_set_net_property (name, "rootpath", (const char *) opt, opt_len);
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_EXTENSIONS_PATH, &opt_len);
+  if (opt && opt_len)
+    grub_env_set_net_property (name, "extensionspath", (const char *) opt, opt_len);
   
   inter->dhcp_ack = grub_malloc (size);
   if (inter->dhcp_ack)
@@ -263,35 +399,208 @@ grub_net_configure_by_dhcp_ack (const char *name,
   return inter;
 }
 
+static grub_err_t
+send_dhcp_packet (struct grub_net_network_level_interface *iface)
+{
+  grub_err_t err;
+  struct grub_net_bootp_packet *pack;
+  struct grub_datetime date;
+  grub_int32_t t = 0;
+  struct grub_net_buff *nb;
+  struct udphdr *udph;
+  grub_net_network_level_address_t target;
+  grub_net_link_level_address_t ll_target;
+
+  static struct grub_dhcp_discover_options discover_options =
+    {
+      {
+	GRUB_NET_BOOTP_RFC1048_MAGIC_0,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_1,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_2,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_3,
+      },
+      {
+	GRUB_NET_DHCP_MESSAGE_TYPE,
+	sizeof (discover_options.message_type.data),
+	GRUB_DHCP_MESSAGE_DISCOVER,
+      },
+      GRUB_NET_BOOTP_END,
+    };
+
+  static struct grub_dhcp_request_options request_options =
+    {
+      {
+	GRUB_NET_BOOTP_RFC1048_MAGIC_0,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_1,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_2,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_3,
+      },
+      {
+	GRUB_NET_DHCP_MESSAGE_TYPE,
+	sizeof (request_options.message_type.data),
+	GRUB_DHCP_MESSAGE_REQUEST,
+      },
+      {
+	GRUB_NET_DHCP_SERVER_IDENTIFIER,
+	sizeof (request_options.server_identifier.data),
+	0,
+      },
+      {
+	GRUB_NET_DHCP_REQUESTED_IP_ADDRESS,
+	sizeof (request_options.requested_ip.data),
+	0,
+      },
+      {
+	GRUB_NET_DHCP_PARAMETER_REQUEST_LIST,
+	sizeof (request_options.parameter_request.data),
+	{
+	  GRUB_NET_BOOTP_NETMASK,
+	  GRUB_NET_BOOTP_ROUTER,
+	  GRUB_NET_BOOTP_DNS,
+	  GRUB_NET_BOOTP_DOMAIN,
+	  GRUB_NET_BOOTP_HOSTNAME,
+	  GRUB_NET_BOOTP_ROOT_PATH,
+	  GRUB_NET_BOOTP_EXTENSIONS_PATH,
+	},
+      },
+      GRUB_NET_BOOTP_END,
+    };
+
+  COMPILE_TIME_ASSERT (sizeof (discover_options) <= GRUB_BOOTP_MAX_OPTIONS_SIZE);
+  COMPILE_TIME_ASSERT (sizeof (request_options) <= GRUB_BOOTP_MAX_OPTIONS_SIZE);
+
+  nb = grub_netbuff_alloc (sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
+  if (!nb)
+    return grub_errno;
+
+  err = grub_netbuff_reserve (nb, sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
+  if (err)
+    goto out;
+
+  err = grub_netbuff_push (nb, GRUB_BOOTP_MAX_OPTIONS_SIZE);
+  if (err)
+    goto out;
+
+  grub_memset (nb->data, 0, GRUB_BOOTP_MAX_OPTIONS_SIZE);
+  if (!iface->srv_id)
+    {
+      grub_memcpy (nb->data, &discover_options, sizeof (discover_options));
+    }
+  else
+    {
+      struct grub_dhcp_request_options *ro = (struct grub_dhcp_request_options *) nb->data;
+
+      grub_memcpy (nb->data, &request_options, sizeof (request_options));
+      /* my_ip and srv_id are stored in network order so do not need conversion. */
+      grub_set_unaligned32 (&ro->server_identifier.data, iface->srv_id);
+      grub_set_unaligned32 (&ro->requested_ip.data, iface->my_ip);
+    }
+
+  err = grub_netbuff_push (nb, sizeof (*pack));
+  if (err)
+    goto out;
+
+  pack = (void *) nb->data;
+  grub_memset (pack, 0, sizeof (*pack));
+  pack->opcode = 1;
+  pack->hw_type = 1;
+  pack->hw_len = 6;
+  err = grub_get_datetime (&date);
+  if (err || !grub_datetime2unixtime (&date, &t))
+    {
+      grub_errno = GRUB_ERR_NONE;
+      t = 0;
+    }
+  pack->seconds = grub_cpu_to_be16 (t);
+  if (!iface->srv_id)
+    iface->xid = pack->ident = grub_cpu_to_be32 (t);
+  else
+    pack->ident = iface->xid;
+
+  grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6);
+
+  grub_netbuff_push (nb, sizeof (*udph));
+
+  udph = (struct udphdr *) nb->data;
+  udph->src = grub_cpu_to_be16_compile_time (68);
+  udph->dst = grub_cpu_to_be16_compile_time (67);
+  udph->chksum = 0;
+  udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
+  target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+  target.ipv4 = 0xffffffff;
+  err = grub_net_link_layer_resolve (iface, &target, &ll_target);
+  if (err)
+    goto out;
+
+  udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
+						 &iface->address,
+						 &target);
+
+  err = grub_net_send_ip_packet (iface, &target, &ll_target, nb,
+				 GRUB_NET_IP_UDP);
+
+out:
+  grub_netbuff_free (nb);
+  return err;
+}
+
 void
 grub_net_process_dhcp (struct grub_net_buff *nb,
-		       struct grub_net_card *card)
+		       struct grub_net_network_level_interface *iface)
 {
   char *name;
-  struct grub_net_network_level_interface *inf;
-
-  name = grub_xasprintf ("%s:dhcp", card->name);
-  if (!name)
+  struct grub_net_card *card = iface->card;
+  const struct grub_net_bootp_packet *bp = (const struct grub_net_bootp_packet *) nb->data;
+  grub_size_t size = nb->tail - nb->data;
+  const grub_uint8_t *opt;
+  grub_uint8_t optlen = 0, type;
+  grub_uint32_t srv_id = 0;
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_MESSAGE_TYPE, &optlen);
+  if (opt && optlen == 1)
+    type = *opt;
+  else
+    type = GRUB_DHCP_MESSAGE_UNKNOWN;
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_SERVER_IDENTIFIER, &optlen);
+  if (opt && optlen == sizeof (srv_id))
+    srv_id = grub_get_unaligned32 (opt);
+
+  /* If we received BOOTP reply or DHCPACK, proceed with configuration. Otherwise
+     store offered address and server id for later processing of DHCPACK.
+     xid and srv_id are stored in network order so do not need conversion. */
+  if ((!iface->srv_id && type == GRUB_DHCP_MESSAGE_UNKNOWN)
+      || (iface->srv_id && type == GRUB_DHCP_MESSAGE_ACK
+	  && bp->ident == iface->xid
+	  && srv_id == iface->srv_id))
     {
-      grub_print_error ();
-      return;
+      name = grub_xasprintf ("%s:dhcp", card->name);
+      if (!name)
+	{
+	  grub_print_error ();
+	  return;
+	}
+      grub_net_configure_by_dhcp_ack (name, card, 0, bp, size, 0, 0, 0);
+      grub_free (name);
+      if (grub_errno)
+	grub_print_error ();
+      else
+	grub_net_network_level_interface_unregister (iface);
     }
-  grub_net_configure_by_dhcp_ack (name, card,
-				  0, (const struct grub_net_bootp_packet *) nb->data,
-				  (nb->tail - nb->data), 0, 0, 0);
-  grub_free (name);
-  if (grub_errno)
-    grub_print_error ();
-  else
+  else if (!iface->srv_id && type == GRUB_DHCP_MESSAGE_OFFER && srv_id)
     {
-      FOR_NET_NETWORK_LEVEL_INTERFACES(inf)
-	if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) == 0
-	    && grub_memcmp (inf->name + grub_strlen (card->name),
-			    ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) == 0)
-	  {
-	    grub_net_network_level_interface_unregister (inf);
-	    break;
-	  }
+      iface->srv_id = srv_id;
+      iface->my_ip = bp->your_ip;
+      /* Reset retransmission timer */
+      iface->dhcp_tmo = iface->dhcp_tmo_left = 1;
+    }
+  else if (iface->srv_id && type == GRUB_DHCP_MESSAGE_NAK
+	   && bp->ident == iface->xid
+	   && srv_id == iface->srv_id)
+    {
+      iface->xid = iface->srv_id = iface->my_ip = 0;
+      /* Reset retransmission timer */
+      iface->dhcp_tmo = iface->dhcp_tmo_left = 1;
     }
 }
 
@@ -308,8 +617,8 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
 		  int argc, char **args)
 {
   struct grub_net_network_level_interface *inter;
-  int num;
-  grub_uint8_t *ptr;
+  unsigned num;
+  const grub_uint8_t *ptr;
   grub_uint8_t taglength;
 
   if (argc < 4)
@@ -327,44 +636,27 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
   if (!inter->dhcp_ack)
     return grub_error (GRUB_ERR_IO, N_("no DHCP info found"));
 
-  if (inter->dhcp_acklen <= OFFSET_OF (vendor, inter->dhcp_ack))
-    return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
-
-  num = grub_strtoul (args[2], 0, 0);
-  if (grub_errno)
-    return grub_errno;
-
   ptr = inter->dhcp_ack->vendor;
 
-  if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
+  /* This duplicates check in find_dhcp_option to preserve previous error return */
+  if (inter->dhcp_acklen < OFFSET_OF (vendor, inter->dhcp_ack) + sizeof (grub_uint32_t)
+      || ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
       || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
       || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
       || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
     return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
-  ptr = ptr + sizeof (grub_uint32_t);
-  while (1)
-    {
-      grub_uint8_t tagtype;
 
-      if (ptr >= ((grub_uint8_t *) inter->dhcp_ack) + inter->dhcp_acklen)
-	return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
-
-      tagtype = *ptr++;
-
-      /* Pad tag.  */
-      if (tagtype == 0)
-	continue;
+  num = grub_strtoul (args[2], 0, 0);
+  if (grub_errno)
+    return grub_errno;
 
-      /* End tag.  */
-      if (tagtype == 0xff)
-	return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
+  /* Exclude PAD (0) and END (255) option codes */
+  if (num == 0 || num > 254)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid DHCP option code"));
 
-      taglength = *ptr++;
-	
-      if (tagtype == num)
-	break;
-      ptr += taglength;
-    }
+  ptr = find_dhcp_option (inter->dhcp_ack, inter->dhcp_acklen, num, &taglength);
+  if (!ptr)
+    return grub_error (GRUB_ERR_IO, N_("no DHCP option %u found"), num);
 
   if (grub_strcmp (args[3], "string") == 0)
     {
@@ -434,8 +726,8 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
   struct grub_net_network_level_interface *ifaces;
   grub_size_t ncards = 0;
   unsigned j = 0;
-  int interval;
   grub_err_t err;
+  unsigned i;
 
   FOR_NET_CARDS (card)
   {
@@ -464,7 +756,6 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
     card->num_ifaces++;
     if (!ifaces[j].name)
       {
-	unsigned i;
 	for (i = 0; i < j; i++)
 	  grub_free (ifaces[i].name);
 	grub_free (ifaces);
@@ -473,6 +764,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
     ifaces[j].address.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV;
     grub_memcpy (&ifaces[j].hwaddress, &card->default_address, 
 		 sizeof (ifaces[j].hwaddress));
+    ifaces[j].dhcp_tmo = ifaces[j].dhcp_tmo_left = 1;
     j++;
   }
   ifaces[ncards - 1].next = grub_net_network_level_interfaces;
@@ -480,85 +772,54 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
     grub_net_network_level_interfaces->prev = & ifaces[ncards - 1].next;
   grub_net_network_level_interfaces = &ifaces[0];
   ifaces[0].prev = &grub_net_network_level_interfaces;
-  for (interval = 200; interval < 10000; interval *= 2)
+
+  /* Running DHCP restransmission timer is kept per interface in dhcp_tmo_left.
+     When it runs off, dhcp_tmo is increased exponentionally and dhcp_tmo_left
+     initialized to it. Max value is 32 which gives approximately 12s total per
+     packet timeout assuming 200ms poll tick. Timeout is reset when DHCP OFFER
+     is received, so total timeout is 25s in the worst case.
+
+     DHCP NAK also resets timer and transaction starts again.
+     
+     Total wait time is limited to ~25s to prevent endless loop in case of
+     permanent NAK
+   */
+  for (i = 0; i < GRUB_DHCP_MAX_PACKET_TIMEOUT * 4; i++)
     {
-      int done = 0;
+      int need_poll = 0;
       for (j = 0; j < ncards; j++)
 	{
-	  struct grub_net_bootp_packet *pack;
-	  struct grub_datetime date;
-	  grub_int32_t t = 0;
-	  struct grub_net_buff *nb;
-	  struct udphdr *udph;
-	  grub_net_network_level_address_t target;
-	  grub_net_link_level_address_t ll_target;
-
-	  if (!ifaces[j].prev)
+	  if (!ifaces[j].prev || ifaces[j].dhcp_tmo > GRUB_DHCP_MAX_PACKET_TIMEOUT)
 	    continue;
-	  nb = grub_netbuff_alloc (sizeof (*pack) + 64 + 128);
-	  if (!nb)
-	    {
-	      grub_netbuff_free (nb);
-	      return grub_errno;
-	    }
-	  err = grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128);
-	  if (err)
-	    {
-	      grub_netbuff_free (nb);
-	      return err;
-	    }
-	  err = grub_netbuff_push (nb, sizeof (*pack) + 64);
-	  if (err)
-	    {
-	      grub_netbuff_free (nb);
-	      return err;
-	    }
-	  pack = (void *) nb->data;
-	  done = 1;
-	  grub_memset (pack, 0, sizeof (*pack) + 64);
-	  pack->opcode = 1;
-	  pack->hw_type = 1;
-	  pack->hw_len = 6;
-	  err = grub_get_datetime (&date);
-	  if (err || !grub_datetime2unixtime (&date, &t))
+
+	  if (--ifaces[j].dhcp_tmo_left)
 	    {
-	      grub_errno = GRUB_ERR_NONE;
-	      t = 0;
+	      need_poll = 1;
+	      continue;
 	    }
-	  pack->ident = grub_cpu_to_be32 (t);
-	  pack->seconds = grub_cpu_to_be16 (t);
-
-	  grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6); 
-
-	  grub_netbuff_push (nb, sizeof (*udph));
-
-	  udph = (struct udphdr *) nb->data;
-	  udph->src = grub_cpu_to_be16_compile_time (68);
-	  udph->dst = grub_cpu_to_be16_compile_time (67);
-	  udph->chksum = 0;
-	  udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
-	  target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-	  target.ipv4 = 0xffffffff;
-	  err = grub_net_link_layer_resolve (&ifaces[j], &target, &ll_target);
-	  if (err)
-	    return err;
 
-	  udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
-							 &ifaces[j].address,
-							 &target);
+	  ifaces[j].dhcp_tmo *= 2;
+	  if (ifaces[j].dhcp_tmo > GRUB_DHCP_MAX_PACKET_TIMEOUT)
+	    continue;
 
-	  err = grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb,
-					 GRUB_NET_IP_UDP);
-	  grub_netbuff_free (nb);
+	  err = send_dhcp_packet (&ifaces[j]);
 	  if (err)
-	    return err;
+	    {
+	      grub_print_error ();
+	      /* To ignore it during next poll */
+	      ifaces[j].dhcp_tmo = GRUB_DHCP_MAX_PACKET_TIMEOUT + 1;
+	      continue;
+	    }
+	  ifaces[j].dhcp_tmo_left = ifaces[j].dhcp_tmo;
+	  need_poll = 1;
 	}
-      if (!done)
+      if (!need_poll)
 	break;
-      grub_net_poll_cards (interval, 0);
+      grub_net_poll_cards (200, 0);
     }
 
   err = GRUB_ERR_NONE;
+
   for (j = 0; j < ncards; j++)
     {
       grub_free (ifaces[j].name);
@@ -575,7 +836,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
   return err;
 }
 
-static grub_command_t cmd_getdhcp, cmd_bootp;
+static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp;
 
 void
 grub_bootp_init (void)
@@ -583,6 +844,9 @@ grub_bootp_init (void)
   cmd_bootp = grub_register_command ("net_bootp", grub_cmd_bootp,
 				     N_("[CARD]"),
 				     N_("perform a bootp autoconfiguration"));
+  cmd_dhcp = grub_register_command ("net_dhcp", grub_cmd_bootp,
+				     N_("[CARD]"),
+				     N_("perform a DHCP autoconfiguration"));
   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."));
@@ -593,4 +857,5 @@ grub_bootp_fini (void)
 {
   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 8c56baa..162c70d 100644
--- a/grub-core/net/ip.c
+++ b/grub-core/net/ip.c
@@ -278,7 +278,7 @@ handle_dgram (struct grub_net_buff *nb,
 	      && grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr,
 			      sizeof (inf->hwaddress.mac)) == 0)
 	    {
-	      grub_net_process_dhcp (nb, inf->card);
+	      grub_net_process_dhcp (nb, inf);
 	      grub_netbuff_free (nb);
 	      return GRUB_ERR_NONE;
 	    }
diff --git a/include/grub/net.h b/include/grub/net.h
index 2192fa1..6d4a7e5 100644
--- a/include/grub/net.h
+++ b/include/grub/net.h
@@ -291,6 +291,11 @@ struct grub_net_network_level_interface
   grub_net_interface_flags_t flags;
   struct grub_net_bootp_packet *dhcp_ack;
   grub_size_t dhcp_acklen;
+  grub_uint32_t xid;      /* DHCPv4 transaction id */
+  grub_uint32_t srv_id;   /* DHCPv4 server_identifier */
+  grub_uint32_t my_ip;    /* DHCPv4 offered IP address */
+  unsigned dhcp_tmo_left; /* DHCPv4 running retransmission timeout */
+  unsigned dhcp_tmo;      /* DHCPv4 current retransmission timeout */
   void *data;
 };
 
@@ -456,6 +461,13 @@ enum
     GRUB_NET_BOOTP_DOMAIN = 0x0f,
     GRUB_NET_BOOTP_ROOT_PATH = 0x11,
     GRUB_NET_BOOTP_EXTENSIONS_PATH = 0x12,
+    GRUB_NET_DHCP_REQUESTED_IP_ADDRESS = 50,
+    GRUB_NET_DHCP_OVERLOAD = 52,
+    GRUB_NET_DHCP_MESSAGE_TYPE = 53,
+    GRUB_NET_DHCP_SERVER_IDENTIFIER = 54,
+    GRUB_NET_DHCP_PARAMETER_REQUEST_LIST = 55,
+    GRUB_NET_DHCP_TFTP_SERVER_NAME = 66,
+    GRUB_NET_DHCP_BOOTFILE_NAME = 67,
     GRUB_NET_BOOTP_END = 0xff
   };
 
@@ -473,7 +485,7 @@ grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf,
 
 void
 grub_net_process_dhcp (struct grub_net_buff *nb,
-		       struct grub_net_card *card);
+		       struct grub_net_network_level_interface *iface);
 
 int
 grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,
-- 
tg: (5b8ddf6..) u/dhcpv4 (depends on: master)


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

* [PATCH v2] bootp: add native DHCPv4 support
  2016-03-20  7:29 [PATCH] bootp: add native DHCPv4 support Andrei Borzenkov
@ 2016-03-22 18:26 ` Andrei Borzenkov
  2016-04-27 14:40   ` Punit Agrawal
  2016-04-27 15:34   ` Mark Rutland
  0 siblings, 2 replies; 7+ messages in thread
From: Andrei Borzenkov @ 2016-03-22 18:26 UTC (permalink / raw)
  To: grub-devel; +Cc: mark.rutland, matthew.t.rivard

This patch adds support for native DHCPv4 and removes requirement for
BOOTP compatibility support in DHCP server.

There is no provision for selecting preferred server. We take the first
OFFER and try to REQUEST configuration from it. If NAK was received,
transaction is restarted, but if we hit timeout, configuration fails.

It also handles pure BOOTP reply (detected by lack of DHCP message type
option) and proceeds with configuration immedately. I could not test it
because I do not have pure BOOTP server.

Because we need access to DHCP options in multiple places now, it also
factors out DHCP option processing, adding support for option overload.

Timeout handling is now per-interface, with independent timeouts for
both DISCOVER and REQUEST. It should make it more responsive if answer
was delayed initially. Total timeout remains the same as originally, as
well as backoff algorithm, but we poll in fixed 200ms ticks so notice
(successful) reply earlier.

Failure to send packet to interface now does not terminate command (and
leaks memory) but rather simply ignores this interface and continues with
remaining ones if present.

Finally it adds net_dhcp alias with intention to deprecate net_bootp
completely (it would be possible to make net_bootp to actually send BOOTP
packet if someone thinks it is required).

Features not implements:

- DHCP server selection. We take first DHCPOFFER or BOOTPREPLY. No plans
to implement.

- DHCP option concatenation (RFC3396). I do not expect to hit it in real
life and it rather complicates implementation, so let's wait for actual
use case.

- client identifier (RFC6842). So far we expect valid MAC address, which
should be enough to uniquely identify client. It is also not clear how to
generate unique client identifier if we ever need one.

v2: change find_dhcp_option to use subscripts to avoid signed/unsigned
    comparison warning.

    Should fix "may be used uninitialized" warning (although I could not
    reproduce it).

---
 grub-core/net/bootp.c | 759 ++++++++++++++++++++++++++++++++++----------------
 grub-core/net/ip.c    |   2 +-
 include/grub/net.h    |  14 +-
 3 files changed, 528 insertions(+), 247 deletions(-)

diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
index 189551a..16f7cb9 100644
--- a/grub-core/net/bootp.c
+++ b/grub-core/net/bootp.c
@@ -25,25 +25,107 @@
 #include <grub/net/udp.h>
 #include <grub/datetime.h>
 
-static void
-parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask)
+struct grub_dhcp_discover_options
+  {
+  grub_uint8_t magic[4];
+  struct
+    {
+      grub_uint8_t code;
+      grub_uint8_t len;
+      grub_uint8_t data;
+    } GRUB_PACKED  message_type;
+    grub_uint8_t end;
+  } GRUB_PACKED;
+
+struct grub_dhcp_request_options
+  {
+    grub_uint8_t magic[4];
+    struct
+      {
+	grub_uint8_t code;
+	grub_uint8_t len;
+	grub_uint8_t data;
+      } GRUB_PACKED  message_type;
+    struct
+      {
+	grub_uint8_t type;
+	grub_uint8_t len;
+	grub_uint32_t data;
+      } GRUB_PACKED 	server_identifier;
+    struct
+      {
+	grub_uint8_t type;
+	grub_uint8_t len;
+	grub_uint32_t data;
+      } GRUB_PACKED 	requested_ip;
+    struct
+      {
+	grub_uint8_t type;
+	grub_uint8_t len;
+	grub_uint8_t data[7];
+      } GRUB_PACKED 	parameter_request;
+    grub_uint8_t end;
+  } GRUB_PACKED;
+
+enum
+  {
+    GRUB_DHCP_MESSAGE_UNKNOWN,
+    GRUB_DHCP_MESSAGE_DISCOVER,
+    GRUB_DHCP_MESSAGE_OFFER,
+    GRUB_DHCP_MESSAGE_REQUEST,
+    GRUB_DHCP_MESSAGE_DECLINE,
+    GRUB_DHCP_MESSAGE_ACK,
+    GRUB_DHCP_MESSAGE_NAK,
+    GRUB_DHCP_MESSAGE_RELEASE,
+    GRUB_DHCP_MESSAGE_INFORM,
+  };
+
+enum
+  {
+    GRUB_DHCP_OPT_OVERLOAD_FILE = 1,
+    GRUB_DHCP_OPT_OVERLOAD_SNAME = 2,
+  };
+
+#define GRUB_BOOTP_MAX_OPTIONS_SIZE 64
+#define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y)))
+
+/* Max timeout when waiting for BOOTP/DHCP reply */
+#define GRUB_DHCP_MAX_PACKET_TIMEOUT 32
+
+static const void *
+find_dhcp_option (const struct grub_net_bootp_packet *bp, grub_size_t size,
+		 grub_uint8_t opt_code, grub_uint8_t *opt_len)
 {
-  const grub_uint8_t *ptr, *ptr0;
+  const grub_uint8_t *ptr;
+  grub_uint8_t overload = 0;
+  int end = 0;
+  grub_size_t i;
+
+  if (opt_len)
+    *opt_len = 0;
+
+  /* Magic */
+  if (size < sizeof (*bp) + sizeof (grub_uint32_t))
+    goto out;
 
-  ptr = ptr0 = vend;
+  ptr = (grub_uint8_t *) (bp + 1);
 
   if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
       || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
       || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
       || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
-    return;
-  ptr = ptr + sizeof (grub_uint32_t);
-  while (ptr - ptr0 < limit)
+    goto out;
+
+  size -= sizeof (*bp);
+  i = sizeof (grub_uint32_t);
+
+again:
+  while (i < size)
     {
       grub_uint8_t tagtype;
       grub_uint8_t taglength;
 
-      tagtype = *ptr++;
+      tagtype = ptr[i++];
 
       /* Pad tag.  */
       if (tagtype == GRUB_NET_BOOTP_PAD)
@@ -51,84 +133,77 @@ parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask)
 
       /* End tag.  */
       if (tagtype == GRUB_NET_BOOTP_END)
-	return;
+	{
+	  end = 1;
+	  break;
+	}
+
+      if (i >= size)
+	goto out;
 
-      taglength = *ptr++;
+      taglength = ptr[i++];
+      if (i + taglength >= size)
+	goto out;
 
-      switch (tagtype)
+      /* FIXME RFC 3396 options concatentation */
+      if (tagtype == opt_code)
 	{
-	case GRUB_NET_BOOTP_NETMASK:
-	  if (taglength == 4)
-	    {
-	      int i;
-	      for (i = 0; i < 32; i++)
-		if (!(ptr[i / 8] & (1 << (7 - (i % 8)))))
-		  break;
-	      *mask = i;
-	    }
-	  break;
+	  if (opt_len)
+	    *opt_len = taglength;
+	  return &ptr[i];
+	}
 
-	case GRUB_NET_BOOTP_ROUTER:
-	  if (taglength == 4)
-	    {
-	      grub_net_network_level_netaddress_t target;
-	      grub_net_network_level_address_t gw;
-	      char *rname;
-	      
-	      target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-	      target.ipv4.base = 0;
-	      target.ipv4.masksize = 0;
-	      gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-	      grub_memcpy (&gw.ipv4, ptr, sizeof (gw.ipv4));
-	      rname = grub_xasprintf ("%s:default", name);
-	      if (rname)
-		grub_net_add_route_gw (rname, target, gw, NULL);
-	      grub_free (rname);
-	    }
-	  break;
-	case GRUB_NET_BOOTP_DNS:
+      if (tagtype == GRUB_NET_DHCP_OVERLOAD && taglength == 1)
+	overload = ptr[i];
+
+      i += taglength;
+    }
+
+    /* RFC2131, 4.1, 23ff:
+       If the options in a DHCP message extend into the 'sname' and 'file'
+       fields, the 'option overload' option MUST appear in the 'options'
+       field, with value 1, 2 or 3, as specified in RFC 1533.  If the
+       'option overload' option is present in the 'options' field, the
+       options in the 'options' field MUST be terminated by an 'end' option,
+       and MAY contain one or more 'pad' options to fill the options field.
+       The options in the 'sname' and 'file' fields (if in use as indicated
+       by the 'options overload' option) MUST begin with the first octet of
+       the field, MUST be terminated by an 'end' option, and MUST be
+       followed by 'pad' options to fill the remainder of the field.  Any
+       individual option in the 'options', 'sname' and 'file' fields MUST be
+       entirely contained in that field.  The options in the 'options' field
+       MUST be interpreted first, so that any 'option overload' options may
+       be interpreted.  The 'file' field MUST be interpreted next (if the
+       'option overload' option indicates that the 'file' field contains
+       DHCP options), followed by the 'sname' field.
+
+       FIXME: We do not explicitly check for trailing 'pad' options here.
+     */
+    if (end)
+      {
+	end = 0;
+	if (overload & GRUB_DHCP_OPT_OVERLOAD_FILE)
 	  {
-	    int i;
-	    for (i = 0; i < taglength / 4; i++)
-	      {
-		struct grub_net_network_level_address s;
-		s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-		s.ipv4 = grub_get_unaligned32 (ptr);
-		s.option = DNS_OPTION_PREFER_IPV4;
-		grub_net_add_dns_server (&s);
-		ptr += 4;
-	      }
+	    overload &= ~GRUB_DHCP_OPT_OVERLOAD_FILE;
+	    ptr = (grub_uint8_t *) &bp->boot_file[0];
+	    size = sizeof (bp->boot_file);
+	    i = 0;
+	    goto again;
 	  }
-	  continue;
-	case GRUB_NET_BOOTP_HOSTNAME:
-          grub_env_set_net_property (name, "hostname", (const char *) ptr,
-                                     taglength);
-          break;
-
-	case GRUB_NET_BOOTP_DOMAIN:
-          grub_env_set_net_property (name, "domain", (const char *) ptr,
-                                     taglength);
-          break;
-
-	case GRUB_NET_BOOTP_ROOT_PATH:
-          grub_env_set_net_property (name, "rootpath", (const char *) ptr,
-                                     taglength);
-          break;
-
-	case GRUB_NET_BOOTP_EXTENSIONS_PATH:
-          grub_env_set_net_property (name, "extensionspath", (const char *) ptr,
-                                     taglength);
-          break;
-
-	  /* If you need any other options please contact GRUB
-	     development team.  */
-	}
 
-      ptr += taglength;
-    }
-}
+	if (overload & GRUB_DHCP_OPT_OVERLOAD_SNAME)
+	  {
+	    overload &= ~GRUB_DHCP_OPT_OVERLOAD_SNAME;
+	    ptr = (grub_uint8_t *) &bp->server_name[0];
+	    size = sizeof (bp->server_name);
+	    i = 0;
+	    goto again;
+	  }
+      }
 
-#define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - (grub_uint8_t *)(y)))
+out:
+  return 0;
+}
 
 struct grub_net_network_level_interface *
 grub_net_configure_by_dhcp_ack (const char *name,
@@ -142,6 +217,10 @@ grub_net_configure_by_dhcp_ack (const char *name,
   grub_net_link_level_address_t hwaddr;
   struct grub_net_network_level_interface *inter;
   int mask = -1;
+  const grub_uint8_t *opt;
+  grub_uint8_t opt_len, overload = 0;
+  const char *boot_file = 0, *server_name = 0;
+  grub_size_t boot_file_len, server_name_len;
 
   addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
   addr.ipv4 = bp->your_ip;
@@ -160,38 +239,36 @@ grub_net_configure_by_dhcp_ack (const char *name,
   if (!inter)
     return 0;
 
-#if 0
-  /* This is likely based on misunderstanding. gateway_ip refers to
-     address of BOOTP relay and should not be used after BOOTP transaction
-     is complete.
-     See RFC1542, 3.4 Interpretation of the 'giaddr' field
-   */
-  if (bp->gateway_ip)
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_OVERLOAD, &opt_len);
+  if (opt && opt_len == 1)
+    overload = *opt;
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_TFTP_SERVER_NAME, &opt_len);
+  if (opt && opt_len)
     {
-      grub_net_network_level_netaddress_t target;
-      grub_net_network_level_address_t gw;
-      char *rname;
-	  
-      target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-      target.ipv4.base = bp->server_ip;
-      target.ipv4.masksize = 32;
-      gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-      gw.ipv4 = bp->gateway_ip;
-      rname = grub_xasprintf ("%s:gw", name);
-      if (rname)
-	grub_net_add_route_gw (rname, target, gw);
-      grub_free (rname);
+      server_name = (const char *) opt;
+      server_name_len = opt_len;
+    }
+  else if (size > OFFSET_OF (server_name, bp) && !(overload & GRUB_DHCP_OPT_OVERLOAD_SNAME) &&
+	   bp->server_name[0])
+    {
+      server_name = bp->server_name;
+      server_name_len = sizeof (bp->server_name);
+    }
 
-      target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-      target.ipv4.base = bp->gateway_ip;
-      target.ipv4.masksize = 32;
-      grub_net_add_route (name, target, inter);
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_BOOTFILE_NAME, &opt_len);
+  if (opt && opt_len)
+    {
+      boot_file = (const char *) opt;
+      boot_file_len = opt_len;
+    }
+  else if (size > OFFSET_OF (boot_file, bp) && !(overload && GRUB_DHCP_OPT_OVERLOAD_FILE) &&
+	   bp->boot_file[0])
+    {
+      boot_file = bp->boot_file;
+      boot_file_len = sizeof (bp->boot_file);
     }
-#endif
 
-  if (size > OFFSET_OF (boot_file, bp))
-    grub_env_set_net_property (name, "boot_file", bp->boot_file,
-                               sizeof (bp->boot_file));
   if (is_def)
     grub_net_default_server = 0;
   if (is_def && !grub_net_default_server && bp->server_ip)
@@ -219,40 +296,103 @@ grub_net_configure_by_dhcp_ack (const char *name,
 				((grub_uint8_t *) &bp->server_ip)[3]);
       grub_print_error ();
     }
-  if (size > OFFSET_OF (server_name, bp)
-      && bp->server_name[0])
+
+  if (server_name)
     {
-      grub_env_set_net_property (name, "dhcp_server_name", bp->server_name,
-                                 sizeof (bp->server_name));
+      grub_env_set_net_property (name, "dhcp_server_name", server_name, server_name_len);
       if (is_def && !grub_net_default_server)
 	{
-	  grub_net_default_server = grub_strdup (bp->server_name);
+	  grub_net_default_server = grub_strdup (server_name);
 	  grub_print_error ();
 	}
       if (device && !*device)
 	{
-	  *device = grub_xasprintf ("tftp,%s", bp->server_name);
+	  *device = grub_xasprintf ("tftp,%s", server_name);
 	  grub_print_error ();
 	}
     }
 
-  if (size > OFFSET_OF (boot_file, bp) && path)
+  if (boot_file)
     {
-      *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
-      grub_print_error ();
-      if (*path)
+      grub_env_set_net_property (name, "boot_file", boot_file, boot_file_len);
+      if (path)
 	{
-	  char *slash;
-	  slash = grub_strrchr (*path, '/');
-	  if (slash)
-	    *slash = 0;
-	  else
-	    **path = 0;
+	  *path = grub_strndup (boot_file, boot_file_len);
+	  grub_print_error ();
+	  if (*path)
+	    {
+	      char *slash;
+	      slash = grub_strrchr (*path, '/');
+	      if (slash)
+		*slash = 0;
+	      else
+		**path = 0;
+	    }
 	}
     }
-  if (size > OFFSET_OF (vendor, bp))
-    parse_dhcp_vendor (name, &bp->vendor, size - OFFSET_OF (vendor, bp), &mask);
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_NETMASK, &opt_len);
+  if (opt && opt_len == 4)
+    {
+      int i;
+      for (i = 0; i < 32; i++)
+	if (!(opt[i / 8] & (1 << (7 - (i % 8)))))
+	  break;
+      mask = i;
+    }
   grub_net_add_ipv4_local (inter, mask);
+
+  /* We do not implement dead gateway detection and the first entry SHOULD
+     be preferred one */
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_ROUTER, &opt_len);
+  if (opt && opt_len && !(opt_len & 3))
+    {
+      grub_net_network_level_netaddress_t target;
+      grub_net_network_level_address_t gw;
+      char *rname;
+
+      target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+      target.ipv4.base = 0;
+      target.ipv4.masksize = 0;
+      gw.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+      gw.ipv4 = grub_get_unaligned32 (opt);
+      rname = grub_xasprintf ("%s:default", name);
+      if (rname)
+	grub_net_add_route_gw (rname, target, gw, 0);
+      grub_free (rname);
+    }
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_DNS, &opt_len);
+  if (opt && opt_len && !(opt_len & 3))
+    {
+      int i;
+      for (i = 0; i < opt_len / 4; i++)
+	{
+	  struct grub_net_network_level_address s;
+
+	  s.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+	  s.ipv4 = grub_get_unaligned32 (opt);
+	  s.option = DNS_OPTION_PREFER_IPV4;
+	  grub_net_add_dns_server (&s);
+	  opt += 4;
+	}
+    }
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_HOSTNAME, &opt_len);
+  if (opt && opt_len)
+    grub_env_set_net_property (name, "hostname", (const char *) opt, opt_len);
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_DOMAIN, &opt_len);
+  if (opt && opt_len)
+    grub_env_set_net_property (name, "domain", (const char *) opt, opt_len);
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_ROOT_PATH, &opt_len);
+  if (opt && opt_len)
+    grub_env_set_net_property (name, "rootpath", (const char *) opt, opt_len);
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_BOOTP_EXTENSIONS_PATH, &opt_len);
+  if (opt && opt_len)
+    grub_env_set_net_property (name, "extensionspath", (const char *) opt, opt_len);
   
   inter->dhcp_ack = grub_malloc (size);
   if (inter->dhcp_ack)
@@ -266,35 +406,208 @@ grub_net_configure_by_dhcp_ack (const char *name,
   return inter;
 }
 
+static grub_err_t
+send_dhcp_packet (struct grub_net_network_level_interface *iface)
+{
+  grub_err_t err;
+  struct grub_net_bootp_packet *pack;
+  struct grub_datetime date;
+  grub_int32_t t = 0;
+  struct grub_net_buff *nb;
+  struct udphdr *udph;
+  grub_net_network_level_address_t target;
+  grub_net_link_level_address_t ll_target;
+
+  static struct grub_dhcp_discover_options discover_options =
+    {
+      {
+	GRUB_NET_BOOTP_RFC1048_MAGIC_0,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_1,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_2,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_3,
+      },
+      {
+	GRUB_NET_DHCP_MESSAGE_TYPE,
+	sizeof (discover_options.message_type.data),
+	GRUB_DHCP_MESSAGE_DISCOVER,
+      },
+      GRUB_NET_BOOTP_END,
+    };
+
+  static struct grub_dhcp_request_options request_options =
+    {
+      {
+	GRUB_NET_BOOTP_RFC1048_MAGIC_0,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_1,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_2,
+	GRUB_NET_BOOTP_RFC1048_MAGIC_3,
+      },
+      {
+	GRUB_NET_DHCP_MESSAGE_TYPE,
+	sizeof (request_options.message_type.data),
+	GRUB_DHCP_MESSAGE_REQUEST,
+      },
+      {
+	GRUB_NET_DHCP_SERVER_IDENTIFIER,
+	sizeof (request_options.server_identifier.data),
+	0,
+      },
+      {
+	GRUB_NET_DHCP_REQUESTED_IP_ADDRESS,
+	sizeof (request_options.requested_ip.data),
+	0,
+      },
+      {
+	GRUB_NET_DHCP_PARAMETER_REQUEST_LIST,
+	sizeof (request_options.parameter_request.data),
+	{
+	  GRUB_NET_BOOTP_NETMASK,
+	  GRUB_NET_BOOTP_ROUTER,
+	  GRUB_NET_BOOTP_DNS,
+	  GRUB_NET_BOOTP_DOMAIN,
+	  GRUB_NET_BOOTP_HOSTNAME,
+	  GRUB_NET_BOOTP_ROOT_PATH,
+	  GRUB_NET_BOOTP_EXTENSIONS_PATH,
+	},
+      },
+      GRUB_NET_BOOTP_END,
+    };
+
+  COMPILE_TIME_ASSERT (sizeof (discover_options) <= GRUB_BOOTP_MAX_OPTIONS_SIZE);
+  COMPILE_TIME_ASSERT (sizeof (request_options) <= GRUB_BOOTP_MAX_OPTIONS_SIZE);
+
+  nb = grub_netbuff_alloc (sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
+  if (!nb)
+    return grub_errno;
+
+  err = grub_netbuff_reserve (nb, sizeof (*pack) + GRUB_BOOTP_MAX_OPTIONS_SIZE + 128);
+  if (err)
+    goto out;
+
+  err = grub_netbuff_push (nb, GRUB_BOOTP_MAX_OPTIONS_SIZE);
+  if (err)
+    goto out;
+
+  grub_memset (nb->data, 0, GRUB_BOOTP_MAX_OPTIONS_SIZE);
+  if (!iface->srv_id)
+    {
+      grub_memcpy (nb->data, &discover_options, sizeof (discover_options));
+    }
+  else
+    {
+      struct grub_dhcp_request_options *ro = (struct grub_dhcp_request_options *) nb->data;
+
+      grub_memcpy (nb->data, &request_options, sizeof (request_options));
+      /* my_ip and srv_id are stored in network order so do not need conversion. */
+      grub_set_unaligned32 (&ro->server_identifier.data, iface->srv_id);
+      grub_set_unaligned32 (&ro->requested_ip.data, iface->my_ip);
+    }
+
+  err = grub_netbuff_push (nb, sizeof (*pack));
+  if (err)
+    goto out;
+
+  pack = (void *) nb->data;
+  grub_memset (pack, 0, sizeof (*pack));
+  pack->opcode = 1;
+  pack->hw_type = 1;
+  pack->hw_len = 6;
+  err = grub_get_datetime (&date);
+  if (err || !grub_datetime2unixtime (&date, &t))
+    {
+      grub_errno = GRUB_ERR_NONE;
+      t = 0;
+    }
+  pack->seconds = grub_cpu_to_be16 (t);
+  if (!iface->srv_id)
+    iface->xid = pack->ident = grub_cpu_to_be32 (t);
+  else
+    pack->ident = iface->xid;
+
+  grub_memcpy (&pack->mac_addr, &iface->hwaddress.mac, 6);
+
+  grub_netbuff_push (nb, sizeof (*udph));
+
+  udph = (struct udphdr *) nb->data;
+  udph->src = grub_cpu_to_be16_compile_time (68);
+  udph->dst = grub_cpu_to_be16_compile_time (67);
+  udph->chksum = 0;
+  udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
+  target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
+  target.ipv4 = 0xffffffff;
+  err = grub_net_link_layer_resolve (iface, &target, &ll_target);
+  if (err)
+    goto out;
+
+  udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
+						 &iface->address,
+						 &target);
+
+  err = grub_net_send_ip_packet (iface, &target, &ll_target, nb,
+				 GRUB_NET_IP_UDP);
+
+out:
+  grub_netbuff_free (nb);
+  return err;
+}
+
 void
 grub_net_process_dhcp (struct grub_net_buff *nb,
-		       struct grub_net_card *card)
+		       struct grub_net_network_level_interface *iface)
 {
   char *name;
-  struct grub_net_network_level_interface *inf;
-
-  name = grub_xasprintf ("%s:dhcp", card->name);
-  if (!name)
+  struct grub_net_card *card = iface->card;
+  const struct grub_net_bootp_packet *bp = (const struct grub_net_bootp_packet *) nb->data;
+  grub_size_t size = nb->tail - nb->data;
+  const grub_uint8_t *opt;
+  grub_uint8_t opt_len, type;
+  grub_uint32_t srv_id = 0;
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_MESSAGE_TYPE, &opt_len);
+  if (opt && opt_len == 1)
+    type = *opt;
+  else
+    type = GRUB_DHCP_MESSAGE_UNKNOWN;
+
+  opt = find_dhcp_option (bp, size, GRUB_NET_DHCP_SERVER_IDENTIFIER, &opt_len);
+  if (opt && opt_len == sizeof (srv_id))
+    srv_id = grub_get_unaligned32 (opt);
+
+  /* If we received BOOTP reply or DHCPACK, proceed with configuration. Otherwise
+     store offered address and server id for later processing of DHCPACK.
+     xid and srv_id are stored in network order so do not need conversion. */
+  if ((!iface->srv_id && type == GRUB_DHCP_MESSAGE_UNKNOWN)
+      || (iface->srv_id && type == GRUB_DHCP_MESSAGE_ACK
+	  && bp->ident == iface->xid
+	  && srv_id == iface->srv_id))
     {
-      grub_print_error ();
-      return;
+      name = grub_xasprintf ("%s:dhcp", card->name);
+      if (!name)
+	{
+	  grub_print_error ();
+	  return;
+	}
+      grub_net_configure_by_dhcp_ack (name, card, 0, bp, size, 0, 0, 0);
+      grub_free (name);
+      if (grub_errno)
+	grub_print_error ();
+      else
+	grub_net_network_level_interface_unregister (iface);
     }
-  grub_net_configure_by_dhcp_ack (name, card,
-				  0, (const struct grub_net_bootp_packet *) nb->data,
-				  (nb->tail - nb->data), 0, 0, 0);
-  grub_free (name);
-  if (grub_errno)
-    grub_print_error ();
-  else
+  else if (!iface->srv_id && type == GRUB_DHCP_MESSAGE_OFFER && srv_id)
     {
-      FOR_NET_NETWORK_LEVEL_INTERFACES(inf)
-	if (grub_memcmp (inf->name, card->name, grub_strlen (card->name)) == 0
-	    && grub_memcmp (inf->name + grub_strlen (card->name),
-			    ":dhcp_tmp", sizeof (":dhcp_tmp") - 1) == 0)
-	  {
-	    grub_net_network_level_interface_unregister (inf);
-	    break;
-	  }
+      iface->srv_id = srv_id;
+      iface->my_ip = bp->your_ip;
+      /* Reset retransmission timer */
+      iface->dhcp_tmo = iface->dhcp_tmo_left = 1;
+    }
+  else if (iface->srv_id && type == GRUB_DHCP_MESSAGE_NAK
+	   && bp->ident == iface->xid
+	   && srv_id == iface->srv_id)
+    {
+      iface->xid = iface->srv_id = iface->my_ip = 0;
+      /* Reset retransmission timer */
+      iface->dhcp_tmo = iface->dhcp_tmo_left = 1;
     }
 }
 
@@ -311,8 +624,8 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
 		  int argc, char **args)
 {
   struct grub_net_network_level_interface *inter;
-  int num;
-  grub_uint8_t *ptr;
+  unsigned num;
+  const grub_uint8_t *ptr;
   grub_uint8_t taglength;
 
   if (argc < 4)
@@ -330,44 +643,27 @@ grub_cmd_dhcpopt (struct grub_command *cmd __attribute__ ((unused)),
   if (!inter->dhcp_ack)
     return grub_error (GRUB_ERR_IO, N_("no DHCP info found"));
 
-  if (inter->dhcp_acklen <= OFFSET_OF (vendor, inter->dhcp_ack))
-    return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
-
-  num = grub_strtoul (args[2], 0, 0);
-  if (grub_errno)
-    return grub_errno;
-
   ptr = inter->dhcp_ack->vendor;
 
-  if (ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
+  /* This duplicates check in find_dhcp_option to preserve previous error return */
+  if (inter->dhcp_acklen < OFFSET_OF (vendor, inter->dhcp_ack) + sizeof (grub_uint32_t)
+      || ptr[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
       || ptr[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
       || ptr[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
       || ptr[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
     return grub_error (GRUB_ERR_IO, N_("no DHCP options found"));
-  ptr = ptr + sizeof (grub_uint32_t);
-  while (1)
-    {
-      grub_uint8_t tagtype;
-
-      if (ptr >= ((grub_uint8_t *) inter->dhcp_ack) + inter->dhcp_acklen)
-	return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
-
-      tagtype = *ptr++;
 
-      /* Pad tag.  */
-      if (tagtype == 0)
-	continue;
+  num = grub_strtoul (args[2], 0, 0);
+  if (grub_errno)
+    return grub_errno;
 
-      /* End tag.  */
-      if (tagtype == 0xff)
-	return grub_error (GRUB_ERR_IO, N_("no DHCP option %d found"), num);
+  /* Exclude PAD (0) and END (255) option codes */
+  if (num == 0 || num > 254)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("invalid DHCP option code"));
 
-      taglength = *ptr++;
-	
-      if (tagtype == num)
-	break;
-      ptr += taglength;
-    }
+  ptr = find_dhcp_option (inter->dhcp_ack, inter->dhcp_acklen, num, &taglength);
+  if (!ptr)
+    return grub_error (GRUB_ERR_IO, N_("no DHCP option %u found"), num);
 
   if (grub_strcmp (args[3], "string") == 0)
     {
@@ -437,8 +733,8 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
   struct grub_net_network_level_interface *ifaces;
   grub_size_t ncards = 0;
   unsigned j = 0;
-  int interval;
   grub_err_t err;
+  unsigned i;
 
   FOR_NET_CARDS (card)
   {
@@ -467,7 +763,6 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
     card->num_ifaces++;
     if (!ifaces[j].name)
       {
-	unsigned i;
 	for (i = 0; i < j; i++)
 	  grub_free (ifaces[i].name);
 	grub_free (ifaces);
@@ -476,6 +771,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
     ifaces[j].address.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV;
     grub_memcpy (&ifaces[j].hwaddress, &card->default_address, 
 		 sizeof (ifaces[j].hwaddress));
+    ifaces[j].dhcp_tmo = ifaces[j].dhcp_tmo_left = 1;
     j++;
   }
   ifaces[ncards - 1].next = grub_net_network_level_interfaces;
@@ -483,85 +779,54 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
     grub_net_network_level_interfaces->prev = & ifaces[ncards - 1].next;
   grub_net_network_level_interfaces = &ifaces[0];
   ifaces[0].prev = &grub_net_network_level_interfaces;
-  for (interval = 200; interval < 10000; interval *= 2)
+
+  /* Running DHCP restransmission timer is kept per interface in dhcp_tmo_left.
+     When it runs off, dhcp_tmo is increased exponentionally and dhcp_tmo_left
+     initialized to it. Max value is 32 which gives approximately 12s total per
+     packet timeout assuming 200ms poll tick. Timeout is reset when DHCP OFFER
+     is received, so total timeout is 25s in the worst case.
+
+     DHCP NAK also resets timer and transaction starts again.
+
+     Total wait time is limited to ~25s to prevent endless loop in case of
+     permanent NAK
+   */
+  for (i = 0; i < GRUB_DHCP_MAX_PACKET_TIMEOUT * 4; i++)
     {
-      int done = 0;
+      int need_poll = 0;
       for (j = 0; j < ncards; j++)
 	{
-	  struct grub_net_bootp_packet *pack;
-	  struct grub_datetime date;
-	  grub_int32_t t = 0;
-	  struct grub_net_buff *nb;
-	  struct udphdr *udph;
-	  grub_net_network_level_address_t target;
-	  grub_net_link_level_address_t ll_target;
-
-	  if (!ifaces[j].prev)
+	  if (!ifaces[j].prev || ifaces[j].dhcp_tmo > GRUB_DHCP_MAX_PACKET_TIMEOUT)
 	    continue;
-	  nb = grub_netbuff_alloc (sizeof (*pack) + 64 + 128);
-	  if (!nb)
-	    {
-	      grub_netbuff_free (nb);
-	      return grub_errno;
-	    }
-	  err = grub_netbuff_reserve (nb, sizeof (*pack) + 64 + 128);
-	  if (err)
-	    {
-	      grub_netbuff_free (nb);
-	      return err;
-	    }
-	  err = grub_netbuff_push (nb, sizeof (*pack) + 64);
-	  if (err)
-	    {
-	      grub_netbuff_free (nb);
-	      return err;
-	    }
-	  pack = (void *) nb->data;
-	  done = 1;
-	  grub_memset (pack, 0, sizeof (*pack) + 64);
-	  pack->opcode = 1;
-	  pack->hw_type = 1;
-	  pack->hw_len = 6;
-	  err = grub_get_datetime (&date);
-	  if (err || !grub_datetime2unixtime (&date, &t))
+
+	  if (--ifaces[j].dhcp_tmo_left)
 	    {
-	      grub_errno = GRUB_ERR_NONE;
-	      t = 0;
+	      need_poll = 1;
+	      continue;
 	    }
-	  pack->ident = grub_cpu_to_be32 (t);
-	  pack->seconds = grub_cpu_to_be16 (t);
-
-	  grub_memcpy (&pack->mac_addr, &ifaces[j].hwaddress.mac, 6); 
-
-	  grub_netbuff_push (nb, sizeof (*udph));
-
-	  udph = (struct udphdr *) nb->data;
-	  udph->src = grub_cpu_to_be16_compile_time (68);
-	  udph->dst = grub_cpu_to_be16_compile_time (67);
-	  udph->chksum = 0;
-	  udph->len = grub_cpu_to_be16 (nb->tail - nb->data);
-	  target.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
-	  target.ipv4 = 0xffffffff;
-	  err = grub_net_link_layer_resolve (&ifaces[j], &target, &ll_target);
-	  if (err)
-	    return err;
 
-	  udph->chksum = grub_net_ip_transport_checksum (nb, GRUB_NET_IP_UDP,
-							 &ifaces[j].address,
-							 &target);
+	  ifaces[j].dhcp_tmo *= 2;
+	  if (ifaces[j].dhcp_tmo > GRUB_DHCP_MAX_PACKET_TIMEOUT)
+	    continue;
 
-	  err = grub_net_send_ip_packet (&ifaces[j], &target, &ll_target, nb,
-					 GRUB_NET_IP_UDP);
-	  grub_netbuff_free (nb);
+	  err = send_dhcp_packet (&ifaces[j]);
 	  if (err)
-	    return err;
+	    {
+	      grub_print_error ();
+	      /* To ignore it during next poll */
+	      ifaces[j].dhcp_tmo = GRUB_DHCP_MAX_PACKET_TIMEOUT + 1;
+	      continue;
+	    }
+	  ifaces[j].dhcp_tmo_left = ifaces[j].dhcp_tmo;
+	  need_poll = 1;
 	}
-      if (!done)
+      if (!need_poll)
 	break;
-      grub_net_poll_cards (interval, 0);
+      grub_net_poll_cards (200, 0);
     }
 
   err = GRUB_ERR_NONE;
+
   for (j = 0; j < ncards; j++)
     {
       grub_free (ifaces[j].name);
@@ -578,7 +843,7 @@ grub_cmd_bootp (struct grub_command *cmd __attribute__ ((unused)),
   return err;
 }
 
-static grub_command_t cmd_getdhcp, cmd_bootp;
+static grub_command_t cmd_getdhcp, cmd_bootp, cmd_dhcp;
 
 void
 grub_bootp_init (void)
@@ -586,6 +851,9 @@ grub_bootp_init (void)
   cmd_bootp = grub_register_command ("net_bootp", grub_cmd_bootp,
 				     N_("[CARD]"),
 				     N_("perform a bootp autoconfiguration"));
+  cmd_dhcp = grub_register_command ("net_dhcp", grub_cmd_bootp,
+				     N_("[CARD]"),
+				     N_("perform a DHCP autoconfiguration"));
   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."));
@@ -596,4 +864,5 @@ grub_bootp_fini (void)
 {
   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 8c56baa..162c70d 100644
--- a/grub-core/net/ip.c
+++ b/grub-core/net/ip.c
@@ -278,7 +278,7 @@ handle_dgram (struct grub_net_buff *nb,
 	      && grub_memcmp (inf->hwaddress.mac, &bootp->mac_addr,
 			      sizeof (inf->hwaddress.mac)) == 0)
 	    {
-	      grub_net_process_dhcp (nb, inf->card);
+	      grub_net_process_dhcp (nb, inf);
 	      grub_netbuff_free (nb);
 	      return GRUB_ERR_NONE;
 	    }
diff --git a/include/grub/net.h b/include/grub/net.h
index 2192fa1..6d4a7e5 100644
--- a/include/grub/net.h
+++ b/include/grub/net.h
@@ -291,6 +291,11 @@ struct grub_net_network_level_interface
   grub_net_interface_flags_t flags;
   struct grub_net_bootp_packet *dhcp_ack;
   grub_size_t dhcp_acklen;
+  grub_uint32_t xid;      /* DHCPv4 transaction id */
+  grub_uint32_t srv_id;   /* DHCPv4 server_identifier */
+  grub_uint32_t my_ip;    /* DHCPv4 offered IP address */
+  unsigned dhcp_tmo_left; /* DHCPv4 running retransmission timeout */
+  unsigned dhcp_tmo;      /* DHCPv4 current retransmission timeout */
   void *data;
 };
 
@@ -456,6 +461,13 @@ enum
     GRUB_NET_BOOTP_DOMAIN = 0x0f,
     GRUB_NET_BOOTP_ROOT_PATH = 0x11,
     GRUB_NET_BOOTP_EXTENSIONS_PATH = 0x12,
+    GRUB_NET_DHCP_REQUESTED_IP_ADDRESS = 50,
+    GRUB_NET_DHCP_OVERLOAD = 52,
+    GRUB_NET_DHCP_MESSAGE_TYPE = 53,
+    GRUB_NET_DHCP_SERVER_IDENTIFIER = 54,
+    GRUB_NET_DHCP_PARAMETER_REQUEST_LIST = 55,
+    GRUB_NET_DHCP_TFTP_SERVER_NAME = 66,
+    GRUB_NET_DHCP_BOOTFILE_NAME = 67,
     GRUB_NET_BOOTP_END = 0xff
   };
 
@@ -473,7 +485,7 @@ grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf,
 
 void
 grub_net_process_dhcp (struct grub_net_buff *nb,
-		       struct grub_net_card *card);
+		       struct grub_net_network_level_interface *iface);
 
 int
 grub_net_hwaddr_cmp (const grub_net_link_level_address_t *a,
-- 
tg: (f4d35d4..) u/dhcpv4 (depends on: master)


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

* Re: [PATCH v2] bootp: add native DHCPv4 support
  2016-03-22 18:26 ` [PATCH v2] " Andrei Borzenkov
@ 2016-04-27 14:40   ` Punit Agrawal
  2016-04-27 15:34   ` Mark Rutland
  1 sibling, 0 replies; 7+ messages in thread
From: Punit Agrawal @ 2016-04-27 14:40 UTC (permalink / raw)
  To: The development of GNU GRUB; +Cc: mark.rutland, matthew.t.rivard

Hi Andrei,

Thanks for refreshing the patch. I was using a locally fixed up version
of your wip post from earlier in the year.

On 22/03/16 18:26, Andrei Borzenkov wrote:
> This patch adds support for native DHCPv4 and removes requirement for
> BOOTP compatibility support in DHCP server.
>
> There is no provision for selecting preferred server. We take the first
> OFFER and try to REQUEST configuration from it. If NAK was received,
> transaction is restarted, but if we hit timeout, configuration fails.
>
> It also handles pure BOOTP reply (detected by lack of DHCP message type
> option) and proceeds with configuration immedately. I could not test it
> because I do not have pure BOOTP server.
>
> Because we need access to DHCP options in multiple places now, it also
> factors out DHCP option processing, adding support for option overload.
>
> Timeout handling is now per-interface, with independent timeouts for
> both DISCOVER and REQUEST. It should make it more responsive if answer
> was delayed initially. Total timeout remains the same as originally, as
> well as backoff algorithm, but we poll in fixed 200ms ticks so notice
> (successful) reply earlier.
>
> Failure to send packet to interface now does not terminate command (and
> leaks memory) but rather simply ignores this interface and continues with
> remaining ones if present.
>
> Finally it adds net_dhcp alias with intention to deprecate net_bootp
> completely (it would be possible to make net_bootp to actually send BOOTP
> packet if someone thinks it is required).
>

The patch works for me on an arm64 board. With the patch, the board is
able to get an IP via DHCP and subsequently is able to load a kernel
image via tftp.

I haven't tested the bootp functionality as I don't have a DHCP server I
can control.

> Features not implements:
>
> - DHCP server selection. We take first DHCPOFFER or BOOTPREPLY. No plans
> to implement.
>
> - DHCP option concatenation (RFC3396). I do not expect to hit it in real
> life and it rather complicates implementation, so let's wait for actual
> use case.
>
> - client identifier (RFC6842). So far we expect valid MAC address, which
> should be enough to uniquely identify client. It is also not clear how to
> generate unique client identifier if we ever need one.


The unimplemented features don't seem to be an issue in my environment.

It would be great to see this functionality merged and working out of
the box.

Thanks for your efforts.

Punit

>
> v2: change find_dhcp_option to use subscripts to avoid signed/unsigned
>      comparison warning.
>
>      Should fix "may be used uninitialized" warning (although I could not
>      reproduce it).
>
> ---
>   grub-core/net/bootp.c | 759 ++++++++++++++++++++++++++++++++++----------------
>   grub-core/net/ip.c    |   2 +-
>   include/grub/net.h    |  14 +-
>   3 files changed, 528 insertions(+), 247 deletions(-)
>

[...]
IMPORTANT NOTICE: The contents of this email and any attachments are confidential and may also be privileged. If you are not the intended recipient, please notify the sender immediately and do not disclose the contents to any other person, use it for any purpose, or store or copy the information in any medium. Thank you.



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

* Re: [PATCH v2] bootp: add native DHCPv4 support
  2016-03-22 18:26 ` [PATCH v2] " Andrei Borzenkov
  2016-04-27 14:40   ` Punit Agrawal
@ 2016-04-27 15:34   ` Mark Rutland
  2016-11-17 19:06     ` Daniel Kiper
  1 sibling, 1 reply; 7+ messages in thread
From: Mark Rutland @ 2016-04-27 15:34 UTC (permalink / raw)
  To: Andrei Borzenkov; +Cc: grub-devel, matthew.t.rivard

Hi,

On Tue, Mar 22, 2016 at 09:26:20PM +0300, Andrei Borzenkov wrote:
> This patch adds support for native DHCPv4 and removes requirement for
> BOOTP compatibility support in DHCP server.

Sorry for the delay in testing this; I'd meant to take a look at this
but it managed to fall off my radar.

I've just given this a spin atop of 7a5b301e3adb8e05 ("build: Use
AC_HEADER_MAJOR to find device macros"), with a small conflict manually
fixed up (I assume that the NULL check for inter still stands).

Otherwise, this seems to work perfectly for me in my BOOT-free work
environment on an AArch64 UEFI platform:

grub> insmod efinet
grub> net_ls_cards
efinet0 00:02:f7:00:61:79
grub> net_dhcp efinet0
grub> net_default_server=10.1.205.151
grub> linux (tftp)/Image.gz
grub> boot
EFI stub: Booting Linux Kernel...
EFI stub: Using DTB from configuration table
EFI stub: Exiting boot services and installing virtual address map...
[    0.000000] Booting Linux on physical CPU 0x100
[    0.000000] Linux version 4.6.0-rc5-next-20160427+ (mark@leverpostej) (gcc version 5.1.1 20150608 (Linaro GCC 5.1-2015.08) ) #8 SMP PREEMPT Wed Apr 27 14:53:05 BST 2016
...

As previously, I don't have a BOOTP server to hand to test with; sorry
for not being able to test that.

It would be great to see this committed!

Thanks,
Mark.


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

* Re: [PATCH v2] bootp: add native DHCPv4 support
  2016-04-27 15:34   ` Mark Rutland
@ 2016-11-17 19:06     ` Daniel Kiper
  2016-11-18 17:51       ` Andrei Borzenkov
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Kiper @ 2016-11-17 19:06 UTC (permalink / raw)
  To: arvidjaar, phcoder, grub-devel; +Cc: matthew.t.rivard

On Wed, Apr 27, 2016 at 04:34:19PM +0100, Mark Rutland wrote:
> Hi,
>
> On Tue, Mar 22, 2016 at 09:26:20PM +0300, Andrei Borzenkov wrote:
> > This patch adds support for native DHCPv4 and removes requirement for
> > BOOTP compatibility support in DHCP server.
>
> Sorry for the delay in testing this; I'd meant to take a look at this
> but it managed to fall off my radar.
>
> I've just given this a spin atop of 7a5b301e3adb8e05 ("build: Use
> AC_HEADER_MAJOR to find device macros"), with a small conflict manually
> fixed up (I assume that the NULL check for inter still stands).
>
> Otherwise, this seems to work perfectly for me in my BOOT-free work
> environment on an AArch64 UEFI platform:
>
> grub> insmod efinet
> grub> net_ls_cards
> efinet0 00:02:f7:00:61:79
> grub> net_dhcp efinet0
> grub> net_default_server=10.1.205.151
> grub> linux (tftp)/Image.gz
> grub> boot
> EFI stub: Booting Linux Kernel...
> EFI stub: Using DTB from configuration table
> EFI stub: Exiting boot services and installing virtual address map...
> [    0.000000] Booting Linux on physical CPU 0x100
> [    0.000000] Linux version 4.6.0-rc5-next-20160427+ (mark@leverpostej) (gcc version 5.1.1 20150608 (Linaro GCC 5.1-2015.08) ) #8 SMP PREEMPT Wed Apr 27 14:53:05 BST 2016
> ...
>
> As previously, I don't have a BOOTP server to hand to test with; sorry
> for not being able to test that.
>
> It would be great to see this committed!

Vladimir, Andrei, should we take it into 2.02 train?

Daniel


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

* Re: [PATCH v2] bootp: add native DHCPv4 support
  2016-11-17 19:06     ` Daniel Kiper
@ 2016-11-18 17:51       ` Andrei Borzenkov
  2016-11-22  9:22         ` Daniel Kiper
  0 siblings, 1 reply; 7+ messages in thread
From: Andrei Borzenkov @ 2016-11-18 17:51 UTC (permalink / raw)
  To: Daniel Kiper, phcoder, grub-devel; +Cc: matthew.t.rivard

17.11.2016 22:06, Daniel Kiper пишет:
> On Wed, Apr 27, 2016 at 04:34:19PM +0100, Mark Rutland wrote:
>> Hi,
>>
>> On Tue, Mar 22, 2016 at 09:26:20PM +0300, Andrei Borzenkov wrote:
>>> This patch adds support for native DHCPv4 and removes requirement for
>>> BOOTP compatibility support in DHCP server.
>>
>> Sorry for the delay in testing this; I'd meant to take a look at this
>> but it managed to fall off my radar.
>>
>> I've just given this a spin atop of 7a5b301e3adb8e05 ("build: Use
>> AC_HEADER_MAJOR to find device macros"), with a small conflict manually
>> fixed up (I assume that the NULL check for inter still stands).
>>
>> Otherwise, this seems to work perfectly for me in my BOOT-free work
>> environment on an AArch64 UEFI platform:
>>
>> grub> insmod efinet
>> grub> net_ls_cards
>> efinet0 00:02:f7:00:61:79
>> grub> net_dhcp efinet0
>> grub> net_default_server=10.1.205.151
>> grub> linux (tftp)/Image.gz
>> grub> boot
>> EFI stub: Booting Linux Kernel...
>> EFI stub: Using DTB from configuration table
>> EFI stub: Exiting boot services and installing virtual address map...
>> [    0.000000] Booting Linux on physical CPU 0x100
>> [    0.000000] Linux version 4.6.0-rc5-next-20160427+ (mark@leverpostej) (gcc version 5.1.1 20150608 (Linaro GCC 5.1-2015.08) ) #8 SMP PREEMPT Wed Apr 27 14:53:05 BST 2016
>> ...
>>
>> As previously, I don't have a BOOTP server to hand to test with; sorry
>> for not being able to test that.
>>
>> It would be great to see this committed!
> 
> Vladimir, Andrei, should we take it into 2.02 train?
> 

I do not think so, not at this time, sorry. It is far too intrusive
change, and patch is obviously incomplete, in particular, it does not
touch lease renewal at all. I also think we should keep net_bootp doing
what it does now, and provide new net_dhcp, not replacing exiting
functionality.

What we do need is to fix manual to not claim we are doing DHCP right now.


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

* Re: [PATCH v2] bootp: add native DHCPv4 support
  2016-11-18 17:51       ` Andrei Borzenkov
@ 2016-11-22  9:22         ` Daniel Kiper
  0 siblings, 0 replies; 7+ messages in thread
From: Daniel Kiper @ 2016-11-22  9:22 UTC (permalink / raw)
  To: arvidjaar, grub-devel; +Cc: dkiper, phcoder, matthew.t.rivard

On Fri, Nov 18, 2016 at 08:51:43PM +0300, Andrei Borzenkov wrote:
> 17.11.2016 22:06, Daniel Kiper ??????????:
> > On Wed, Apr 27, 2016 at 04:34:19PM +0100, Mark Rutland wrote:
> >> Hi,
> >>
> >> On Tue, Mar 22, 2016 at 09:26:20PM +0300, Andrei Borzenkov wrote:
> >>> This patch adds support for native DHCPv4 and removes requirement for
> >>> BOOTP compatibility support in DHCP server.
> >>
> >> Sorry for the delay in testing this; I'd meant to take a look at this
> >> but it managed to fall off my radar.
> >>
> >> I've just given this a spin atop of 7a5b301e3adb8e05 ("build: Use
> >> AC_HEADER_MAJOR to find device macros"), with a small conflict manually
> >> fixed up (I assume that the NULL check for inter still stands).
> >>
> >> Otherwise, this seems to work perfectly for me in my BOOT-free work
> >> environment on an AArch64 UEFI platform:
> >>
> >> grub> insmod efinet
> >> grub> net_ls_cards
> >> efinet0 00:02:f7:00:61:79
> >> grub> net_dhcp efinet0
> >> grub> net_default_server=10.1.205.151
> >> grub> linux (tftp)/Image.gz
> >> grub> boot
> >> EFI stub: Booting Linux Kernel...
> >> EFI stub: Using DTB from configuration table
> >> EFI stub: Exiting boot services and installing virtual address map...
> >> [    0.000000] Booting Linux on physical CPU 0x100
> >> [    0.000000] Linux version 4.6.0-rc5-next-20160427+ (mark@leverpostej) (gcc version 5.1.1 20150608 (Linaro GCC 5.1-2015.08) ) #8 SMP PREEMPT Wed Apr 27 14:53:05 BST 2016
> >> ...
> >>
> >> As previously, I don't have a BOOTP server to hand to test with; sorry
> >> for not being able to test that.
> >>
> >> It would be great to see this committed!
> >
> > Vladimir, Andrei, should we take it into 2.02 train?
> >
>
> I do not think so, not at this time, sorry. It is far too intrusive
> change, and patch is obviously incomplete, in particular, it does not

OK, added to list of tasks after 2.02 release.

> touch lease renewal at all. I also think we should keep net_bootp doing
> what it does now, and provide new net_dhcp, not replacing exiting
> functionality.

That it good idea.

> What we do need is to fix manual to not claim we are doing DHCP right now.

Make sense for me.

Daniel


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

end of thread, other threads:[~2016-11-22  9:22 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-03-20  7:29 [PATCH] bootp: add native DHCPv4 support Andrei Borzenkov
2016-03-22 18:26 ` [PATCH v2] " Andrei Borzenkov
2016-04-27 14:40   ` Punit Agrawal
2016-04-27 15:34   ` Mark Rutland
2016-11-17 19:06     ` Daniel Kiper
2016-11-18 17:51       ` Andrei Borzenkov
2016-11-22  9:22         ` Daniel Kiper

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.