From cb2f15c544895e1f3d540dd39d36c4611bdf5b7b Mon Sep 17 00:00:00 2001 From: Paulo Flabiano Smorigo Date: Wed, 22 Jan 2020 12:01:55 +0100 Subject: [PATCH] normal/main: Search for specific config files for netboot 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 Signed-off-by: Javier Martinez Canillas Reviewed-by: Daniel Kiper --- grub-core/net/net.c | 131 ++++++++++++++++++++++++++++++++++++++++ grub-core/normal/main.c | 26 ++++++-- include/grub/net.h | 2 + 3 files changed, 155 insertions(+), 4 deletions(-) diff --git a/grub-core/net/net.c b/grub-core/net/net.c index d5d726a31..f82ac15b4 100644 --- a/grub-core/net/net.c +++ b/grub-core/net/net.c @@ -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; diff --git a/grub-core/normal/main.c b/grub-core/normal/main.c index 1b03dfd57..c4ebe9e22 100644 --- a/grub-core/normal/main.c +++ b/grub-core/normal/main.c @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -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); diff --git a/include/grub/net.h b/include/grub/net.h index c00b0ab11..7ae4b6bd8 100644 --- a/include/grub/net.h +++ b/include/grub/net.h @@ -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;