grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH v3 02/10] net: dhcp: replace parse_dhcp_vendor() with find_dhcp_o


From: Andre Przywara
Subject: [PATCH v3 02/10] net: dhcp: replace parse_dhcp_vendor() with find_dhcp_option()
Date: Thu, 7 Mar 2019 15:14:08 +0000

From: Andrei Borzenkov <address@hidden>

For proper DHCP support we will need to parse DHCP options from a packet
more often and at various places.

Refactor the option parsing into a new function, which will scan a
packet to find *a particular* option field.
Use that new function in places where we were dealing with DHCP options
before.

Signed-off-by: Andre Przywara <address@hidden>
---
 grub-core/net/bootp.c | 227 ++++++++++++++++++++++--------------------
 1 file changed, 117 insertions(+), 110 deletions(-)

diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
index c92dfbd3a..8d6763689 100644
--- a/grub-core/net/bootp.c
+++ b/grub-core/net/bootp.c
@@ -25,25 +25,41 @@
 #include <grub/net/udp.h>
 #include <grub/datetime.h>
 
-static void
-parse_dhcp_vendor (const char *name, const void *vend, int limit, int *mask)
+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_size_t i;
 
-  ptr = ptr0 = vend;
+  if (opt_len)
+    *opt_len = 0;
+
+  /* Is the packet big enough to hold at least the magic cookie? */
+  if (size < sizeof (*bp) + sizeof (grub_uint32_t))
+    return NULL;
+
+  /*
+   * Pointer arithmetic to point behind the common stub packet, where
+   * the options start.
+   */
+  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)
+    return NULL;
+
+  size -= sizeof (*bp);
+  i = sizeof (grub_uint32_t);
+
+  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,81 +67,27 @@ parse_dhcp_vendor (const char *name, const void *vend, int 
limit, int *mask)
 
       /* End tag.  */
       if (tagtype == GRUB_NET_BOOTP_END)
-       return;
+       break;
 
-      taglength = *ptr++;
+      if (i >= size)
+       return NULL;
 
-      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;
+      taglength = ptr[i++];
+      if (i + taglength >= size)
+       return NULL;
 
-       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[i];
        }
 
-      ptr += taglength;
+      i += taglength;
     }
+
+  return NULL;
 }
 
 #define OFFSET_OF(x, y) ((grub_size_t)((grub_uint8_t *)((y)->x) - 
(grub_uint8_t *)(y)))
@@ -143,6 +105,8 @@ grub_net_configure_by_dhcp_ack (const char *name,
   struct grub_net_network_level_interface *inter;
   int mask = -1;
   char server_ip[sizeof ("xxx.xxx.xxx.xxx")];
+  const grub_uint8_t *opt;
+  grub_uint8_t opt_len;
 
   addr.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4;
   addr.ipv4 = bp->your_ip;
@@ -225,9 +189,69 @@ grub_net_configure_by_dhcp_ack (const char *name,
            **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)
@@ -286,8 +310,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)
@@ -305,44 +329,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)
     {
-- 
2.17.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]