net: dhcp6: pxe: Add DHCP/PXE commands for IPv6

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>
diff --git a/cmd/pxe.c b/cmd/pxe.c
index db8e469..6771425 100644
--- 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 @@
 {
 	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 @@
 }
 
 /*
+ * 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 @@
 	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 @@
 		i++;
 	}
 
+error_exit:
 	pxe_destroy_ctx(&ctx);
 
 	return -ENOENT;
@@ -169,9 +205,18 @@
 	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 @@
 	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 @@
 	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 @@
 	}
 
 	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 @@
 }
 
 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 @@
 	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 */