net: dhcp6: pxe: Add DHCP/PXE commands for IPv6
authorSean Edmond <seanedmond@microsoft.com>
Tue, 11 Apr 2023 17:48:47 +0000 (10:48 -0700)
committerTom Rini <trini@konsulko.com>
Fri, 5 May 2023 21:48:44 +0000 (17:48 -0400)
Adds commands to support DHCP and PXE with IPv6.

New configs added:
- CMD_DHCP6
- DHCP6_PXE_CLIENTARCH
- DHCP6_PXE_DHCP_OPTION
- DHCP6_ENTERPRISE_ID

New commands added (when IPv6 is enabled):
- dhcp6
- pxe get -ipv6
- pxe boot -ipv6

Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>
boot/bootmeth_distro.c
boot/bootmeth_pxe.c
boot/pxe_utils.c
cmd/Kconfig
cmd/net.c
cmd/pxe.c
cmd/sysboot.c
include/pxe_utils.h

index 3569298..b4b73ec 100644 (file)
@@ -150,7 +150,7 @@ static int distro_boot(struct udevice *dev, struct bootflow *bflow)
        info.dev = dev;
        info.bflow = bflow;
        ret = pxe_setup_ctx(&ctx, &cmdtp, distro_getfile, &info, true,
-                           bflow->subdir);
+                           bflow->subdir, false);
        if (ret)
                return log_msg_ret("ctx", -EINVAL);
 
index ecf8557..5a8af2b 100644 (file)
@@ -70,7 +70,7 @@ static int distro_pxe_read_bootflow(struct udevice *dev, struct bootflow *bflow)
        addr = simple_strtoul(addr_str, NULL, 16);
 
        log_debug("calling pxe_get()\n");
-       ret = pxe_get(addr, &bootdir, &size);
+       ret = pxe_get(addr, &bootdir, &size, false);
        log_debug("pxe_get() returned %d\n", ret);
        if (ret)
                return log_msg_ret("pxeb", ret);
@@ -146,7 +146,7 @@ static int distro_pxe_boot(struct udevice *dev, struct bootflow *bflow)
        info.bflow = bflow;
        info.cmdtp = &cmdtp;
        ret = pxe_setup_ctx(ctx, &cmdtp, distro_pxe_getfile, &info, false,
-                           bflow->subdir);
+                           bflow->subdir, false);
        if (ret)
                return log_msg_ret("ctx", -EINVAL);
 
index 3a1e50f..d13c47d 100644 (file)
@@ -1578,7 +1578,7 @@ void handle_pxe_menu(struct pxe_context *ctx, struct pxe_menu *cfg)
 
 int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp,
                  pxe_getfile_func getfile, void *userdata,
-                 bool allow_abs_path, const char *bootfile)
+                 bool allow_abs_path, const char *bootfile, bool use_ipv6)
 {
        const char *last_slash;
        size_t path_len = 0;
@@ -1588,6 +1588,7 @@ int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp,
        ctx->getfile = getfile;
        ctx->userdata = userdata;
        ctx->allow_abs_path = allow_abs_path;
+       ctx->use_ipv6 = use_ipv6;
 
        /* figure out the boot directory, if there is one */
        if (bootfile && strlen(bootfile) >= MAX_TFTP_PATH_LEN)
index e45b884..460f298 100644 (file)
@@ -1673,6 +1673,15 @@ config CMD_DHCP
        help
          Boot image via network using DHCP/TFTP protocol
 
+config CMD_DHCP6
+       bool "dhcp6"
+       depends on IPV6
+       help
+         Boot image via network using DHCPv6/TFTP protocol using IPv6.
+
+         Will perform 4-message exchange with DHCPv6 server, requesting
+         the minimum required options to TFTP boot. Complies with RFC 8415.
+
 config BOOTP_MAY_FAIL
        bool "Allow for the BOOTP/DHCP server to not be found"
        depends on CMD_BOOTP
@@ -1786,6 +1795,23 @@ config BOOTP_VCI_STRING
        default "U-Boot.arm" if ARM
        default "U-Boot"
 
+if CMD_DHCP6
+
+config DHCP6_PXE_CLIENTARCH
+       hex
+       default 0x16 if ARM64
+       default 0x15 if ARM
+       default 0xFF
+
+config DHCP6_PXE_DHCP_OPTION
+       bool "Request & store 'pxe_configfile' from DHCP6 server"
+
+config DHCP6_ENTERPRISE_ID
+       int "Enterprise ID to send in DHCPv6 Vendor Class Option"
+       default 0
+
+endif
+
 config CMD_TFTPBOOT
        bool "tftpboot"
        default y
index 036b772..68d4062 100644 (file)
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -111,6 +111,29 @@ U_BOOT_CMD(
 );
 #endif
 
+#if defined(CONFIG_CMD_DHCP6)
+static int do_dhcp6(struct cmd_tbl *cmdtp, int flag, int argc,
+                   char *const argv[])
+{
+       int i;
+       int dhcp_argc;
+       char *dhcp_argv[] = {NULL, NULL, NULL, NULL};
+
+       /* Add -ipv6 flag for autoload */
+       for (i = 0; i < argc; i++)
+               dhcp_argv[i] = argv[i];
+       dhcp_argc = argc + 1;
+       dhcp_argv[dhcp_argc - 1] =  USE_IP6_CMD_PARAM;
+
+       return netboot_common(DHCP6, cmdtp, dhcp_argc, dhcp_argv);
+}
+
+U_BOOT_CMD(dhcp6,      3,      1,      do_dhcp6,
+          "boot image via network using DHCPv6/TFTP protocol.\n"
+          "Use IPv6 hostIPaddr framed with [] brackets",
+          "[loadAddress] [[hostIPaddr:]bootfilename]");
+#endif
+
 #if defined(CONFIG_CMD_DHCP)
 static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc,
                   char *const argv[])
index db8e469..6771425 100644 (file)
--- a/cmd/pxe.c
+++ b/cmd/pxe.c
@@ -8,6 +8,8 @@
 #include <command.h>
 #include <fs.h>
 #include <net.h>
+#include <net6.h>
+#include <malloc.h>
 
 #include "pxe_utils.h"
 
@@ -29,12 +31,20 @@ static int do_get_tftp(struct pxe_context *ctx, const char *file_path,
 {
        char *tftp_argv[] = {"tftp", NULL, NULL, NULL};
        int ret;
+       int num_args;
 
        tftp_argv[1] = file_addr;
        tftp_argv[2] = (void *)file_path;
+       if (ctx->use_ipv6) {
+               tftp_argv[3] = USE_IP6_CMD_PARAM;
+               num_args = 4;
+       } else {
+               num_args = 3;
+       }
 
-       if (do_tftpb(ctx->cmdtp, 0, 3, tftp_argv))
+       if (do_tftpb(ctx->cmdtp, 0, num_args, tftp_argv))
                return -ENOENT;
+
        ret = pxe_get_file_size(sizep);
        if (ret)
                return log_msg_ret("tftp", ret);
@@ -44,6 +54,22 @@ static int do_get_tftp(struct pxe_context *ctx, const char *file_path,
 }
 
 /*
+ * Looks for a pxe file with specified config file name,
+ * which is received from DHCPv4 option 209 or
+ * DHCPv6 option 60.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+static int pxe_dhcp_option_path(struct pxe_context *ctx, unsigned long pxefile_addr_r)
+{
+       int ret = get_pxe_file(ctx, pxelinux_configfile, pxefile_addr_r);
+
+       free(pxelinux_configfile);
+
+       return ret;
+}
+
+/*
  * Looks for a pxe file with a name based on the pxeuuid environment variable.
  *
  * Returns 1 on success or < 0 on error.
@@ -105,15 +131,24 @@ static int pxe_ipaddr_paths(struct pxe_context *ctx, unsigned long pxefile_addr_
        return -ENOENT;
 }
 
-int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep)
+int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6)
 {
        struct cmd_tbl cmdtp[] = {};    /* dummy */
        struct pxe_context ctx;
        int i;
 
        if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
-                         env_get("bootfile")))
+                         env_get("bootfile"), use_ipv6))
                return -ENOMEM;
+
+       if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION) &&
+           pxelinux_configfile && use_ipv6) {
+               if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
+                       goto done;
+
+               goto error_exit;
+       }
+
        /*
         * Keep trying paths until we successfully get a file we're looking
         * for.
@@ -131,6 +166,7 @@ int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep)
                i++;
        }
 
+error_exit:
        pxe_destroy_ctx(&ctx);
 
        return -ENOENT;
@@ -169,9 +205,18 @@ do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
        char *fname;
        ulong size;
        int ret;
+       bool use_ipv6 = false;
 
-       if (argc != 1)
-               return CMD_RET_USAGE;
+       if (IS_ENABLED(CONFIG_IPV6)) {
+               if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
+                       use_ipv6 = true;
+
+               if (!(argc == 1 || (argc == 2 && use_ipv6)))
+                       return CMD_RET_USAGE;
+       } else {
+               if (argc != 1)
+                       return CMD_RET_USAGE;
+       }
 
        pxefile_addr_str = from_env("pxefile_addr_r");
 
@@ -183,7 +228,7 @@ do_pxe_get(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
        if (ret < 0)
                return 1;
 
-       ret = pxe_get(pxefile_addr_r, &fname, &size);
+       ret = pxe_get(pxefile_addr_r, &fname, &size, use_ipv6);
        switch (ret) {
        case 0:
                printf("Config file '%s' found\n", fname);
@@ -211,13 +256,19 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
        char *pxefile_addr_str;
        struct pxe_context ctx;
        int ret;
+       bool use_ipv6 = false;
+
+       if (IS_ENABLED(CONFIG_IPV6)) {
+               if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
+                       use_ipv6 = true;
+       }
 
-       if (argc == 1) {
+       if (argc == 1 || (argc == 2 && use_ipv6)) {
                pxefile_addr_str = from_env("pxefile_addr_r");
                if (!pxefile_addr_str)
                        return 1;
 
-       } else if (argc == 2) {
+       } else if (argc == 2 || (argc == 3 && use_ipv6)) {
                pxefile_addr_str = argv[1];
        } else {
                return CMD_RET_USAGE;
@@ -229,7 +280,7 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
        }
 
        if (pxe_setup_ctx(&ctx, cmdtp, do_get_tftp, NULL, false,
-                         env_get("bootfile"))) {
+                         env_get("bootfile"), use_ipv6)) {
                printf("Out of memory\n");
                return CMD_RET_FAILURE;
        }
@@ -244,8 +295,8 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 }
 
 static struct cmd_tbl cmd_pxe_sub[] = {
-       U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
-       U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
+       U_BOOT_CMD_MKENT(get, 2, 1, do_pxe_get, "", ""),
+       U_BOOT_CMD_MKENT(boot, 3, 1, do_pxe_boot, "", "")
 };
 
 static void __maybe_unused pxe_reloc(void)
@@ -281,9 +332,11 @@ static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
        return CMD_RET_USAGE;
 }
 
-U_BOOT_CMD(pxe, 3, 1, do_pxe,
-          "commands to get and boot from pxe files",
-          "get - try to retrieve a pxe file using tftp\n"
-          "pxe boot [pxefile_addr_r] - boot from the pxe file at pxefile_addr_r\n"
+U_BOOT_CMD(pxe, 4, 1, do_pxe,
+          "commands to get and boot from pxe files\n"
+          "To use IPv6 add -ipv6 parameter",
+          "get [" USE_IP6_CMD_PARAM "] - try to retrieve a pxe file using tftp\n"
+          "pxe boot [pxefile_addr_r] [-ipv6] - boot from the pxe file at pxefile_addr_r\n"
 );
-#endif
+
+#endif /* CONFIG_CMD_NET */
index 04c0702..63a7806 100644 (file)
@@ -101,7 +101,7 @@ static int do_sysboot(struct cmd_tbl *cmdtp, int flag, int argc,
        }
 
        if (pxe_setup_ctx(&ctx, cmdtp, sysboot_read_file, &info, true,
-                         filename)) {
+                         filename, false)) {
                printf("Out of memory\n");
                return CMD_RET_FAILURE;
        }
index 1e5e842..9f19593 100644 (file)
@@ -93,6 +93,7 @@ typedef int (*pxe_getfile_func)(struct pxe_context *ctx, const char *file_path,
  * @bootdir: Directory that files are loaded from ("" if no directory). This is
  *     allocated
  * @pxe_file_size: Size of the PXE file
+ * @use_ipv6: TRUE : use IPv6 addressing, FALSE : use IPv4 addressing
  */
 struct pxe_context {
        struct cmd_tbl *cmdtp;
@@ -112,6 +113,7 @@ struct pxe_context {
        bool allow_abs_path;
        char *bootdir;
        ulong pxe_file_size;
+       bool use_ipv6;
 };
 
 /**
@@ -209,12 +211,14 @@ int format_mac_pxe(char *outbuf, size_t outbuf_len);
  * @allow_abs_path: true to allow absolute paths
  * @bootfile: Bootfile whose directory loaded files are relative to, NULL if
  *     none
+ * @use_ipv6: TRUE : use IPv6 addressing
+ *            FALSE : use IPv4 addressing
  * Return: 0 if OK, -ENOMEM if out of memory, -E2BIG if bootfile is larger than
  *     MAX_TFTP_PATH_LEN bytes
  */
 int pxe_setup_ctx(struct pxe_context *ctx, struct cmd_tbl *cmdtp,
                  pxe_getfile_func getfile, void *userdata,
-                 bool allow_abs_path, const char *bootfile);
+                 bool allow_abs_path, const char *bootfile, bool use_ipv6);
 
 /**
  * pxe_destroy_ctx() - Destroy a PXE context
@@ -251,7 +255,9 @@ int pxe_get_file_size(ulong *sizep);
  *     "rpi/info", which indicates that all files should be fetched from the
  *     "rpi/" subdirectory
  * @sizep: Size of the PXE file (not bootfile)
+ * @use_ipv6: TRUE : use IPv6 addressing
+ *            FALSE : use IPv4 addressing
  */
-int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep);
+int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep, bool use_ipv6);
 
 #endif /* __PXE_UTILS_H */