grub-devel
[Top][All Lists]
Advanced

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

[PATCH] efinet: get bootstrap info from proxy offer packet


From: chen zhihui
Subject: [PATCH] efinet: get bootstrap info from proxy offer packet
Date: Thu, 21 Apr 2016 08:36:37 +0000

From: chenzhihui <address@hidden>

Bootstrap server ip address and boot file name maybe come from
a separate proxy DHCP server, check the proxy_offer packet if
failed with dhcp_ack packet.

Signed-off-by: chenzhihui <address@hidden>
Tested-by: Jerome Forissier <address@hidden>
---
 grub-core/net/bootp.c              | 170 ++++++++++++++++++++++++++++++++++++-
 grub-core/net/drivers/efi/efinet.c |  23 ++++-
 include/grub/net.h                 |  10 +++
 3 files changed, 200 insertions(+), 3 deletions(-)

diff --git a/grub-core/net/bootp.c b/grub-core/net/bootp.c
index 4fdeac3..52f4051 100644
--- a/grub-core/net/bootp.c
+++ b/grub-core/net/bootp.c
@@ -186,7 +186,7 @@ grub_net_configure_by_dhcp_ack (const char *name,
     }
 #endif
 
-  if (size > OFFSET_OF (boot_file, bp))
+  if (size > OFFSET_OF (boot_file, bp) && bp->boot_file[0])
     grub_env_set_net_property (name, "boot_file", bp->boot_file,
                                sizeof (bp->boot_file));
   if (is_def)
@@ -233,7 +233,7 @@ grub_net_configure_by_dhcp_ack (const char *name,
        }
     }
 
-  if (size > OFFSET_OF (boot_file, bp) && path)
+  if (size > OFFSET_OF (boot_file, bp) && bp->boot_file[0] && path)
     {
       *path = grub_strndup (bp->boot_file, sizeof (bp->boot_file));
       grub_print_error ();
@@ -263,6 +263,172 @@ grub_net_configure_by_dhcp_ack (const char *name,
   return inter;
 }
 
+struct dhcp4_packet_option {
+       grub_uint8_t code;
+       grub_uint8_t length;
+       grub_uint8_t data[0];
+};
+
+/*
+ * Get specified option from DHCP extension data
+ *
+ * from PxeBcDhcp.c of UEFI
+ *
+ */
+static struct dhcp4_packet_option *
+dhcp_proxy_extension_option (const grub_uint8_t *buf,
+               grub_size_t size,
+               grub_uint8_t code)
+{
+       struct dhcp4_packet_option *option = (struct dhcp4_packet_option *)buf;
+       grub_size_t offset = 0;
+
+       while (offset < size && option->code != GRUB_NET_BOOTP_END) {
+               if (option->code == code)
+                       return option;
+
+               if (option->code == GRUB_NET_BOOTP_PAD)
+                       offset++;
+               else
+                       offset += option->length + 2;
+
+               option = (struct dhcp4_packet_option *)(buf + offset);
+       }
+
+       return NULL;
+}
+
+#define PXE_CLASS_ID "PXEClient"
+
+static int
+proxy_offer_is_valid(const struct grub_net_bootp_packet *bp,
+               grub_size_t size)
+{
+       const grub_uint8_t *buf;
+       grub_uint32_t option_size;
+       struct dhcp4_packet_option *option;
+
+       if (size <= OFFSET_OF (vendor, bp) + sizeof (grub_uint32_t))
+               return 0;
+
+       if (bp->vendor[0] != GRUB_NET_BOOTP_RFC1048_MAGIC_0
+                       || bp->vendor[1] != GRUB_NET_BOOTP_RFC1048_MAGIC_1
+                       || bp->vendor[2] != GRUB_NET_BOOTP_RFC1048_MAGIC_2
+                       || bp->vendor[3] != GRUB_NET_BOOTP_RFC1048_MAGIC_3)
+               return 0;
+
+       buf = bp->vendor + sizeof (grub_uint32_t);
+       option_size = size - OFFSET_OF(vendor, bp) - sizeof (grub_uint32_t);
+       option = dhcp_proxy_extension_option(buf, option_size, 
GRUB_NET_DHCP_CLASS_ID);
+       if (option == NULL)
+               return 0;
+
+       if (option->length < grub_strlen(PXE_CLASS_ID))
+               return 0;
+
+       if (grub_strncmp(option->data, PXE_CLASS_ID, grub_strlen(PXE_CLASS_ID)))
+               return 0;
+
+       return 1;
+}
+
+void
+grub_net_configure_by_proxy_offer (const struct grub_net_bootp_packet *bp,
+                               grub_size_t size,
+                               char **device,
+                               char **path)
+{
+       const grub_uint8_t *buf = bp->vendor + sizeof (grub_uint32_t);
+       grub_uint32_t option_size =
+               size - OFFSET_OF(vendor, bp) - sizeof (grub_uint32_t);
+       struct dhcp4_packet_option *option;
+
+       if (device == NULL)
+               return;
+
+       if (!proxy_offer_is_valid(bp, size))
+               return;
+
+       if (!*device && bp->server_ip)
+       {
+               *device = grub_xasprintf ("tftp,%d.%d.%d.%d",
+                               ((grub_uint8_t *) &bp->server_ip)[0],
+                               ((grub_uint8_t *) &bp->server_ip)[1],
+                               ((grub_uint8_t *) &bp->server_ip)[2],
+                               ((grub_uint8_t *) &bp->server_ip)[3]);
+               grub_print_error ();
+       }
+
+       option = dhcp_proxy_extension_option(buf,
+                       option_size, GRUB_NET_DHCP_OVERLOAD);
+
+       if ((option == NULL || option->data[0] == 1) && !*device && 
bp->server_name[0])
+       {
+               *device = grub_xasprintf ("tftp,%s", bp->server_name);
+               grub_print_error ();
+       }
+
+       if (!*device)
+       {
+               option = dhcp_proxy_extension_option(buf,
+                               option_size, GRUB_NET_DHCP_SERVER_ID);
+
+               if (option) {
+                       *device = grub_xasprintf("tftp,%d.%d.%d.%d",
+                                       option->data[0],
+                                       option->data[1],
+                                       option->data[2],
+                                       option->data[3]);
+                       grub_print_error ();
+               }
+       }
+
+       if (*device && grub_net_default_server == NULL)
+               grub_net_default_server = grub_strdup((*device) + 5);
+
+       if (path && !*path) {
+               option = dhcp_proxy_extension_option(buf,
+                               option_size, GRUB_NET_DHCP_OVERLOAD);
+
+               if (option == NULL || option->data[0] == 2)
+               {
+                       *path = grub_strndup (bp->boot_file, sizeof 
(bp->boot_file));
+                       grub_print_error ();
+
+                       if (*path)
+                       {
+                               char *slash;
+
+                               slash = grub_strrchr (*path, '/');
+                               if (slash)
+                                       *slash = 0;
+                               else
+                                       **path = 0;
+                       }
+               }
+               else
+               {
+                       option = dhcp_proxy_extension_option(buf,
+                                       option_size, GRUB_NET_DHCP_BOOTFILE);
+                       if (option) {
+                               *path = grub_strndup (option->data, 
option->length);
+                               grub_print_error ();
+
+                               if (*path)
+                               {
+                                       char *slash;
+
+                                       slash = grub_strrchr (*path, '/');
+                                       if (slash)
+                                               *slash = 0;
+                                       else
+                                               **path = 0;
+                               }
+                       }
+               }
+       }
+}
+
 void
 grub_net_process_dhcp (struct grub_net_buff *nb,
                       struct grub_net_card *card)
diff --git a/grub-core/net/drivers/efi/efinet.c 
b/grub-core/net/drivers/efi/efinet.c
index 5388f95..ef0ccd9 100644
--- a/grub-core/net/drivers/efi/efinet.c
+++ b/grub-core/net/drivers/efi/efinet.c
@@ -338,6 +338,7 @@ grub_efi_net_config_real (grub_efi_handle_t hnd, char 
**device,
   FOR_NET_CARDS (card)
   {
     grub_efi_device_path_t *cdp;
+       struct grub_net_network_level_interface *inter;
     struct grub_efi_pxe *pxe;
     struct grub_efi_pxe_mode *pxe_mode;
     if (card->driver != &efidriver)
@@ -378,11 +379,31 @@ 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,
+    inter = grub_net_configure_by_dhcp_ack (card->name, card, 0,
                                    (struct grub_net_bootp_packet *)
                                    &pxe_mode->dhcp_ack,
                                    sizeof (pxe_mode->dhcp_ack),
                                    1, device, path);
+
+       /*
+        * Bootstrap server ip address and file name maybe
+        * come from a separate proxy DHCP server,
+        * so check the proxy_offer DHCP packet
+        *
+        */
+       if (inter && *path == NULL) {
+               if (*device) {
+                       grub_free(*device);
+                       *device = NULL;
+               }
+
+               grub_net_configure_by_proxy_offer(
+                               (struct grub_net_bootp_packet 
*)&pxe_mode->proxy_offer,
+                               sizeof (pxe_mode->proxy_offer),
+                               device,
+                               path);
+       }
+
     return;
   }
 }
diff --git a/include/grub/net.h b/include/grub/net.h
index b62643a..f107a23 100644
--- a/include/grub/net.h
+++ b/include/grub/net.h
@@ -433,6 +433,10 @@ enum
     GRUB_NET_BOOTP_DOMAIN = 0x0f,
     GRUB_NET_BOOTP_ROOT_PATH = 0x11,
     GRUB_NET_BOOTP_EXTENSIONS_PATH = 0x12,
+       GRUB_NET_DHCP_OVERLOAD = 0x34,
+       GRUB_NET_DHCP_SERVER_ID = 0x36,
+       GRUB_NET_DHCP_CLASS_ID = 0x3c,
+       GRUB_NET_DHCP_BOOTFILE = 0x43,
     GRUB_NET_BOOTP_END = 0xff
   };
 
@@ -444,6 +448,12 @@ grub_net_configure_by_dhcp_ack (const char *name,
                                grub_size_t size,
                                int is_def, char **device, char **path);
 
+void
+grub_net_configure_by_proxy_offer (const struct grub_net_bootp_packet *bp,
+                               grub_size_t size,
+                               char **device,
+                               char **path);
+
 grub_err_t
 grub_net_add_ipv4_local (struct grub_net_network_level_interface *inf,
                         int mask);
-- 
1.9.1




reply via email to

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