]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
normal/main: Search for specific config files for netboot
authorPaulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
Wed, 22 Jan 2020 11:01:55 +0000 (12:01 +0100)
committerDaniel Kiper <daniel.kiper@oracle.com>
Tue, 18 Feb 2020 14:12:06 +0000 (15:12 +0100)
This patch implements a search for a specific configuration when the config
file is on a remoteserver. It uses the following order:
   1) DHCP client UUID option.
   2) MAC address (in lower case hexadecimal with dash separators);
   3) IP (in upper case hexadecimal) or IPv6;
   4) The original grub.cfg file.

This procedure is similar to what is used by pxelinux and yaboot:
http://www.syslinux.org/wiki/index.php/PXELINUX#config

It is enabled by default but can be disabled by setting the environment
variable "feature_net_search_cfg" to "n" in an embedded configuration.

Fixes: https://bugzilla.redhat.com/show_bug.cgi?id=873406
Signed-off-by: Paulo Flabiano Smorigo <pfsmorigo@br.ibm.com>
Signed-off-by: Javier Martinez Canillas <javierm@redhat.com>
Reviewed-by: Daniel Kiper <daniel.kiper@oracle.com>
grub-core/net/net.c
grub-core/normal/main.c
include/grub/net.h

index d5d726a315ebec85c191e4fa8badfa6f073933c9..f82ac15b4ecbcf78bb57ae02cd2e10c0f282ad1c 100644 (file)
@@ -1735,6 +1735,137 @@ grub_net_restore_hw (void)
   return GRUB_ERR_NONE;
 }
 
+grub_err_t
+grub_net_search_config_file (char *config)
+{
+  grub_size_t config_len;
+  char *suffix;
+
+  auto int search_through (grub_size_t num_tries, grub_size_t slice_size);
+  int search_through (grub_size_t num_tries, grub_size_t slice_size)
+  {
+    while (num_tries-- > 0)
+      {
+        grub_file_t file;
+
+        grub_dprintf ("net", "attempt to fetch config %s\n", config);
+
+        file = grub_file_open (config, GRUB_FILE_TYPE_CONFIG);
+
+        if (file)
+          {
+            grub_file_close (file);
+            return 0;
+          }
+        else
+          {
+            if (grub_errno == GRUB_ERR_IO)
+              grub_errno = GRUB_ERR_NONE;
+          }
+
+        if (grub_strlen (suffix) < slice_size)
+          break;
+
+        config[grub_strlen (config) - slice_size] = '\0';
+      }
+
+    return 1;
+  }
+
+  config_len = grub_strlen (config);
+  config[config_len] = '-';
+  suffix = config + config_len + 1;
+
+  struct grub_net_network_level_interface *inf;
+  FOR_NET_NETWORK_LEVEL_INTERFACES (inf)
+    {
+      /* By the Client UUID. */
+      char *ptr;
+      int client_uuid_len;
+      char *client_uuid_var;
+      const char *client_uuid;
+
+      client_uuid_len = sizeof ("net_") + grub_strlen (inf->name) +
+                        sizeof ("_clientuuid") + 1;
+
+      client_uuid_var = grub_zalloc (client_uuid_len);
+      if (!client_uuid_var)
+        return grub_errno;
+
+      grub_snprintf (client_uuid_var, client_uuid_len,
+                     "net_%s_clientuuid", inf->name);
+
+      client_uuid = grub_env_get (client_uuid_var);
+      grub_free (client_uuid_var);
+
+      if (client_uuid)
+        {
+          grub_strcpy (suffix, client_uuid);
+          if (search_through (1, 0) == 0)
+            return GRUB_ERR_NONE;
+        }
+
+      /* By the MAC address. */
+
+      /* Add ethernet type */
+      grub_strcpy (suffix, "01-");
+
+      grub_net_hwaddr_to_str (&inf->hwaddress, suffix + 3);
+
+      for (ptr = suffix; *ptr; ptr++)
+        if (*ptr == ':')
+          *ptr = '-';
+
+      if (search_through (1, 0) == 0)
+        return GRUB_ERR_NONE;
+
+      /* By IP address */
+
+      switch ((&inf->address)->type)
+        {
+        case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV4:
+          {
+            grub_uint32_t n = grub_be_to_cpu32 ((&inf->address)->ipv4);
+
+            grub_snprintf (suffix, GRUB_NET_MAX_STR_ADDR_LEN, "%02X%02X%02X%02X", \
+                           ((n >> 24) & 0xff), ((n >> 16) & 0xff),      \
+                           ((n >> 8) & 0xff), ((n >> 0) & 0xff));
+
+            if (search_through (8, 1) == 0)
+              return GRUB_ERR_NONE;
+            break;
+          }
+        case GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6:
+          {
+            char buf[GRUB_NET_MAX_STR_ADDR_LEN];
+            struct grub_net_network_level_address base;
+            base.type = GRUB_NET_NETWORK_LEVEL_PROTOCOL_IPV6;
+            grub_memcpy (&base.ipv6, ((&inf->address)->ipv6), 16);
+            grub_net_addr_to_str (&base, buf);
+
+            for (ptr = buf; *ptr; ptr++)
+              if (*ptr == ':')
+                *ptr = '-';
+
+            grub_snprintf (suffix, GRUB_NET_MAX_STR_ADDR_LEN, "%s", buf);
+            if (search_through (1, 0) == 0)
+              return GRUB_ERR_NONE;
+            break;
+          }
+        case GRUB_NET_NETWORK_LEVEL_PROTOCOL_DHCP_RECV:
+          return grub_error (GRUB_ERR_BUG, "shouldn't reach here");
+        default:
+          return grub_error (GRUB_ERR_BUG,
+                             "unsupported address type %d", (&inf->address)->type);
+        }
+    }
+
+  /* Remove the remaining minus sign at the end. */
+  config[config_len] = '\0';
+
+  return GRUB_ERR_NONE;
+}
+
 static struct grub_preboot *fini_hnd;
 
 static grub_command_t cmd_addaddr, cmd_deladdr, cmd_addroute, cmd_delroute;
index 1b03dfd57b9113ba0e6fee3475d25d55534a15e5..c4ebe9e22adae3121adf81aa2c18a2444f911cb1 100644 (file)
@@ -18,6 +18,7 @@
  */
 
 #include <grub/kernel.h>
+#include <grub/net.h>
 #include <grub/normal.h>
 #include <grub/dl.h>
 #include <grub/misc.h>
@@ -323,10 +324,27 @@ grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
 
       prefix = grub_env_get ("prefix");
       if (prefix)
-       {
-         config = grub_xasprintf ("%s/grub.cfg", prefix);
-         if (! config)
-           goto quit;
+        {
+          grub_size_t config_len;
+          int disable_net_search = 0;
+          const char *net_search_cfg;
+
+          config_len = grub_strlen (prefix) +
+                       sizeof ("/grub.cfg-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX");
+          config = grub_malloc (config_len);
+
+          if (!config)
+            goto quit;
+
+          grub_snprintf (config, config_len, "%s/grub.cfg", prefix);
+
+          net_search_cfg = grub_env_get ("feature_net_search_cfg");
+          if (net_search_cfg && net_search_cfg[0] == 'n')
+            disable_net_search = 1;
+
+          if (grub_strncmp (prefix + 1, "tftp", sizeof ("tftp") - 1) == 0 &&
+              !disable_net_search)
+            grub_net_search_config_file (config);
 
          grub_enter_normal_mode (config);
          grub_free (config);
index c00b0ab117ce7abedd91f4a5136c6ca0974a57fb..7ae4b6bd805cb0a0bd39da56e3e301fd8ec7e92f 100644 (file)
@@ -569,6 +569,8 @@ grub_net_add_dns_server (const struct grub_net_network_level_address *s);
 void
 grub_net_remove_dns_server (const struct grub_net_network_level_address *s);
 
+grub_err_t
+grub_net_search_config_file (char *config);
 
 extern char *grub_net_default_server;