All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/2] net: DHCPv6 protocol and commands
@ 2023-02-02  0:26 seanedmond
  2023-02-02  0:26 ` [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6) seanedmond
                   ` (2 more replies)
  0 siblings, 3 replies; 8+ messages in thread
From: seanedmond @ 2023-02-02  0:26 UTC (permalink / raw)
  To: u-boot; +Cc: joe.hershberger, rfried.dev, sjg, v.v.mitrofanov, seanedmond

From: Sean Edmond <seanedmond@microsoft.com>

The recently integrated IPv6 patch series relies on the link-local address,
or a statically assigned IPv6 address for network operations.  This patch
series adds IPv6 address assignment through DHCPv6.

The implementation meets the requirements in RFC 8415 for "Client/Server
Exchanges Involving Four Messages":
https://www.rfc-editor.org/rfc/rfc8415

The implementation sends/receives the minimum required DHCPv6 options to 
network boot.

A new command (dhcp6) will execute the protocol.  In addition, IPv6
functionality has been extended to the existing pxe commands ("pxe get"
and "pxe boot").

Sean Edmond (2):
  net: dhcp6: Add DHCPv6 (DHCP for IPv6)
  net: dhcp6: pxe: Add DHCP/PXE commands for IPv6

 boot/bootmeth_distro.c |   2 +-
 boot/bootmeth_pxe.c    |   4 +-
 boot/pxe_utils.c       |   3 +-
 cmd/Kconfig            |  26 ++
 cmd/net.c              |  22 ++
 cmd/pxe.c              |  86 ++++-
 cmd/sysboot.c          |   2 +-
 include/net.h          |  10 +-
 include/net6.h         |   2 -
 include/pxe_utils.h    |  10 +-
 net/Makefile           |   1 +
 net/dhcpv6.c           | 741 +++++++++++++++++++++++++++++++++++++++++
 net/dhcpv6.h           | 212 ++++++++++++
 net/net.c              |  12 +
 14 files changed, 1113 insertions(+), 20 deletions(-)
 create mode 100644 net/dhcpv6.c
 create mode 100644 net/dhcpv6.h

-- 
2.39.0


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6)
  2023-02-02  0:26 [PATCH 0/2] net: DHCPv6 protocol and commands seanedmond
@ 2023-02-02  0:26 ` seanedmond
  2023-02-02 17:12   ` Simon Glass
  2023-03-05 11:41   ` Vyacheslav V. Mitrofanov
  2023-02-02  0:26 ` [PATCH 2/2] net: dhcp6: pxe: Add DHCP/PXE commands for IPv6 seanedmond
  2023-03-05 11:39 ` [PATCH 0/2] net: DHCPv6 protocol and commands Vyacheslav V. Mitrofanov
  2 siblings, 2 replies; 8+ messages in thread
From: seanedmond @ 2023-02-02  0:26 UTC (permalink / raw)
  To: u-boot; +Cc: joe.hershberger, rfried.dev, sjg, v.v.mitrofanov, seanedmond

From: Sean Edmond <seanedmond@microsoft.com>

Adds DHCPv6 protocol to u-boot.

Allows for address assignement with DHCPv6 4-message exchange
(SOLICIT->ADVERTISE->REQUEST->REPLY).  Includes DHCPv6 options
required by RFC 8415.  Also adds DHCPv6 options required
for PXE boot.

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

Possible enhancements:
- Duplicate address detection on DHCPv6 assigned address
- IPv6 address assignement through SLAAC
- Sending/parsing other DHCPv6 options (NTP, DNS, etc...)

Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
---
 cmd/Kconfig   |  26 ++
 include/net.h |   8 +-
 net/Makefile  |   1 +
 net/dhcpv6.c  | 741 ++++++++++++++++++++++++++++++++++++++++++++++++++
 net/dhcpv6.h  | 212 +++++++++++++++
 net/net.c     |  12 +
 6 files changed, 998 insertions(+), 2 deletions(-)
 create mode 100644 net/dhcpv6.c
 create mode 100644 net/dhcpv6.h

diff --git a/cmd/Kconfig b/cmd/Kconfig
index dc0446e02e..87813ddbb4 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1645,6 +1645,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
@@ -1758,6 +1767,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
diff --git a/include/net.h b/include/net.h
index 399af5e064..cac818e292 100644
--- a/include/net.h
+++ b/include/net.h
@@ -484,6 +484,10 @@ extern char	net_hostname[32];	/* Our hostname */
 #ifdef CONFIG_NET
 extern char	net_root_path[CONFIG_BOOTP_MAX_ROOT_PATH_LEN];	/* Our root path */
 #endif
+#if defined(CONFIG_DHCP6_PXE_DHCP_OPTION)
+/* Indicates whether the pxe path prefix / config file was specified in dhcp option */
+extern char *pxelinux_configfile;
+#endif
 /** END OF BOOTP EXTENTIONS **/
 extern u8		net_ethaddr[ARP_HLEN];		/* Our ethernet address */
 extern u8		net_server_ethaddr[ARP_HLEN];	/* Boot server enet address */
@@ -504,8 +508,8 @@ extern ushort		net_native_vlan;	/* Our Native VLAN */
 extern int		net_restart_wrap;	/* Tried all network devices */
 
 enum proto_t {
-	BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP, NETCONS,
-	SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
+	BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP,
+	NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
 };
 
 extern char	net_boot_file_name[1024];/* Boot File name */
diff --git a/net/Makefile b/net/Makefile
index bea000b206..5968110170 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_IPV6)     += net6.o
 obj-$(CONFIG_CMD_NFS)  += nfs.o
 obj-$(CONFIG_CMD_PING) += ping.o
 obj-$(CONFIG_CMD_PING6) += ping6.o
+obj-$(CONFIG_CMD_DHCP6) += dhcpv6.o
 obj-$(CONFIG_CMD_PCAP) += pcap.o
 obj-$(CONFIG_CMD_RARP) += rarp.o
 obj-$(CONFIG_CMD_SNTP) += sntp.o
diff --git a/net/dhcpv6.c b/net/dhcpv6.c
new file mode 100644
index 0000000000..0f0fa291d3
--- /dev/null
+++ b/net/dhcpv6.c
@@ -0,0 +1,741 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) Microsoft Corporation
+ * Author: Sean Edmond <seanedmond@microsoft.com>
+ *
+ */
+
+/* Simple DHCP6 network layer implementation. */
+
+#include <common.h>
+#include <bootstage.h>
+#include <command.h>
+#include <env.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <net.h>
+#include <rand.h>
+#include <uuid.h>
+#include <linux/delay.h>
+#include <net/tftp.h>
+#include "dhcpv6.h"
+#include <net6.h>
+#include <malloc.h>
+#include "net_rand.h"
+
+/* Copied from bootp */
+#ifndef CONFIG_NET_RETRY_COUNT
+# define TIMEOUT_COUNT	5	/* # of timeouts before giving up */
+#else
+# define TIMEOUT_COUNT	(CONFIG_NET_RETRY_COUNT)
+#endif
+
+#define PORT_DHCP6_S	547	/* DHCP6 server UDP port */
+#define PORT_DHCP6_C	546	/* DHCP6 client UDP port */
+
+//default timeout parameters (in ms)
+#define SOL_MAX_DELAY_MS	1000
+#define SOL_TIMEOUT_MS		1000
+#define SOL_MAX_RT_MS		3600000
+#define REQ_TIMEOUT_MS		1000
+#define REQ_MAX_RT_MS		30000
+#define REQ_MAX_RC		10
+#define MAX_WAIT_TIME_MS	60000
+
+//global variable to track any updates from DHCP6 server
+int updated_sol_max_rt_ms = SOL_MAX_RT_MS;
+
+static void dhcp6_timeout_handler(void);
+static void dhcp6_state_machine(bool timeout, uchar *rx_pkt, unsigned int len);
+static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len);
+
+struct dhcp6_sm_params sm_params;
+
+/*
+ * Handle DHCP received packets (set as UDP handler)
+ */
+static void dhcp6_handler(uchar *pkt, unsigned int dest, struct in_addr sip,
+			  unsigned int src, unsigned int len)
+{
+	//return if ports don't match DHCPv6 ports
+	if (dest != PORT_DHCP6_C || src != PORT_DHCP6_S)
+		return;
+
+	dhcp6_state_machine(false, pkt, len);
+}
+
+/**
+ * dhcp6_add_option() - Adds DHCP6 option to a packet
+ * @option_id: The option ID to add (See DHCP6_OPTION_* definitions)
+ * @pkt: A pointer to the current write location of the TX packet
+ *
+ * Return: The number of bytes written into "*pkt"
+ */
+static int dhcp6_add_option(int option_id, uchar *pkt)
+{
+	struct dhcp6_option_duid_ll *duid_opt;
+	struct dhcp6_option_elapsed_time *elapsed_time_opt;
+	struct dhcp6_option_ia_ta *ia_ta_opt;
+	struct dhcp6_option_ia_na *ia_na_opt;
+	struct dhcp6_option_oro *oro_opt;
+	struct dhcp6_option_client_arch *client_arch_opt;
+	struct dhcp6_option_vendor_class *vendor_class_opt;
+	int opt_len;
+	long elapsed_time;
+	size_t vci_strlen;
+	int num_oro = 0;
+	int num_client_arch = 0;
+	int num_vc_data = 0;
+	struct dhcp6_option_hdr *dhcp_option = (struct dhcp6_option_hdr *)pkt;
+	uchar *dhcp_option_start = pkt + sizeof(struct dhcp6_option_hdr);
+
+	dhcp_option->option_id = htons(option_id);
+
+	switch (option_id) {
+	case DHCP6_OPTION_CLIENTID:
+		/* Only support for DUID-LL in Client ID option for now */
+		duid_opt = (struct dhcp6_option_duid_ll *)dhcp_option_start;
+		duid_opt->duid_type = htons(DUID_TYPE_LL);
+		duid_opt->hw_type = htons(DUID_HW_TYPE_ENET);
+		memcpy(duid_opt->ll_addr, net_ethaddr, ETH_ALEN);
+		opt_len = sizeof(struct dhcp6_option_duid_ll) + ETH_ALEN;
+
+		/* Save DUID for comparison later */
+		memcpy(sm_params.duid, duid_opt, opt_len);
+		break;
+	case DHCP6_OPTION_ELAPSED_TIME:
+		// calculate elapsed time in 1/100th of a second
+		elapsed_time = (sm_params.dhcp6_retry_ms -
+			sm_params.dhcp6_start_ms) / 10;
+		if (elapsed_time > 0xFFFF)
+			elapsed_time = 0xFFFF;
+
+		elapsed_time_opt = (struct dhcp6_option_elapsed_time *)dhcp_option_start;
+		elapsed_time_opt->elapsed_time = htons(elapsed_time);
+
+		opt_len = sizeof(struct dhcp6_option_elapsed_time);
+		break;
+	case DHCP6_OPTION_IA_TA:
+		ia_ta_opt = (struct dhcp6_option_ia_ta *)dhcp_option_start;
+		ia_ta_opt->iaid = htonl(sm_params.ia_id);
+
+		opt_len = sizeof(struct dhcp6_option_ia_ta);
+		break;
+	case DHCP6_OPTION_IA_NA:
+		ia_na_opt = (struct dhcp6_option_ia_na *)dhcp_option_start;
+		ia_na_opt->iaid = htonl(sm_params.ia_id);
+		/* In a message sent by a client to a server,
+		 * the T1 and T2 fields SHOULD be set to 0
+		 */
+		ia_na_opt->t1 = 0;
+		ia_na_opt->t2 = 0;
+
+		opt_len = sizeof(struct dhcp6_option_ia_na);
+		break;
+	case DHCP6_OPTION_ORO:
+		oro_opt = (struct dhcp6_option_oro *)dhcp_option_start;
+		oro_opt->req_option_code[num_oro++] = htons(DHCP6_OPTION_OPT_BOOTFILE_URL);
+		oro_opt->req_option_code[num_oro++] = htons(DHCP6_OPTION_SOL_MAX_RT);
+		if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION)) {
+			oro_opt->req_option_code[num_oro++] =
+				htons(DHCP6_OPTION_OPT_BOOTFILE_PARAM);
+		}
+
+		opt_len = sizeof(__be16) * num_oro;
+		break;
+	case DHCP6_OPTION_CLIENT_ARCH_TYPE:
+		client_arch_opt = (struct dhcp6_option_client_arch *)dhcp_option_start;
+		client_arch_opt->arch_type[num_client_arch++] = htons(CONFIG_DHCP6_PXE_CLIENTARCH);
+
+		opt_len = sizeof(__be16) * num_client_arch;
+		break;
+	case DHCP6_OPTION_VENDOR_CLASS:
+		vendor_class_opt = (struct dhcp6_option_vendor_class *)dhcp_option_start;
+		vendor_class_opt->enterprise_number = htonl(CONFIG_DHCP6_ENTERPRISE_ID);
+
+		vci_strlen = strlen(DHCP6_VCI_STRING);
+		vendor_class_opt->vendor_class_data[num_vc_data].vendor_class_len =
+			htons(vci_strlen);
+		memcpy(vendor_class_opt->vendor_class_data[num_vc_data].opaque_data,
+		       DHCP6_VCI_STRING, vci_strlen);
+		num_vc_data++;
+
+		opt_len = sizeof(struct dhcp6_option_vendor_class) +
+			  sizeof(struct vendor_class_data) * num_vc_data +
+			  vci_strlen;
+		break;
+	case DHCP6_OPTION_NII:
+		dhcp_option_start[0] = 1;
+		dhcp_option_start[1] = 0;
+		dhcp_option_start[2] = 0;
+
+		opt_len = 3;
+		break;
+	default:
+		printf("***Warning unknown DHCP6 option %d.  Not adding to message\n", option_id);
+		return 0;
+	}
+	dhcp_option->option_len = htons(opt_len);
+
+	return opt_len + sizeof(struct dhcp6_option_hdr);
+}
+
+/**
+ * dhcp6_send_solicit_packet() - Send a SOLICIT packet
+ *
+ * Implements RFC 8415:
+ *    - 16.2. Solicit Message
+ *    - 18.2.1. Creation and Transmission of Solicit Messages
+ *
+ * Adds DHCP6 header and DHCP6 options.  Sends the UDP packet
+ * and sets the UDP handler.
+ */
+static void dhcp6_send_solicit_packet(void)
+{
+	struct in6_addr dhcp_bcast_ip6;
+	int len = 0;
+	uchar *pkt;
+	uchar *dhcp_pkt_start_ptr;
+	struct dhcp6_hdr *dhcp_hdr;
+
+	pkt = net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE + UDP_HDR_SIZE;
+	dhcp_pkt_start_ptr = pkt;
+
+	/* Add the DHCP6 header */
+	dhcp_hdr = (struct dhcp6_hdr *)pkt;
+	dhcp_hdr->msg_type = DHCP6_MSG_SOLICIT;
+	dhcp_hdr->trans_id = htons(sm_params.trans_id);
+	pkt += sizeof(struct dhcp6_hdr);
+
+	/* Add the options */
+	pkt += dhcp6_add_option(DHCP6_OPTION_CLIENTID, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_ELAPSED_TIME, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_IA_NA, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_ORO, pkt);
+	if (CONFIG_DHCP6_PXE_CLIENTARCH != 0xFF)
+		pkt += dhcp6_add_option(DHCP6_OPTION_CLIENT_ARCH_TYPE, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_VENDOR_CLASS, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_NII, pkt);
+
+	/* calculate packet length */
+	len = pkt - dhcp_pkt_start_ptr;
+
+	/* send UDP packet to DHCP6 multicast address */
+	string_to_ip6(DHCP6_MULTICAST_ADDR, sizeof(DHCP6_MULTICAST_ADDR), &dhcp_bcast_ip6);
+	net_set_udp_handler(dhcp6_handler);
+	net_send_udp_packet6((uchar *)net_bcast_ethaddr, &dhcp_bcast_ip6,
+			     PORT_DHCP6_S, PORT_DHCP6_C, len);
+}
+
+/**
+ * dhcp6_send_request_packet() - Send a REQUEST packet
+ *
+ *  * Implements RFC 8415:
+ *    - 16.4. Request Message
+ *    - 18.2.2. Creation and Transmission of Request Messages
+ *
+ * Adds DHCP6 header and DHCP6 options.  Sends the UDP packet
+ * and sets the UDP handler.
+ */
+static void dhcp6_send_request_packet(void)
+{
+	struct in6_addr dhcp_bcast_ip6;
+	int len = 0;
+	uchar *pkt;
+	uchar *dhcp_pkt_start_ptr;
+	struct dhcp6_hdr *dhcp_hdr;
+
+	pkt = net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE + UDP_HDR_SIZE;
+	dhcp_pkt_start_ptr = pkt;
+
+	/* Add the DHCP6 header */
+	dhcp_hdr = (struct dhcp6_hdr *)pkt;
+	dhcp_hdr->msg_type = DHCP6_MSG_REQUEST;
+	dhcp_hdr->trans_id = htons(sm_params.trans_id);
+	pkt += sizeof(struct dhcp6_hdr);
+
+	/* add the options */
+	pkt += dhcp6_add_option(DHCP6_OPTION_CLIENTID, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_ELAPSED_TIME, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_IA_NA, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_ORO, pkt);
+	/* copy received IA_TA/IA_NA into the REQUEST packet */
+	if (sm_params.server_uid.uid_ptr) {
+		memcpy(pkt, sm_params.server_uid.uid_ptr, sm_params.server_uid.uid_size);
+		pkt += sm_params.server_uid.uid_size;
+	}
+	if (CONFIG_DHCP6_PXE_CLIENTARCH != 0xFF)
+		pkt += dhcp6_add_option(DHCP6_OPTION_CLIENT_ARCH_TYPE, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_VENDOR_CLASS, pkt);
+	pkt += dhcp6_add_option(DHCP6_OPTION_NII, pkt);
+
+	/* calculate packet length */
+	len = pkt - dhcp_pkt_start_ptr;
+
+	/* send UDP packet to DHCP6 multicast address */
+	string_to_ip6(DHCP6_MULTICAST_ADDR, strlen(DHCP6_MULTICAST_ADDR), &dhcp_bcast_ip6);
+	net_set_udp_handler(dhcp6_handler);
+	net_send_udp_packet6((uchar *)net_bcast_ethaddr, &dhcp_bcast_ip6,
+			     PORT_DHCP6_S, PORT_DHCP6_C, len);
+}
+
+static void dhcp6_parse_ia_options(struct dhcp6_option_hdr *ia_ptr, uchar *ia_option_ptr)
+{
+	struct dhcp6_option_hdr *ia_option_hdr;
+
+	ia_option_hdr = (struct dhcp6_option_hdr *)ia_option_ptr;
+
+	/* Search for options encapsulated in IA_NA/IA_TA (DHCP6_OPTION_IAADDR
+	 * or DHCP6_OPTION_STATUS_CODE)
+	 */
+	while (ia_option_ptr < ((uchar *)ia_ptr + ntohs(ia_ptr->option_len))) {
+		switch (ntohs(ia_option_hdr->option_id)) {
+		case DHCP6_OPTION_IAADDR:
+			sm_params.rx_status.ia_addr_found = true;
+			net_copy_ip6(&sm_params.rx_status.ia_addr_ipv6,
+				     (ia_option_ptr + sizeof(struct dhcp6_hdr)));
+			debug("DHCP6_OPTION_IAADDR FOUND\n");
+			break;
+		case DHCP6_OPTION_STATUS_CODE:
+			sm_params.rx_status.ia_status_code =
+				ntohs(*((u16 *)(ia_option_ptr + sizeof(struct dhcp6_hdr))));
+			printf("ERROR : IA STATUS %d\n", sm_params.rx_status.ia_status_code);
+			break;
+		default:
+			debug("Unknown Option in IA, skipping\n");
+			break;
+		}
+
+		ia_option_ptr += ntohs(((struct dhcp6_option_hdr *)ia_option_ptr)->option_len);
+	}
+}
+
+/**
+ * dhcp6_parse_options() - Parse the DHCP6 options
+ *
+ * @rx_pkt: pointer to beginning of received DHCP6 packet
+ * @len: Total length of the DHCP6 packet
+ *
+ * Parses the DHCP options from a received DHCP packet. Perform error checking
+ * on the options received.  Any relevant status is available in:
+ * "sm_params.rx_status"
+ *
+ */
+static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len)
+{
+	uchar *option_ptr;
+	int sol_max_rt_sec, option_len;
+	char *s, *e;
+	struct dhcp6_option_hdr *option_hdr;
+
+	memset(&sm_params.rx_status, 0, sizeof(struct dhcp6_rx_pkt_status));
+
+	option_hdr = (struct dhcp6_option_hdr *)(rx_pkt + sizeof(struct dhcp6_hdr));
+	/* check that required options exist */
+	while (option_hdr < (struct dhcp6_option_hdr *)(rx_pkt + len)) {
+		option_ptr = ((uchar *)option_hdr) + sizeof(struct dhcp6_hdr);
+		option_len = ntohs(option_hdr->option_len);
+
+		switch (ntohs(option_hdr->option_id)) {
+		case DHCP6_OPTION_CLIENTID:
+			if (memcmp(option_ptr, sm_params.duid, option_len)
+			    != 0) {
+				debug("CLIENT ID DOESN'T MATCH\n");
+			} else {
+				debug("CLIENT ID FOUND and MATCHES\n");
+				sm_params.rx_status.client_id_match = true;
+			}
+			break;
+		case DHCP6_OPTION_SERVERID:
+			sm_params.rx_status.server_id_found = true;
+			sm_params.rx_status.server_uid_ptr = (uchar *)option_hdr;
+			sm_params.rx_status.server_uid_size = option_len +
+							      sizeof(struct dhcp6_option_hdr);
+			debug("SERVER ID FOUND\n");
+			break;
+		case DHCP6_OPTION_IA_TA:
+		case DHCP6_OPTION_IA_NA:
+			/* check the IA_ID */
+			if (*((u32 *)option_ptr) !=  htonl(sm_params.ia_id)) {
+				debug("IA_ID mismatch 0x%08x 0x%08x\n",
+				      *((u32 *)option_ptr), htonl(sm_params.ia_id));
+				break;
+			}
+
+			if (ntohs(option_hdr->option_id) == DHCP6_OPTION_IA_NA) {
+				/* skip past IA_ID/T1/T2 */
+				option_ptr += 3 * sizeof(u32);
+			} else if (ntohs(option_hdr->option_id) == DHCP6_OPTION_IA_TA) {
+				/* skip past IA_ID */
+				option_ptr += sizeof(u32);
+			}
+			/* parse the IA_NA/IA_TA encapsulated options */
+			dhcp6_parse_ia_options(option_hdr, option_ptr);
+			break;
+		case DHCP6_OPTION_STATUS_CODE:
+			debug("DHCP6_OPTION_STATUS_CODE FOUND\n");
+			sm_params.rx_status.status_code = ntohs(*((u16 *)option_ptr));
+			debug("DHCP6 top-level status code %d\n", sm_params.rx_status.status_code);
+			debug("DHCP6 status message: %.*s\n", len, option_ptr + 2);
+			break;
+		case DHCP6_OPTION_SOL_MAX_RT:
+			debug("DHCP6_OPTION_SOL_MAX_RT FOUND\n");
+			sol_max_rt_sec = ntohl(*((u32 *)option_ptr));
+
+			// A DHCP client MUST ignore any SOL_MAX_RT option values that are less
+			// than 60 or more than 86400
+			if (sol_max_rt_sec >= 60 && sol_max_rt_sec <= 86400) {
+				updated_sol_max_rt_ms = sol_max_rt_sec * 1000;
+				if (sm_params.curr_state == DHCP6_SOLICIT)
+					sm_params.mrt_ms = updated_sol_max_rt_ms;
+			}
+			break;
+		case DHCP6_OPTION_OPT_BOOTFILE_URL:
+			debug("DHCP6_OPTION_OPT_BOOTFILE_URL FOUND\n");
+			copy_filename(net_boot_file_name, option_ptr, option_len + 1);
+			debug("net_boot_file_name: %s\n", net_boot_file_name);
+
+			//copy server_ip6 (required for PXE)
+			s = strchr(net_boot_file_name, '[');
+			e = strchr(net_boot_file_name, ']');
+			if (s && e && e > s)
+				string_to_ip6(s + 1, e - s - 1, &net_server_ip6);
+			break;
+#if IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION)
+		case DHCP6_OPTION_OPT_BOOTFILE_PARAM:
+			debug("DHCP6_OPTION_OPT_BOOTFILE_PARAM FOUND\n");
+
+			if (pxelinux_configfile)
+				free(pxelinux_configfile);
+
+			pxelinux_configfile = (char *)malloc((option_len + 1) * sizeof(char));
+			if (pxelinux_configfile) {
+				memcpy(pxelinux_configfile, option_ptr, option_len);
+				pxelinux_configfile[option_len] = '\0';
+			}
+
+			debug("PXE CONFIG FILE %s\n", pxelinux_configfile);
+			break;
+#endif
+		case DHCP6_OPTION_PREFERENCE:
+			debug("DHCP6_OPTION_PREFERENCE FOUND\n");
+			sm_params.rx_status.preference = *option_ptr;
+			break;
+		default:
+			debug("Unknown Option ID: %d, skipping parsing\n",
+			      ntohs(option_hdr->option_id));
+			break;
+		}
+		/* Increment to next option header */
+		option_hdr = (struct dhcp6_option_hdr *)(((uchar *)option_hdr) +
+			     sizeof(struct dhcp6_option_hdr) + option_len);
+	}
+}
+
+/**
+ * dhcp6_check_advertise_packet() - Perform error checking on an expected
+ *                                  ADVERTISE packet.
+ *
+ * @rx_pkt: pointer to beginning of received DHCP6 packet
+ * @len: Total length of the DHCP6 packet
+ *
+ * Implements RFC 8415:
+ *    - 16.3.  Advertise Message
+ *    - 18.2.10.  Receipt of Reply Messages
+ *
+ * Return : 0 : ADVERTISE packet was received with no errors.
+ *              State machine can progress
+ *          1 : - packet received is not an ADVERTISE packet
+ *              - there were errors in the packet received,
+ *              - this is the first SOLICIT packet, but
+ *                received preference is not 255, so we have
+ *                to wait for more server responses.
+ */
+static int dhcp6_check_advertise_packet(uchar *rx_pkt, unsigned int len)
+{
+	u16 rx_uid_size;
+	struct dhcp6_hdr *dhcp6_hdr = (struct dhcp6_hdr *)rx_pkt;
+
+	/* Ignore message if msg-type != advertise */
+	if (dhcp6_hdr->msg_type != DHCP6_MSG_ADVERTISE)
+		return 1;
+	/* Ignore message if transaction ID doesn't match */
+	if (dhcp6_hdr->trans_id != htons(sm_params.trans_id))
+		return 1;
+
+	dhcp6_parse_options(rx_pkt, len);
+
+	/* Ignore advertise if any of these conditions met */
+	if (!sm_params.rx_status.server_id_found  ||
+	    !sm_params.rx_status.client_id_match  ||
+	    sm_params.rx_status.status_code != DHCP6_SUCCESS) {
+		return 1;
+	}
+
+	if (sm_params.rx_status.server_id_found) {
+		/* if no server UID has been received yet, or if the server UID
+		 * received has a higher preference value than the currently saved
+		 * server UID, save the new server UID and preference
+		 */
+		if (!sm_params.server_uid.uid_ptr ||
+		    (sm_params.server_uid.uid_ptr &&
+		    sm_params.server_uid.preference < sm_params.rx_status.preference)) {
+			rx_uid_size = sm_params.rx_status.server_uid_size;
+			if (sm_params.server_uid.uid_ptr)
+				free(sm_params.server_uid.uid_ptr);
+			sm_params.server_uid.uid_ptr = malloc(rx_uid_size * sizeof(uchar));
+			if (sm_params.server_uid.uid_ptr)
+				memcpy(sm_params.server_uid.uid_ptr,
+				       sm_params.rx_status.server_uid_ptr, rx_uid_size);
+
+			sm_params.server_uid.uid_size = rx_uid_size;
+			sm_params.server_uid.preference = sm_params.rx_status.preference;
+		}
+
+		/* If the first SOLICIT and preference code is 255, use right away.
+		 * Otherwise, wait for the first SOLICIT period for more
+		 * DHCP6 servers to respond.
+		 */
+		if (sm_params.retry_cnt == 1 &&
+		    sm_params.server_uid.preference != 255) {
+			debug("valid ADVERTISE, waiting for first SOLICIT period\n");
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * dhcp6_check_reply_packet() - Perform error checking on an expected
+ *                              REPLY packet.
+ *
+ * @rx_pkt: pointer to beginning of received DHCP6 packet
+ * @len: Total length of the DHCP6 packet
+ *
+ * Implements RFC 8415:
+ *    - 16.10. Reply Message
+ *    - 18.2.10. Receipt of Reply Messages
+ *
+ * Return : 0 - REPLY packet was received with no errors
+ *          1 - packet received is not an REPLY packet,
+ *              or there were errors in the packet received
+ */
+static int dhcp6_check_reply_packet(uchar *rx_pkt, unsigned int len)
+{
+	struct dhcp6_hdr *dhcp6_hdr = (struct dhcp6_hdr *)rx_pkt;
+
+	//Ignore message if msg-type != reply
+	if (dhcp6_hdr->msg_type != DHCP6_MSG_REPLY)
+		return 1;
+	//check that transaction ID matches
+	if (dhcp6_hdr->trans_id != htons(sm_params.trans_id))
+		return 1;
+
+	dhcp6_parse_options(rx_pkt, len);
+
+	//if no addresses found, restart DHCP
+	if (!sm_params.rx_status.ia_addr_found ||
+	    sm_params.rx_status.ia_status_code == DHCP6_NO_ADDRS_AVAIL ||
+	    sm_params.rx_status.status_code == DHCP6_NOT_ON_LINK) {
+		//restart DHCP
+		debug("No address found in reply.  Restarting DHCP\n");
+		dhcp6_start();
+	}
+
+	//ignore reply if any of these conditions met
+	if (!sm_params.rx_status.server_id_found  ||
+	    !sm_params.rx_status.client_id_match ||
+	    sm_params.rx_status.status_code == DHCP6_UNSPEC_FAIL) {
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * dhcp6_state_machine() - DHCP6 state machine
+ *
+ * @timeout: TRUE : timeout waiting for response from
+ *                  DHCP6 server
+ *           FALSE : init or received response from DHCP6 server
+ * @rx_pkt: Pointer to the beginning of received DHCP6 packet.
+ *          Will be NULL if called as part of init
+ *          or timeout==TRUE
+ * @len: Total length of the DHCP6 packet if rx_pkt != NULL
+ *
+ * Implements RFC 8415:
+ *    - 5.2.  Client/Server Exchanges Involving Four Messages
+ *    - 15.  Reliability of Client-Initiated Message Exchanges
+ *
+ * Handles:
+ *    - transmission of SOLICIT and REQUEST packets
+ *    - retransmission of SOLICIT and REQUEST packets if no
+ *      response is received within the timeout window
+ *    - checking received ADVERTISE and REPLY packets to
+ *      assess if the DHCP state machine can progress
+ */
+static void dhcp6_state_machine(bool timeout, uchar *rx_pkt, unsigned int len)
+{
+	int rand_minus_plus_100;
+
+	switch (sm_params.curr_state) {
+	case DHCP6_INIT:
+		sm_params.next_state = DHCP6_SOLICIT;
+		break;
+	case DHCP6_SOLICIT:
+		if (!timeout) {
+			/* check the rx packet and determine if we can transition to next
+			 * state.
+			 */
+			if (dhcp6_check_advertise_packet(rx_pkt, len))
+				return;
+
+			debug("ADVERTISE good, transition to REQUEST\n");
+			sm_params.next_state = DHCP6_REQUEST;
+		} else if (sm_params.retry_cnt == 1)  {
+			/* If a server UID was received in the first SOLICIT period
+			 * transition to REQUEST
+			 */
+			if (sm_params.server_uid.uid_ptr)
+				sm_params.next_state = DHCP6_REQUEST;
+		}
+		break;
+	case DHCP6_REQUEST:
+		if (!timeout) {
+			/* check the rx packet and determine if we can transition to next state */
+			if (dhcp6_check_reply_packet(rx_pkt, len))
+				return;
+
+			debug("REPLY good, transition to DONE\n");
+			sm_params.next_state = DHCP6_DONE;
+		}
+		break;
+	case DHCP6_DONE:
+	case DHCP6_FAIL:
+		/* Shouldn't get here, as state machine should exit
+		 * immediately when DHCP6_DONE or DHCP6_FAIL is entered.
+		 * Proceed anyway to proceed DONE/FAIL actions
+		 */
+		debug("Unexpected DHCP6 state : %d\n", sm_params.curr_state);
+		break;
+	}
+	/* re-seed the RNG */
+	srand(get_ticks() + rand());
+
+	/* handle state machine entry conditions */
+	if (sm_params.curr_state != sm_params.next_state) {
+		sm_params.retry_cnt = 0;
+
+		if (sm_params.next_state == DHCP6_SOLICIT) {
+			/* delay a random ammount (special for SOLICIT) */
+			udelay((rand() % SOL_MAX_DELAY_MS) * 1000);
+			/* init timestamp variables after SOLICIT delay */
+			sm_params.dhcp6_start_ms = get_timer(0);
+			sm_params.dhcp6_retry_start_ms = sm_params.dhcp6_start_ms;
+			sm_params.dhcp6_retry_ms = sm_params.dhcp6_start_ms;
+			/* init transaction and ia_id */
+			sm_params.trans_id = rand() & 0xFFFFFF;
+			sm_params.ia_id = rand();
+			/* initialize retransmission parameters */
+			sm_params.irt_ms = SOL_TIMEOUT_MS;
+			sm_params.mrt_ms = updated_sol_max_rt_ms;
+			/* RFCs default MRC is be 0 (try infinitely)
+			 * give up after TIMEOUT_COUNT number of tries (same as DHCPv4)
+			 */
+			sm_params.mrc = TIMEOUT_COUNT;
+			sm_params.mrd_ms = 0;
+
+		} else if (sm_params.next_state == DHCP6_REQUEST) {
+			/* init timestamp variables  */
+			sm_params.dhcp6_retry_start_ms = get_timer(0);
+			sm_params.dhcp6_retry_ms = sm_params.dhcp6_start_ms;
+			/* initialize retransmission parameters */
+			sm_params.irt_ms = REQ_TIMEOUT_MS;
+			sm_params.mrt_ms = REQ_MAX_RT_MS;
+			sm_params.mrc = REQ_MAX_RC;
+			sm_params.mrd_ms = 0;
+		}
+	}
+
+	if (timeout)
+		sm_params.dhcp6_retry_ms = get_timer(0);
+
+	/* Check if MRC or MRD have been passed */
+	if ((sm_params.mrc != 0 &&
+	     sm_params.retry_cnt >= sm_params.mrc) ||
+	    (sm_params.mrd_ms != 0 &&
+	     ((sm_params.dhcp6_retry_ms - sm_params.dhcp6_retry_start_ms) >= sm_params.mrd_ms))) {
+		sm_params.next_state = DHCP6_FAIL;
+	}
+
+	/* calculate retransmission timeout (RT) */
+	rand_minus_plus_100 = ((rand() % 200) - 100);
+	if (sm_params.retry_cnt == 0) {
+		sm_params.rt_ms = sm_params.irt_ms +
+				  ((sm_params.irt_ms * rand_minus_plus_100) / 1000);
+	} else {
+		sm_params.rt_ms = (2 * sm_params.rt_prev_ms) +
+				  ((sm_params.rt_prev_ms * rand_minus_plus_100) / 1000);
+	}
+
+	if (sm_params.rt_ms > sm_params.mrt_ms) {
+		sm_params.rt_ms = sm_params.mrt_ms +
+				  ((sm_params.mrt_ms * rand_minus_plus_100) / 1000);
+	}
+
+	sm_params.rt_prev_ms = sm_params.rt_ms;
+
+	net_set_timeout_handler(sm_params.rt_ms, dhcp6_timeout_handler);
+
+	/* send transmit/retransmit message or fail */
+	sm_params.curr_state = sm_params.next_state;
+
+	if (sm_params.curr_state == DHCP6_SOLICIT) {
+		/* send solicit packet */
+		dhcp6_send_solicit_packet();
+		printf("DHCP6 SOLICIT %d\n", sm_params.retry_cnt);
+	} else if (sm_params.curr_state == DHCP6_REQUEST) {
+		/* send request packet */
+		dhcp6_send_request_packet();
+		printf("DHCP6 REQUEST %d\n", sm_params.retry_cnt);
+	} else if (sm_params.curr_state == DHCP6_DONE) {
+		net_set_timeout_handler(0, 0);
+
+		/* Duplicate address detection (DAD) should be
+		 * performed here before setting net_ip6
+		 * (enhancement should be considered)
+		 */
+		net_copy_ip6(&net_ip6, &sm_params.rx_status.ia_addr_ipv6);
+		printf("DHCP6 client bound to %pI6c\n", &net_ip6);
+		/* will load with TFTP6 */
+		net_auto_load();
+	} else if (sm_params.curr_state == DHCP6_FAIL) {
+		printf("DHCP6 FAILED, TERMINATING\n");
+		net_set_state(NETLOOP_FAIL);
+	}
+	sm_params.retry_cnt++;
+}
+
+/*
+ * Timeout for DHCP6 SOLICIT/REQUEST.
+ */
+static void dhcp6_timeout_handler(void)
+{
+	/* call state machine with the timeout flag */
+	dhcp6_state_machine(true, NULL, 0);
+}
+
+/*
+ * Start or restart DHCP6
+ */
+void dhcp6_start(void)
+{
+	memset(&sm_params, 0, sizeof(struct dhcp6_sm_params));
+
+	/* seed the RNG with MAC address */
+	srand_mac();
+
+	sm_params.curr_state = DHCP6_INIT;
+	dhcp6_state_machine(false, NULL, 0);
+}
diff --git a/net/dhcpv6.h b/net/dhcpv6.h
new file mode 100644
index 0000000000..da722feff7
--- /dev/null
+++ b/net/dhcpv6.h
@@ -0,0 +1,212 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright (C) Microsoft Corporation
+ * Author: Sean Edmond <seanedmond@microsoft.com>
+ *
+ */
+
+#ifndef __DHCP6_H__
+#define __DHCP6_H__
+
+#include <net6.h>
+#include <net.h>
+
+/* Message types */
+#define DHCP6_MSG_SOLICIT	1
+#define DHCP6_MSG_ADVERTISE	2
+#define DHCP6_MSG_REQUEST	3
+#define DHCP6_MSG_REPLY		7
+
+/* Option Codes */
+#define DHCP6_OPTION_CLIENTID		1
+#define DHCP6_OPTION_SERVERID		2
+#define DHCP6_OPTION_IA_NA		3
+#define DHCP6_OPTION_IA_TA		4
+#define DHCP6_OPTION_IAADDR		5
+#define DHCP6_OPTION_ORO		6
+#define DHCP6_OPTION_PREFERENCE		7
+#define DHCP6_OPTION_ELAPSED_TIME	8
+#define DHCP6_OPTION_STATUS_CODE	13
+#define DHCP6_OPTION_OPT_BOOTFILE_URL	59
+#define DHCP6_OPTION_OPT_BOOTFILE_PARAM	60
+#define DHCP6_OPTION_SOL_MAX_RT		82
+#define DHCP6_OPTION_CLIENT_ARCH_TYPE	61
+#define DHCP6_OPTION_VENDOR_CLASS	16
+#define DHCP6_OPTION_NII		62
+
+/* DUID */
+#define DUID_TYPE_LL		3
+#define DUID_HW_TYPE_ENET	1
+#define DUID_LL_SIZE		(sizeof(struct dhcp6_option_duid_ll) + ETH_ALEN)
+#define DUID_MAX_SIZE		DUID_LL_SIZE /* only supports DUID-LL currently */
+
+/* vendor-class-data to send in vendor clas option */
+#define DHCP6_VCI_STRING	"U-boot"
+
+#define DHCP6_MULTICAST_ADDR	"ff02::1:2"	/* DHCP multicast address */
+
+/* DHCP6 States supported */
+enum dhcp6_state {
+	DHCP6_INIT,
+	DHCP6_SOLICIT,
+	DHCP6_REQUEST,
+	DHCP6_DONE,
+	DHCP6_FAIL,
+};
+
+/* DHCP6 Status codes */
+enum dhcp6_status {
+	DHCP6_SUCCESS = 0,
+	DHCP6_UNSPEC_FAIL = 1,
+	DHCP6_NO_ADDRS_AVAIL = 2,
+	DHCP6_NO_BINDING = 3,
+	DHCP6_NOT_ON_LINK = 4,
+	DHCP6_USE_MULTICAST = 5,
+	DHCP6_NO_PREFIX_AVAIL = 6,
+};
+
+/* DHCP6 message header format */
+struct dhcp6_hdr {
+	unsigned int msg_type : 8;	/* message type */
+	unsigned int trans_id : 24;	/* transaction ID */
+} __packed;
+
+/* DHCP6 option header format */
+struct dhcp6_option_hdr {
+	__be16	option_id;	/* option id */
+	__be16	option_len;	/* Option length */
+	u8	option_data[0];	/* Option data */
+} __packed;
+
+/* DHCP6_OPTION_CLIENTID option (DUID-LL) */
+struct dhcp6_option_duid_ll {
+	__be16	duid_type;
+	__be16	hw_type;
+	u8	ll_addr[0];
+} __packed;
+
+/* DHCP6_OPTION_ELAPSED_TIME option */
+struct dhcp6_option_elapsed_time {
+	__be16	elapsed_time;
+} __packed;
+
+/* DHCP6_OPTION_IA_TA option */
+struct dhcp6_option_ia_ta {
+	__be32	iaid;
+	u8	ia_ta_options[0];
+} __packed;
+
+/* DHCP6_OPTION_IA_NA option */
+struct dhcp6_option_ia_na {
+	__be32	iaid;
+	__be32	t1;
+	__be32	t2;
+	u8	ia_na_options[0];
+} __packed;
+
+/* OPTION_ORO option */
+struct dhcp6_option_oro  {
+	__be16	req_option_code[0];
+} __packed;
+
+/* DHCP6_OPTION_CLIENT_ARCH_TYPE option */
+struct dhcp6_option_client_arch {
+	__be16	arch_type[0];
+} __packed;
+
+/* vendor-class-data inside OPTION_VENDOR_CLASS option */
+struct vendor_class_data {
+	__be16	vendor_class_len;
+	u8	opaque_data[0];
+} __packed;
+
+/* DHCP6_OPTION_VENDOR_CLASS option */
+struct dhcp6_option_vendor_class {
+	__be32				enterprise_number;
+	struct vendor_class_data	vendor_class_data[0];
+} __packed;
+
+/**
+ * struct dhcp6_rx_pkt_status - Structure that holds status
+ *                              from a received message
+ * @client_id_match: Client ID was found and matches DUID sent
+ * @server_id_found: Server ID was found in the message
+ * @server_uid_ptr: Pointer to received server ID
+ * @server_uid_size: Size of received server ID
+ * @ia_addr_found: IA addr option was found in received message
+ * @ia_addr_ipv6: The IPv6 address received in IA
+ * @ia_status_code: Status code received in the IA
+ * @status_code: Top-level status code received
+ * @preference: Preference code received
+ */
+struct dhcp6_rx_pkt_status {
+	bool			client_id_match;
+	bool			server_id_found;
+	uchar			*server_uid_ptr;
+	u16			server_uid_size;
+	bool			ia_addr_found;
+	struct in6_addr		ia_addr_ipv6;
+	enum dhcp6_status	ia_status_code;
+	enum dhcp6_status	status_code;
+	u8			preference;
+};
+
+/**
+ * struct dhcp6_server_uid - Structure that holds the server UID
+ *                             received from an ADVERTISE and saved
+ *                             given the server selection criteria.
+ * @uid_ptr: Dynamically allocated and copied server UID
+ * @uid_size: Size of the server UID in uid_ptr (in bytes)
+ * @preference: Preference code associated with this server UID
+ */
+struct dhcp6_server_uid {
+	uchar	*uid_ptr;
+	u16	uid_size;
+	u8	preference;
+};
+
+/**
+ * struct dhcp6_rx_pkt_status - Structure that holds DHCP6
+ *                              state machine parameters
+ * @curr_state: current DHCP6 state
+ * @next_state: next DHCP6 state
+ * @dhcp6_start_ms: timestamp DHCP6 start
+ * @dhcp6_retry_start_ms: timestamp of current TX message start
+ * @dhcp6_retry_ms: timestamp of last retransmission
+ * @retry_cnt: retry count
+ * @trans_id: transaction ID
+ * @ia_id: transmitted IA ID
+ * @irt_ms: Initial retransmission time (in ms)
+ * @mrt_ms: Maximum retransmission time (in ms)
+ * @mrc: Maximum retransmission count
+ * @mrd_ms: Maximum retransmission duration (in ms)
+ * @rt_ms: retransmission timeout (is ms)
+ * @rt_prev_ms: previous retransmission timeout
+ * @rx_status: Status from received message
+ * @server_uid: Saved Server UID for selected server
+ * @duid: pointer to transmitted Client DUID
+ */
+struct dhcp6_sm_params {
+	enum dhcp6_state		curr_state;
+	enum dhcp6_state		next_state;
+	ulong				dhcp6_start_ms;
+	ulong				dhcp6_retry_start_ms;
+	ulong				dhcp6_retry_ms;
+	u32				retry_cnt;
+	u32				trans_id;
+	u32				ia_id;
+	int				irt_ms;
+	int				mrt_ms;
+	int				mrc;
+	int				mrd_ms;
+	int				rt_ms;
+	int				rt_prev_ms;
+	struct dhcp6_rx_pkt_status	rx_status;
+	struct dhcp6_server_uid		server_uid;
+	char				duid[DUID_MAX_SIZE];
+};
+
+/* Send a DHCPv6 request */
+void dhcp6_start(void);
+
+#endif /* __DHCP6_H__ */
diff --git a/net/net.c b/net/net.c
index c9a749f6cc..73d5b2bc80 100644
--- a/net/net.c
+++ b/net/net.c
@@ -122,6 +122,9 @@
 #endif
 #include <net/tcp.h>
 #include <net/wget.h>
+#if defined(CONFIG_CMD_DHCP6)
+#include "dhcpv6.h"
+#endif
 
 /** BOOTP EXTENTIONS **/
 
@@ -135,6 +138,10 @@ struct in_addr net_dns_server;
 /* Our 2nd DNS IP address */
 struct in_addr net_dns_server2;
 #endif
+#if defined(CONFIG_DHCP6_PXE_DHCP_OPTION)
+/* Indicates whether the pxe path prefix / config file was specified in dhcp option */
+char *pxelinux_configfile;
+#endif
 
 /** END OF BOOTP EXTENTIONS **/
 
@@ -510,6 +517,11 @@ restart:
 			dhcp_request();		/* Basically same as BOOTP */
 			break;
 #endif
+#if defined(CONFIG_CMD_DHCP6)
+		case DHCP6:
+			dhcp6_start();
+			break;
+#endif
 #if defined(CONFIG_CMD_BOOTP)
 		case BOOTP:
 			bootp_reset();
-- 
2.39.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH 2/2] net: dhcp6: pxe: Add DHCP/PXE commands for IPv6
  2023-02-02  0:26 [PATCH 0/2] net: DHCPv6 protocol and commands seanedmond
  2023-02-02  0:26 ` [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6) seanedmond
@ 2023-02-02  0:26 ` seanedmond
  2023-03-05 11:44   ` Vyacheslav V. Mitrofanov
  2023-03-05 11:39 ` [PATCH 0/2] net: DHCPv6 protocol and commands Vyacheslav V. Mitrofanov
  2 siblings, 1 reply; 8+ messages in thread
From: seanedmond @ 2023-02-02  0:26 UTC (permalink / raw)
  To: u-boot; +Cc: joe.hershberger, rfried.dev, sjg, v.v.mitrofanov, seanedmond

From: Sean Edmond <seanedmond@microsoft.com>

Adds commands to support DHCP and PXE with IPv6.

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

Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
---
 boot/bootmeth_distro.c |  2 +-
 boot/bootmeth_pxe.c    |  4 +-
 boot/pxe_utils.c       |  3 +-
 cmd/net.c              | 22 +++++++++++
 cmd/pxe.c              | 86 +++++++++++++++++++++++++++++++++++++-----
 cmd/sysboot.c          |  2 +-
 include/net.h          |  2 +
 include/net6.h         |  2 -
 include/pxe_utils.h    | 10 ++++-
 9 files changed, 115 insertions(+), 18 deletions(-)

diff --git a/boot/bootmeth_distro.c b/boot/bootmeth_distro.c
index 356929828b..b4b73ecbf5 100644
--- a/boot/bootmeth_distro.c
+++ b/boot/bootmeth_distro.c
@@ -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);
 
diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c
index ecf8557af8..5a8af2bbd0 100644
--- a/boot/bootmeth_pxe.c
+++ b/boot/bootmeth_pxe.c
@@ -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);
 
diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c
index 3a1e50f2b1..d13c47dd94 100644
--- a/boot/pxe_utils.c
+++ b/boot/pxe_utils.c
@@ -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)
diff --git a/cmd/net.c b/cmd/net.c
index 4227321871..88d53d14d5 100644
--- a/cmd/net.c
+++ b/cmd/net.c
@@ -111,6 +111,28 @@ 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",
+	   "[loadAddress] [[hostIPaddr:]bootfilename]");
+#endif
+
 #if defined(CONFIG_CMD_DHCP)
 static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc,
 		   char *const argv[])
diff --git a/cmd/pxe.c b/cmd/pxe.c
index db8e4697f2..ebc44fd661 100644
--- a/cmd/pxe.c
+++ b/cmd/pxe.c
@@ -11,6 +11,10 @@
 
 #include "pxe_utils.h"
 
+#if IS_ENABLED(CONFIG_IPV6)
+#include <net6.h>
+#endif
+
 #ifdef CONFIG_CMD_NET
 const char *pxe_default_paths[] = {
 #ifdef CONFIG_SYS_SOC
@@ -29,12 +33,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);
@@ -43,6 +55,22 @@ static int do_get_tftp(struct pxe_context *ctx, const char *file_path,
 	return 1;
 }
 
+#if defined(CONFIG_DHCP6_PXE_DHCP_OPTION)
+/*
+ * Looks for a pxe file with specified config file name,
+ * which is received from DHCP option 209.
+ *
+ * Returns 1 on success or < 0 on error.
+ */
+static inline 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;
+}
+#endif
 /*
  * Looks for a pxe file with a name based on the pxeuuid environment variable.
  *
@@ -105,15 +133,25 @@ 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 defined(CONFIG_DHCP6_PXE_DHCP_OPTION)
+	if (pxelinux_configfile && use_ipv6) {
+		if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
+			goto done;
+
+		goto error_exit;
+	}
+#endif
+
 	/*
 	 * Keep trying paths until we successfully get a file we're looking
 	 * for.
@@ -131,6 +169,7 @@ int pxe_get(ulong pxefile_addr_r, char **bootdirp, ulong *sizep)
 		i++;
 	}
 
+error_exit:
 	pxe_destroy_ctx(&ctx);
 
 	return -ENOENT;
@@ -169,9 +208,17 @@ 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 (argc != 1 && argc != 2)
+			return CMD_RET_USAGE;
+		if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
+			use_ipv6 = true;
+	} else {
+		if (argc != 1)
+			return CMD_RET_USAGE;
+	}
 
 	pxefile_addr_str = from_env("pxefile_addr_r");
 
@@ -183,7 +230,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 +258,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 +282,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 +297,13 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 }
 
 static struct cmd_tbl cmd_pxe_sub[] = {
+#if IS_ENABLED(CONFIG_IPV6)
+	U_BOOT_CMD_MKENT(get, 2, 1, do_pxe_get, "", ""),
+	U_BOOT_CMD_MKENT(boot, 3, 1, do_pxe_boot, "", "")
+#else
 	U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
 	U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
+#endif
 };
 
 static void __maybe_unused pxe_reloc(void)
@@ -281,9 +339,19 @@ static int do_pxe(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
 	return CMD_RET_USAGE;
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+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"
+);
+#else
 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"
 );
 #endif
+
+#endif /* CONFIG_CMD_NET */
diff --git a/cmd/sysboot.c b/cmd/sysboot.c
index 04c0702026..63a7806deb 100644
--- a/cmd/sysboot.c
+++ b/cmd/sysboot.c
@@ -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;
 	}
diff --git a/include/net.h b/include/net.h
index cac818e292..7463b1285b 100644
--- a/include/net.h
+++ b/include/net.h
@@ -49,6 +49,8 @@ struct udevice;
  */
 #define ARP_HLEN_ASCII (ARP_HLEN * 2) + (ARP_HLEN - 1)
 
+#define USE_IP6_CMD_PARAM	"-ipv6"
+
 /* IPv4 addresses are always 32 bits in size */
 struct in_addr {
 	__be32 s_addr;
diff --git a/include/net6.h b/include/net6.h
index 9b3de028e6..571c0593e5 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -39,8 +39,6 @@ struct in6_addr {
 #define IPV6_ADDRSCOPE_ORG	0x08
 #define IPV6_ADDRSCOPE_GLOBAL	0x0E
 
-#define USE_IP6_CMD_PARAM	"-ipv6"
-
 /**
  * struct ipv6hdr - Internet Protocol V6 (IPv6) header.
  *
diff --git a/include/pxe_utils.h b/include/pxe_utils.h
index 1e5e8424f5..9f19593048 100644
--- a/include/pxe_utils.h
+++ b/include/pxe_utils.h
@@ -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 */
-- 
2.39.0


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6)
  2023-02-02  0:26 ` [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6) seanedmond
@ 2023-02-02 17:12   ` Simon Glass
  2023-02-03 22:12     ` Tom Rini
  2023-03-05 11:41   ` Vyacheslav V. Mitrofanov
  1 sibling, 1 reply; 8+ messages in thread
From: Simon Glass @ 2023-02-02 17:12 UTC (permalink / raw)
  To: seanedmond
  Cc: u-boot, joe.hershberger, rfried.dev, v.v.mitrofanov, seanedmond

Hi,

On Wed, 1 Feb 2023 at 17:26, <seanedmond@linux.microsoft.com> wrote:
>
> From: Sean Edmond <seanedmond@microsoft.com>
>
> Adds DHCPv6 protocol to u-boot.
>
> Allows for address assignement with DHCPv6 4-message exchange
> (SOLICIT->ADVERTISE->REQUEST->REPLY).  Includes DHCPv6 options
> required by RFC 8415.  Also adds DHCPv6 options required
> for PXE boot.
>
> New configs added:
> - CMD_DHCP6
> - DHCP6_PXE_CLIENTARCH
> - DHCP6_PXE_DHCP_OPTION
> - DHCP6_ENTERPRISE_ID
>
> Possible enhancements:
> - Duplicate address detection on DHCPv6 assigned address
> - IPv6 address assignement through SLAAC
> - Sending/parsing other DHCPv6 options (NTP, DNS, etc...)
>
> Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
> ---
>  cmd/Kconfig   |  26 ++
>  include/net.h |   8 +-
>  net/Makefile  |   1 +
>  net/dhcpv6.c  | 741 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  net/dhcpv6.h  | 212 +++++++++++++++
>  net/net.c     |  12 +
>  6 files changed, 998 insertions(+), 2 deletions(-)
>  create mode 100644 net/dhcpv6.c
>  create mode 100644 net/dhcpv6.h

Can you write a sandbox test for this? See test/dm/eth.c

Regards,
Simon

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6)
  2023-02-02 17:12   ` Simon Glass
@ 2023-02-03 22:12     ` Tom Rini
  0 siblings, 0 replies; 8+ messages in thread
From: Tom Rini @ 2023-02-03 22:12 UTC (permalink / raw)
  To: Simon Glass
  Cc: seanedmond, u-boot, joe.hershberger, rfried.dev, v.v.mitrofanov,
	seanedmond

[-- Attachment #1: Type: text/plain, Size: 1400 bytes --]

On Thu, Feb 02, 2023 at 10:12:04AM -0700, Simon Glass wrote:
> Hi,
> 
> On Wed, 1 Feb 2023 at 17:26, <seanedmond@linux.microsoft.com> wrote:
> >
> > From: Sean Edmond <seanedmond@microsoft.com>
> >
> > Adds DHCPv6 protocol to u-boot.
> >
> > Allows for address assignement with DHCPv6 4-message exchange
> > (SOLICIT->ADVERTISE->REQUEST->REPLY).  Includes DHCPv6 options
> > required by RFC 8415.  Also adds DHCPv6 options required
> > for PXE boot.
> >
> > New configs added:
> > - CMD_DHCP6
> > - DHCP6_PXE_CLIENTARCH
> > - DHCP6_PXE_DHCP_OPTION
> > - DHCP6_ENTERPRISE_ID
> >
> > Possible enhancements:
> > - Duplicate address detection on DHCPv6 assigned address
> > - IPv6 address assignement through SLAAC
> > - Sending/parsing other DHCPv6 options (NTP, DNS, etc...)
> >
> > Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
> > ---
> >  cmd/Kconfig   |  26 ++
> >  include/net.h |   8 +-
> >  net/Makefile  |   1 +
> >  net/dhcpv6.c  | 741 ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  net/dhcpv6.h  | 212 +++++++++++++++
> >  net/net.c     |  12 +
> >  6 files changed, 998 insertions(+), 2 deletions(-)
> >  create mode 100644 net/dhcpv6.c
> >  create mode 100644 net/dhcpv6.h
> 
> Can you write a sandbox test for this? See test/dm/eth.c

This should be like our existing dhcp test I think, under
test/py/tests/test_net.py

-- 
Tom

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 659 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 0/2] net: DHCPv6 protocol and commands
  2023-02-02  0:26 [PATCH 0/2] net: DHCPv6 protocol and commands seanedmond
  2023-02-02  0:26 ` [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6) seanedmond
  2023-02-02  0:26 ` [PATCH 2/2] net: dhcp6: pxe: Add DHCP/PXE commands for IPv6 seanedmond
@ 2023-03-05 11:39 ` Vyacheslav V. Mitrofanov
  2 siblings, 0 replies; 8+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-03-05 11:39 UTC (permalink / raw)
  To: u-boot, seanedmond; +Cc: sjg, seanedmond, joe.hershberger, rfried.dev

> From: Sean Edmond <seanedmond@microsoft.com>
> 
> The recently integrated IPv6 patch series relies on the link-local
> address,
> or a statically assigned IPv6 address for network operations.  This
> patch
> series adds IPv6 address assignment through DHCPv6.
> 
> The implementation meets the requirements in RFC 8415 for
> "Client/Server
> Exchanges Involving Four Messages":
> https://www.rfc-editor.org/rfc/rfc8415
> 
> The implementation sends/receives the minimum required DHCPv6 options
> to
> network boot.
> 
> A new command (dhcp6) will execute the protocol.  In addition, IPv6
> functionality has been extended to the existing pxe commands ("pxe
> get"
> and "pxe boot").
> 
> Sean Edmond (2):
>   net: dhcp6: Add DHCPv6 (DHCP for IPv6)
>   net: dhcp6: pxe: Add DHCP/PXE commands for IPv6
> 
>  boot/bootmeth_distro.c |   2 +-
>  boot/bootmeth_pxe.c    |   4 +-
>  boot/pxe_utils.c       |   3 +-
>  cmd/Kconfig            |  26 ++
>  cmd/net.c              |  22 ++
>  cmd/pxe.c              |  86 ++++-
>  cmd/sysboot.c          |   2 +-
>  include/net.h          |  10 +-
>  include/net6.h         |   2 -
>  include/pxe_utils.h    |  10 +-
>  net/Makefile           |   1 +
>  net/dhcpv6.c           | 741
> +++++++++++++++++++++++++++++++++++++++++
>  net/dhcpv6.h           | 212 ++++++++++++
>  net/net.c              |  12 +
>  14 files changed, 1113 insertions(+), 20 deletions(-)
>  create mode 100644 net/dhcpv6.c
>  create mode 100644 net/dhcpv6.h
> 
> --
> 2.39.0
> 
> 


Hello! Very sorry for the long response!
I checked your patches and in general it looks good. Anyway I have made
some notes. It is up to you and maintainers to improve the code or not.


Moreover I've briefly tested it and it works.
=> dhcp6
: PHY present at 0
: Starting autonegotiation...
: Autonegotiation complete
: link up, 100Mbps full-duplex (lpa: 0x41e1)
DHCP6 SOLICIT 0
DHCP6 REQUEST 0
DHCP6 client bound to fe80:0000:0000:0000:42f2:e9ff:fe6d:beef

Notes:
1. Some comments have //-style. Better to use /**/
//global variable to track any updates from DHCP6 server 

2. If you apply only the first patch you can add to config CMD_DHCP6
but when you run u-boot it won't be available. Looks like sth
incomplete. I think it would be much better to enable CMD_DHCP6 in the
second patch when you add do_dhcp6().

3. struct dhcp6_sm_params has invalid description. You copy-passed it
from the previous func.

4. Add CMD_DHCP6 to sandbox_*_defconfig

5. if CONFIG_DHCP6_PXE_DHCP_OPTION is not configured then you will get
"cmd/pxe.c:172:1: warning: label ‘error_exit’ defined but not used"
during a build

6. How to determine IPv6 address in dhcp6 command 
=> help dhcp6
	dhcp6 [loadAddress] [[hostIPaddr:]bootfilename]

In tftpboot []-notation is used to specify IPv6

7. net_set_timeout_handler(); has prototype void
net_set_timeout_handler(ulong iv, thand_f *f). Maybe it is better to
use net_set_timeout_handler(0, NULL); than net_set_timeout_handler(0,
0); 

8. You moved #define USE_IP6_CMD_PARAM      "-ipv6" from include/net6.h
to include/net.h
I think it is better to leave IPv6 defines in net6 header.

Thanks!

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6)
  2023-02-02  0:26 ` [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6) seanedmond
  2023-02-02 17:12   ` Simon Glass
@ 2023-03-05 11:41   ` Vyacheslav V. Mitrofanov
  1 sibling, 0 replies; 8+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-03-05 11:41 UTC (permalink / raw)
  To: u-boot, seanedmond; +Cc: sjg, seanedmond, joe.hershberger, rfried.dev

> From: Sean Edmond <seanedmond@microsoft.com>
> 
> Adds DHCPv6 protocol to u-boot.
> 
> Allows for address assignement with DHCPv6 4-message exchange
> (SOLICIT->ADVERTISE->REQUEST->REPLY).  Includes DHCPv6 options
> required by RFC 8415.  Also adds DHCPv6 options required
> for PXE boot.
> 
> New configs added:
> - CMD_DHCP6
> - DHCP6_PXE_CLIENTARCH
> - DHCP6_PXE_DHCP_OPTION
> - DHCP6_ENTERPRISE_ID
> 
> Possible enhancements:
> - Duplicate address detection on DHCPv6 assigned address
> - IPv6 address assignement through SLAAC
> - Sending/parsing other DHCPv6 options (NTP, DNS, etc...)
> 
> Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
> ---
>  cmd/Kconfig   |  26 ++
>  include/net.h |   8 +-
>  net/Makefile  |   1 +
>  net/dhcpv6.c  | 741
> ++++++++++++++++++++++++++++++++++++++++++++++++++
>  net/dhcpv6.h  | 212 +++++++++++++++
>  net/net.c     |  12 +
>  6 files changed, 998 insertions(+), 2 deletions(-)
>  create mode 100644 net/dhcpv6.c
>  create mode 100644 net/dhcpv6.h
> 
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index dc0446e02e..87813ddbb4 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1645,6 +1645,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
> @@ -1758,6 +1767,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
> diff --git a/include/net.h b/include/net.h
> index 399af5e064..cac818e292 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -484,6 +484,10 @@ extern char        net_hostname[32];       /*
> Our hostname */
>  #ifdef CONFIG_NET
>  extern char    net_root_path[CONFIG_BOOTP_MAX_ROOT_PATH_LEN];  /*
> Our root path */
>  #endif
> +#if defined(CONFIG_DHCP6_PXE_DHCP_OPTION)
> +/* Indicates whether the pxe path prefix / config file was specified
> in dhcp option */
> +extern char *pxelinux_configfile;
> +#endif
>  /** END OF BOOTP EXTENTIONS **/
>  extern u8              net_ethaddr[ARP_HLEN];          /* Our
> ethernet address */
>  extern u8              net_server_ethaddr[ARP_HLEN];   /* Boot
> server enet address */
> @@ -504,8 +508,8 @@ extern
> ushort               net_native_vlan;        /* Our Native VLAN */
>  extern int             net_restart_wrap;       /* Tried all network
> devices */
> 
>  enum proto_t {
> -       BOOTP, RARP, ARP, TFTPGET, DHCP, PING, PING6, DNS, NFS, CDP,
> NETCONS,
> -       SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI,
> WGET
> +       BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS,
> NFS, CDP,
> +       NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL,
> UDP, NCSI, WGET
>  };
> 
>  extern char    net_boot_file_name[1024];/* Boot File name */
> diff --git a/net/Makefile b/net/Makefile
> index bea000b206..5968110170 100644
> --- a/net/Makefile
> +++ b/net/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_IPV6)     += net6.o
>  obj-$(CONFIG_CMD_NFS)  += nfs.o
>  obj-$(CONFIG_CMD_PING) += ping.o
>  obj-$(CONFIG_CMD_PING6) += ping6.o
> +obj-$(CONFIG_CMD_DHCP6) += dhcpv6.o
>  obj-$(CONFIG_CMD_PCAP) += pcap.o
>  obj-$(CONFIG_CMD_RARP) += rarp.o
>  obj-$(CONFIG_CMD_SNTP) += sntp.o
> diff --git a/net/dhcpv6.c b/net/dhcpv6.c
> new file mode 100644
> index 0000000000..0f0fa291d3
> --- /dev/null
> +++ b/net/dhcpv6.c
> @@ -0,0 +1,741 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright (C) Microsoft Corporation
> + * Author: Sean Edmond <seanedmond@microsoft.com>
> + *
> + */
> +
> +/* Simple DHCP6 network layer implementation. */
> +
> +#include <common.h>
> +#include <bootstage.h>
> +#include <command.h>
> +#include <env.h>
> +#include <efi_loader.h>
> +#include <log.h>
> +#include <net.h>
> +#include <rand.h>
> +#include <uuid.h>
> +#include <linux/delay.h>
> +#include <net/tftp.h>
> +#include "dhcpv6.h"
> +#include <net6.h>
> +#include <malloc.h>
> +#include "net_rand.h"
> +
> +/* Copied from bootp */
> +#ifndef CONFIG_NET_RETRY_COUNT
> +# define TIMEOUT_COUNT 5       /* # of timeouts before giving up */
> +#else
> +# define TIMEOUT_COUNT (CONFIG_NET_RETRY_COUNT)
> +#endif
> +
> +#define PORT_DHCP6_S   547     /* DHCP6 server UDP port */
> +#define PORT_DHCP6_C   546     /* DHCP6 client UDP port */
> +
> +//default timeout parameters (in ms)
> +#define SOL_MAX_DELAY_MS       1000
> +#define SOL_TIMEOUT_MS         1000
> +#define SOL_MAX_RT_MS          3600000
> +#define REQ_TIMEOUT_MS         1000
> +#define REQ_MAX_RT_MS          30000
> +#define REQ_MAX_RC             10
> +#define MAX_WAIT_TIME_MS       60000
> +
> +//global variable to track any updates from DHCP6 server
> +int updated_sol_max_rt_ms = SOL_MAX_RT_MS;
> +
> +static void dhcp6_timeout_handler(void);
> +static void dhcp6_state_machine(bool timeout, uchar *rx_pkt,
> unsigned int len);
> +static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len);
> +
> +struct dhcp6_sm_params sm_params;
> +
> +/*
> + * Handle DHCP received packets (set as UDP handler)
> + */
> +static void dhcp6_handler(uchar *pkt, unsigned int dest, struct
> in_addr sip,
> +                         unsigned int src, unsigned int len)
> +{
> +       //return if ports don't match DHCPv6 ports
> +       if (dest != PORT_DHCP6_C || src != PORT_DHCP6_S)
> +               return;
> +
> +       dhcp6_state_machine(false, pkt, len);
> +}
> +
> +/**
> + * dhcp6_add_option() - Adds DHCP6 option to a packet
> + * @option_id: The option ID to add (See DHCP6_OPTION_* definitions)
> + * @pkt: A pointer to the current write location of the TX packet
> + *
> + * Return: The number of bytes written into "*pkt"
> + */
> +static int dhcp6_add_option(int option_id, uchar *pkt)
> +{
> +       struct dhcp6_option_duid_ll *duid_opt;
> +       struct dhcp6_option_elapsed_time *elapsed_time_opt;
> +       struct dhcp6_option_ia_ta *ia_ta_opt;
> +       struct dhcp6_option_ia_na *ia_na_opt;
> +       struct dhcp6_option_oro *oro_opt;
> +       struct dhcp6_option_client_arch *client_arch_opt;
> +       struct dhcp6_option_vendor_class *vendor_class_opt;
> +       int opt_len;
> +       long elapsed_time;
> +       size_t vci_strlen;
> +       int num_oro = 0;
> +       int num_client_arch = 0;
> +       int num_vc_data = 0;
> +       struct dhcp6_option_hdr *dhcp_option = (struct
> dhcp6_option_hdr *)pkt;
> +       uchar *dhcp_option_start = pkt + sizeof(struct
> dhcp6_option_hdr);
> +
> +       dhcp_option->option_id = htons(option_id);
> +
> +       switch (option_id) {
> +       case DHCP6_OPTION_CLIENTID:
> +               /* Only support for DUID-LL in Client ID option for
> now */
> +               duid_opt = (struct dhcp6_option_duid_ll
> *)dhcp_option_start;
> +               duid_opt->duid_type = htons(DUID_TYPE_LL);
> +               duid_opt->hw_type = htons(DUID_HW_TYPE_ENET);
> +               memcpy(duid_opt->ll_addr, net_ethaddr, ETH_ALEN);
> +               opt_len = sizeof(struct dhcp6_option_duid_ll) +
> ETH_ALEN;
> +
> +               /* Save DUID for comparison later */
> +               memcpy(sm_params.duid, duid_opt, opt_len);
> +               break;
> +       case DHCP6_OPTION_ELAPSED_TIME:
> +               // calculate elapsed time in 1/100th of a second
> +               elapsed_time = (sm_params.dhcp6_retry_ms -
> +                       sm_params.dhcp6_start_ms) / 10;
> +               if (elapsed_time > 0xFFFF)
> +                       elapsed_time = 0xFFFF;
> +
> +               elapsed_time_opt = (struct dhcp6_option_elapsed_time
> *)dhcp_option_start;
> +               elapsed_time_opt->elapsed_time = htons(elapsed_time);
> +
> +               opt_len = sizeof(struct dhcp6_option_elapsed_time);
> +               break;
> +       case DHCP6_OPTION_IA_TA:
> +               ia_ta_opt = (struct dhcp6_option_ia_ta
> *)dhcp_option_start;
> +               ia_ta_opt->iaid = htonl(sm_params.ia_id);
> +
> +               opt_len = sizeof(struct dhcp6_option_ia_ta);
> +               break;
> +       case DHCP6_OPTION_IA_NA:
> +               ia_na_opt = (struct dhcp6_option_ia_na
> *)dhcp_option_start;
> +               ia_na_opt->iaid = htonl(sm_params.ia_id);
> +               /* In a message sent by a client to a server,
> +                * the T1 and T2 fields SHOULD be set to 0
> +                */
> +               ia_na_opt->t1 = 0;
> +               ia_na_opt->t2 = 0;
> +
> +               opt_len = sizeof(struct dhcp6_option_ia_na);
> +               break;
> +       case DHCP6_OPTION_ORO:
> +               oro_opt = (struct dhcp6_option_oro
> *)dhcp_option_start;
> +               oro_opt->req_option_code[num_oro++] =
> htons(DHCP6_OPTION_OPT_BOOTFILE_URL);
> +               oro_opt->req_option_code[num_oro++] =
> htons(DHCP6_OPTION_SOL_MAX_RT);
> +               if (IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION)) {
> +                       oro_opt->req_option_code[num_oro++] =
> +                               htons(DHCP6_OPTION_OPT_BOOTFILE_PARAM
> );
> +               }
> +
> +               opt_len = sizeof(__be16) * num_oro;
> +               break;
> +       case DHCP6_OPTION_CLIENT_ARCH_TYPE:
> +               client_arch_opt = (struct dhcp6_option_client_arch
> *)dhcp_option_start;
> +               client_arch_opt->arch_type[num_client_arch++] =
> htons(CONFIG_DHCP6_PXE_CLIENTARCH);
> +
> +               opt_len = sizeof(__be16) * num_client_arch;
> +               break;
> +       case DHCP6_OPTION_VENDOR_CLASS:
> +               vendor_class_opt = (struct dhcp6_option_vendor_class
> *)dhcp_option_start;
> +               vendor_class_opt->enterprise_number =
> htonl(CONFIG_DHCP6_ENTERPRISE_ID);
> +
> +               vci_strlen = strlen(DHCP6_VCI_STRING);
> +               vendor_class_opt-
> >vendor_class_data[num_vc_data].vendor_class_len =
> +                       htons(vci_strlen);
> +               memcpy(vendor_class_opt-
> >vendor_class_data[num_vc_data].opaque_data,
> +                      DHCP6_VCI_STRING, vci_strlen);
> +               num_vc_data++;
> +
> +               opt_len = sizeof(struct dhcp6_option_vendor_class) +
> +                         sizeof(struct vendor_class_data) *
> num_vc_data +
> +                         vci_strlen;
> +               break;
> +       case DHCP6_OPTION_NII:
> +               dhcp_option_start[0] = 1;
> +               dhcp_option_start[1] = 0;
> +               dhcp_option_start[2] = 0;
> +
> +               opt_len = 3;
> +               break;
> +       default:
> +               printf("***Warning unknown DHCP6 option %d.  Not
> adding to message\n", option_id);
> +               return 0;
> +       }
> +       dhcp_option->option_len = htons(opt_len);
> +
> +       return opt_len + sizeof(struct dhcp6_option_hdr);
> +}
> +
> +/**
> + * dhcp6_send_solicit_packet() - Send a SOLICIT packet
> + *
> + * Implements RFC 8415:
> + *    - 16.2. Solicit Message
> + *    - 18.2.1. Creation and Transmission of Solicit Messages
> + *
> + * Adds DHCP6 header and DHCP6 options.  Sends the UDP packet
> + * and sets the UDP handler.
> + */
> +static void dhcp6_send_solicit_packet(void)
> +{
> +       struct in6_addr dhcp_bcast_ip6;
> +       int len = 0;
> +       uchar *pkt;
> +       uchar *dhcp_pkt_start_ptr;
> +       struct dhcp6_hdr *dhcp_hdr;
> +
> +       pkt = net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE +
> UDP_HDR_SIZE;
> +       dhcp_pkt_start_ptr = pkt;
> +
> +       /* Add the DHCP6 header */
> +       dhcp_hdr = (struct dhcp6_hdr *)pkt;
> +       dhcp_hdr->msg_type = DHCP6_MSG_SOLICIT;
> +       dhcp_hdr->trans_id = htons(sm_params.trans_id);
> +       pkt += sizeof(struct dhcp6_hdr);
> +
> +       /* Add the options */
> +       pkt += dhcp6_add_option(DHCP6_OPTION_CLIENTID, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_ELAPSED_TIME, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_IA_NA, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_ORO, pkt);
> +       if (CONFIG_DHCP6_PXE_CLIENTARCH != 0xFF)
> +               pkt +=
> dhcp6_add_option(DHCP6_OPTION_CLIENT_ARCH_TYPE, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_VENDOR_CLASS, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_NII, pkt);
> +
> +       /* calculate packet length */
> +       len = pkt - dhcp_pkt_start_ptr;
> +
> +       /* send UDP packet to DHCP6 multicast address */
> +       string_to_ip6(DHCP6_MULTICAST_ADDR,
> sizeof(DHCP6_MULTICAST_ADDR), &dhcp_bcast_ip6);
> +       net_set_udp_handler(dhcp6_handler);
> +       net_send_udp_packet6((uchar *)net_bcast_ethaddr,
> &dhcp_bcast_ip6,
> +                            PORT_DHCP6_S, PORT_DHCP6_C, len);
> +}
> +
> +/**
> + * dhcp6_send_request_packet() - Send a REQUEST packet
> + *
> + *  * Implements RFC 8415:
> + *    - 16.4. Request Message
> + *    - 18.2.2. Creation and Transmission of Request Messages
> + *
> + * Adds DHCP6 header and DHCP6 options.  Sends the UDP packet
> + * and sets the UDP handler.
> + */
> +static void dhcp6_send_request_packet(void)
> +{
> +       struct in6_addr dhcp_bcast_ip6;
> +       int len = 0;
> +       uchar *pkt;
> +       uchar *dhcp_pkt_start_ptr;
> +       struct dhcp6_hdr *dhcp_hdr;
> +
> +       pkt = net_tx_packet + net_eth_hdr_size() + IP6_HDR_SIZE +
> UDP_HDR_SIZE;
> +       dhcp_pkt_start_ptr = pkt;
> +
> +       /* Add the DHCP6 header */
> +       dhcp_hdr = (struct dhcp6_hdr *)pkt;
> +       dhcp_hdr->msg_type = DHCP6_MSG_REQUEST;
> +       dhcp_hdr->trans_id = htons(sm_params.trans_id);
> +       pkt += sizeof(struct dhcp6_hdr);
> +
> +       /* add the options */
> +       pkt += dhcp6_add_option(DHCP6_OPTION_CLIENTID, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_ELAPSED_TIME, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_IA_NA, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_ORO, pkt);
> +       /* copy received IA_TA/IA_NA into the REQUEST packet */
> +       if (sm_params.server_uid.uid_ptr) {
> +               memcpy(pkt, sm_params.server_uid.uid_ptr,
> sm_params.server_uid.uid_size);
> +               pkt += sm_params.server_uid.uid_size;
> +       }
> +       if (CONFIG_DHCP6_PXE_CLIENTARCH != 0xFF)
> +               pkt +=
> dhcp6_add_option(DHCP6_OPTION_CLIENT_ARCH_TYPE, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_VENDOR_CLASS, pkt);
> +       pkt += dhcp6_add_option(DHCP6_OPTION_NII, pkt);
> +
> +       /* calculate packet length */
> +       len = pkt - dhcp_pkt_start_ptr;
> +
> +       /* send UDP packet to DHCP6 multicast address */
> +       string_to_ip6(DHCP6_MULTICAST_ADDR,
> strlen(DHCP6_MULTICAST_ADDR), &dhcp_bcast_ip6);
> +       net_set_udp_handler(dhcp6_handler);
> +       net_send_udp_packet6((uchar *)net_bcast_ethaddr,
> &dhcp_bcast_ip6,
> +                            PORT_DHCP6_S, PORT_DHCP6_C, len);
> +}
> +
> +static void dhcp6_parse_ia_options(struct dhcp6_option_hdr *ia_ptr,
> uchar *ia_option_ptr)
> +{
> +       struct dhcp6_option_hdr *ia_option_hdr;
> +
> +       ia_option_hdr = (struct dhcp6_option_hdr *)ia_option_ptr;
> +
> +       /* Search for options encapsulated in IA_NA/IA_TA
> (DHCP6_OPTION_IAADDR
> +        * or DHCP6_OPTION_STATUS_CODE)
> +        */
> +       while (ia_option_ptr < ((uchar *)ia_ptr + ntohs(ia_ptr-
> >option_len))) {
> +               switch (ntohs(ia_option_hdr->option_id)) {
> +               case DHCP6_OPTION_IAADDR:
> +                       sm_params.rx_status.ia_addr_found = true;
> +                       net_copy_ip6(&sm_params.rx_status.ia_addr_ipv
> 6,
> +                                    (ia_option_ptr + sizeof(struct
> dhcp6_hdr)));
> +                       debug("DHCP6_OPTION_IAADDR FOUND\n");
> +                       break;
> +               case DHCP6_OPTION_STATUS_CODE:
> +                       sm_params.rx_status.ia_status_code =
> +                               ntohs(*((u16 *)(ia_option_ptr +
> sizeof(struct dhcp6_hdr))));
> +                       printf("ERROR : IA STATUS %d\n",
> sm_params.rx_status.ia_status_code);
> +                       break;
> +               default:
> +                       debug("Unknown Option in IA, skipping\n");
> +                       break;
> +               }
> +
> +               ia_option_ptr += ntohs(((struct dhcp6_option_hdr
> *)ia_option_ptr)->option_len);
> +       }
> +}
> +
> +/**
> + * dhcp6_parse_options() - Parse the DHCP6 options
> + *
> + * @rx_pkt: pointer to beginning of received DHCP6 packet
> + * @len: Total length of the DHCP6 packet
> + *
> + * Parses the DHCP options from a received DHCP packet. Perform
> error checking
> + * on the options received.  Any relevant status is available in:
> + * "sm_params.rx_status"
> + *
> + */
> +static void dhcp6_parse_options(uchar *rx_pkt, unsigned int len)
> +{
> +       uchar *option_ptr;
> +       int sol_max_rt_sec, option_len;
> +       char *s, *e;
> +       struct dhcp6_option_hdr *option_hdr;
> +
> +       memset(&sm_params.rx_status, 0, sizeof(struct
> dhcp6_rx_pkt_status));
> +
> +       option_hdr = (struct dhcp6_option_hdr *)(rx_pkt +
> sizeof(struct dhcp6_hdr));
> +       /* check that required options exist */
> +       while (option_hdr < (struct dhcp6_option_hdr *)(rx_pkt +
> len)) {
> +               option_ptr = ((uchar *)option_hdr) + sizeof(struct
> dhcp6_hdr);
> +               option_len = ntohs(option_hdr->option_len);
> +
> +               switch (ntohs(option_hdr->option_id)) {
> +               case DHCP6_OPTION_CLIENTID:
> +                       if (memcmp(option_ptr, sm_params.duid,
> option_len)
> +                           != 0) {
> +                               debug("CLIENT ID DOESN'T MATCH\n");
> +                       } else {
> +                               debug("CLIENT ID FOUND and
> MATCHES\n");
> +                               sm_params.rx_status.client_id_match =
> true;
> +                       }
> +                       break;
> +               case DHCP6_OPTION_SERVERID:
> +                       sm_params.rx_status.server_id_found = true;
> +                       sm_params.rx_status.server_uid_ptr = (uchar
> *)option_hdr;
> +                       sm_params.rx_status.server_uid_size =
> option_len +
> +                                                             sizeof(
> struct dhcp6_option_hdr);
> +                       debug("SERVER ID FOUND\n");
> +                       break;
> +               case DHCP6_OPTION_IA_TA:
> +               case DHCP6_OPTION_IA_NA:
> +                       /* check the IA_ID */
> +                       if (*((u32 *)option_ptr)
> !=  htonl(sm_params.ia_id)) {
> +                               debug("IA_ID mismatch 0x%08x
> 0x%08x\n",
> +                                     *((u32 *)option_ptr),
> htonl(sm_params.ia_id));
> +                               break;
> +                       }
> +
> +                       if (ntohs(option_hdr->option_id) ==
> DHCP6_OPTION_IA_NA) {
> +                               /* skip past IA_ID/T1/T2 */
> +                               option_ptr += 3 * sizeof(u32);
> +                       } else if (ntohs(option_hdr->option_id) ==
> DHCP6_OPTION_IA_TA) {
> +                               /* skip past IA_ID */
> +                               option_ptr += sizeof(u32);
> +                       }
> +                       /* parse the IA_NA/IA_TA encapsulated options
> */
> +                       dhcp6_parse_ia_options(option_hdr,
> option_ptr);
> +                       break;
> +               case DHCP6_OPTION_STATUS_CODE:
> +                       debug("DHCP6_OPTION_STATUS_CODE FOUND\n");
> +                       sm_params.rx_status.status_code =
> ntohs(*((u16 *)option_ptr));
> +                       debug("DHCP6 top-level status code %d\n",
> sm_params.rx_status.status_code);
> +                       debug("DHCP6 status message: %.*s\n", len,
> option_ptr + 2);
> +                       break;
> +               case DHCP6_OPTION_SOL_MAX_RT:
> +                       debug("DHCP6_OPTION_SOL_MAX_RT FOUND\n");
> +                       sol_max_rt_sec = ntohl(*((u32 *)option_ptr));
> +
> +                       // A DHCP client MUST ignore any SOL_MAX_RT
> option values that are less
> +                       // than 60 or more than 86400
> +                       if (sol_max_rt_sec >= 60 && sol_max_rt_sec <=
> 86400) {
> +                               updated_sol_max_rt_ms =
> sol_max_rt_sec * 1000;
> +                               if (sm_params.curr_state ==
> DHCP6_SOLICIT)
> +                                       sm_params.mrt_ms =
> updated_sol_max_rt_ms;
> +                       }
> +                       break;
> +               case DHCP6_OPTION_OPT_BOOTFILE_URL:
> +                       debug("DHCP6_OPTION_OPT_BOOTFILE_URL
> FOUND\n");
> +                       copy_filename(net_boot_file_name, option_ptr,
> option_len + 1);
> +                       debug("net_boot_file_name: %s\n",
> net_boot_file_name);
> +
> +                       //copy server_ip6 (required for PXE)
> +                       s = strchr(net_boot_file_name, '[');
> +                       e = strchr(net_boot_file_name, ']');
> +                       if (s && e && e > s)
> +                               string_to_ip6(s + 1, e - s - 1,
> &net_server_ip6);
> +                       break;
> +#if IS_ENABLED(CONFIG_DHCP6_PXE_DHCP_OPTION)
> +               case DHCP6_OPTION_OPT_BOOTFILE_PARAM:
> +                       debug("DHCP6_OPTION_OPT_BOOTFILE_PARAM
> FOUND\n");
> +
> +                       if (pxelinux_configfile)
> +                               free(pxelinux_configfile);
> +
> +                       pxelinux_configfile = (char
> *)malloc((option_len + 1) * sizeof(char));
> +                       if (pxelinux_configfile) {
> +                               memcpy(pxelinux_configfile,
> option_ptr, option_len);
> +                               pxelinux_configfile[option_len] =
> '\0';
> +                       }
> +
> +                       debug("PXE CONFIG FILE %s\n",
> pxelinux_configfile);
> +                       break;
> +#endif
> +               case DHCP6_OPTION_PREFERENCE:
> +                       debug("DHCP6_OPTION_PREFERENCE FOUND\n");
> +                       sm_params.rx_status.preference = *option_ptr;
> +                       break;
> +               default:
> +                       debug("Unknown Option ID: %d, skipping
> parsing\n",
> +                             ntohs(option_hdr->option_id));
> +                       break;
> +               }
> +               /* Increment to next option header */
> +               option_hdr = (struct dhcp6_option_hdr *)(((uchar
> *)option_hdr) +
> +                            sizeof(struct dhcp6_option_hdr) +
> option_len);
> +       }
> +}
> +
> +/**
> + * dhcp6_check_advertise_packet() - Perform error checking on an
> expected
> + *                                  ADVERTISE packet.
> + *
> + * @rx_pkt: pointer to beginning of received DHCP6 packet
> + * @len: Total length of the DHCP6 packet
> + *
> + * Implements RFC 8415:
> + *    - 16.3.  Advertise Message
> + *    - 18.2.10.  Receipt of Reply Messages
> + *
> + * Return : 0 : ADVERTISE packet was received with no errors.
> + *              State machine can progress
> + *          1 : - packet received is not an ADVERTISE packet
> + *              - there were errors in the packet received,
> + *              - this is the first SOLICIT packet, but
> + *                received preference is not 255, so we have
> + *                to wait for more server responses.
> + */
> +static int dhcp6_check_advertise_packet(uchar *rx_pkt, unsigned int
> len)
> +{
> +       u16 rx_uid_size;
> +       struct dhcp6_hdr *dhcp6_hdr = (struct dhcp6_hdr *)rx_pkt;
> +
> +       /* Ignore message if msg-type != advertise */
> +       if (dhcp6_hdr->msg_type != DHCP6_MSG_ADVERTISE)
> +               return 1;
> +       /* Ignore message if transaction ID doesn't match */
> +       if (dhcp6_hdr->trans_id != htons(sm_params.trans_id))
> +               return 1;
> +
> +       dhcp6_parse_options(rx_pkt, len);
> +
> +       /* Ignore advertise if any of these conditions met */
> +       if (!sm_params.rx_status.server_id_found  ||
> +           !sm_params.rx_status.client_id_match  ||
> +           sm_params.rx_status.status_code != DHCP6_SUCCESS) {
> +               return 1;
> +       }
> +
> +       if (sm_params.rx_status.server_id_found) {
> +               /* if no server UID has been received yet, or if the
> server UID
> +                * received has a higher preference value than the
> currently saved
> +                * server UID, save the new server UID and preference
> +                */
> +               if (!sm_params.server_uid.uid_ptr ||
> +                   (sm_params.server_uid.uid_ptr &&
> +                   sm_params.server_uid.preference <
> sm_params.rx_status.preference)) {
> +                       rx_uid_size =
> sm_params.rx_status.server_uid_size;
> +                       if (sm_params.server_uid.uid_ptr)
> +                               free(sm_params.server_uid.uid_ptr);
> +                       sm_params.server_uid.uid_ptr =
> malloc(rx_uid_size * sizeof(uchar));
> +                       if (sm_params.server_uid.uid_ptr)
> +                               memcpy(sm_params.server_uid.uid_ptr,
> +                                      sm_params.rx_status.server_uid
> _ptr, rx_uid_size);
> +
> +                       sm_params.server_uid.uid_size = rx_uid_size;
> +                       sm_params.server_uid.preference =
> sm_params.rx_status.preference;
> +               }
> +
> +               /* If the first SOLICIT and preference code is 255,
> use right away.
> +                * Otherwise, wait for the first SOLICIT period for
> more
> +                * DHCP6 servers to respond.
> +                */
> +               if (sm_params.retry_cnt == 1 &&
> +                   sm_params.server_uid.preference != 255) {
> +                       debug("valid ADVERTISE, waiting for first
> SOLICIT period\n");
> +                       return 1;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * dhcp6_check_reply_packet() - Perform error checking on an
> expected
> + *                              REPLY packet.
> + *
> + * @rx_pkt: pointer to beginning of received DHCP6 packet
> + * @len: Total length of the DHCP6 packet
> + *
> + * Implements RFC 8415:
> + *    - 16.10. Reply Message
> + *    - 18.2.10. Receipt of Reply Messages
> + *
> + * Return : 0 - REPLY packet was received with no errors
> + *          1 - packet received is not an REPLY packet,
> + *              or there were errors in the packet received
> + */
> +static int dhcp6_check_reply_packet(uchar *rx_pkt, unsigned int len)
> +{
> +       struct dhcp6_hdr *dhcp6_hdr = (struct dhcp6_hdr *)rx_pkt;
> +
> +       //Ignore message if msg-type != reply
> +       if (dhcp6_hdr->msg_type != DHCP6_MSG_REPLY)
> +               return 1;
> +       //check that transaction ID matches
> +       if (dhcp6_hdr->trans_id != htons(sm_params.trans_id))
> +               return 1;
> +
> +       dhcp6_parse_options(rx_pkt, len);
> +
> +       //if no addresses found, restart DHCP
> +       if (!sm_params.rx_status.ia_addr_found ||
> +           sm_params.rx_status.ia_status_code ==
> DHCP6_NO_ADDRS_AVAIL ||
> +           sm_params.rx_status.status_code == DHCP6_NOT_ON_LINK) {
> +               //restart DHCP
> +               debug("No address found in reply.  Restarting
> DHCP\n");
> +               dhcp6_start();
> +       }
> +
> +       //ignore reply if any of these conditions met
> +       if (!sm_params.rx_status.server_id_found  ||
> +           !sm_params.rx_status.client_id_match ||
> +           sm_params.rx_status.status_code == DHCP6_UNSPEC_FAIL) {
> +               return 1;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * dhcp6_state_machine() - DHCP6 state machine
> + *
> + * @timeout: TRUE : timeout waiting for response from
> + *                  DHCP6 server
> + *           FALSE : init or received response from DHCP6 server
> + * @rx_pkt: Pointer to the beginning of received DHCP6 packet.
> + *          Will be NULL if called as part of init
> + *          or timeout==TRUE
> + * @len: Total length of the DHCP6 packet if rx_pkt != NULL
> + *
> + * Implements RFC 8415:
> + *    - 5.2.  Client/Server Exchanges Involving Four Messages
> + *    - 15.  Reliability of Client-Initiated Message Exchanges
> + *
> + * Handles:
> + *    - transmission of SOLICIT and REQUEST packets
> + *    - retransmission of SOLICIT and REQUEST packets if no
> + *      response is received within the timeout window
> + *    - checking received ADVERTISE and REPLY packets to
> + *      assess if the DHCP state machine can progress
> + */
> +static void dhcp6_state_machine(bool timeout, uchar *rx_pkt,
> unsigned int len)
> +{
> +       int rand_minus_plus_100;
> +
> +       switch (sm_params.curr_state) {
> +       case DHCP6_INIT:
> +               sm_params.next_state = DHCP6_SOLICIT;
> +               break;
> +       case DHCP6_SOLICIT:
> +               if (!timeout) {
> +                       /* check the rx packet and determine if we
> can transition to next
> +                        * state.
> +                        */
> +                       if (dhcp6_check_advertise_packet(rx_pkt,
> len))
> +                               return;
> +
> +                       debug("ADVERTISE good, transition to
> REQUEST\n");
> +                       sm_params.next_state = DHCP6_REQUEST;
> +               } else if (sm_params.retry_cnt == 1)  {
> +                       /* If a server UID was received in the first
> SOLICIT period
> +                        * transition to REQUEST
> +                        */
> +                       if (sm_params.server_uid.uid_ptr)
> +                               sm_params.next_state = DHCP6_REQUEST;
> +               }
> +               break;
> +       case DHCP6_REQUEST:
> +               if (!timeout) {
> +                       /* check the rx packet and determine if we
> can transition to next state */
> +                       if (dhcp6_check_reply_packet(rx_pkt, len))
> +                               return;
> +
> +                       debug("REPLY good, transition to DONE\n");
> +                       sm_params.next_state = DHCP6_DONE;
> +               }
> +               break;
> +       case DHCP6_DONE:
> +       case DHCP6_FAIL:
> +               /* Shouldn't get here, as state machine should exit
> +                * immediately when DHCP6_DONE or DHCP6_FAIL is
> entered.
> +                * Proceed anyway to proceed DONE/FAIL actions
> +                */
> +               debug("Unexpected DHCP6 state : %d\n",
> sm_params.curr_state);
> +               break;
> +       }
> +       /* re-seed the RNG */
> +       srand(get_ticks() + rand());
> +
> +       /* handle state machine entry conditions */
> +       if (sm_params.curr_state != sm_params.next_state) {
> +               sm_params.retry_cnt = 0;
> +
> +               if (sm_params.next_state == DHCP6_SOLICIT) {
> +                       /* delay a random ammount (special for
> SOLICIT) */
> +                       udelay((rand() % SOL_MAX_DELAY_MS) * 1000);
> +                       /* init timestamp variables after SOLICIT
> delay */
> +                       sm_params.dhcp6_start_ms = get_timer(0);
> +                       sm_params.dhcp6_retry_start_ms =
> sm_params.dhcp6_start_ms;
> +                       sm_params.dhcp6_retry_ms =
> sm_params.dhcp6_start_ms;
> +                       /* init transaction and ia_id */
> +                       sm_params.trans_id = rand() & 0xFFFFFF;
> +                       sm_params.ia_id = rand();
> +                       /* initialize retransmission parameters */
> +                       sm_params.irt_ms = SOL_TIMEOUT_MS;
> +                       sm_params.mrt_ms = updated_sol_max_rt_ms;
> +                       /* RFCs default MRC is be 0 (try infinitely)
> +                        * give up after TIMEOUT_COUNT number of
> tries (same as DHCPv4)
> +                        */
> +                       sm_params.mrc = TIMEOUT_COUNT;
> +                       sm_params.mrd_ms = 0;
> +
> +               } else if (sm_params.next_state == DHCP6_REQUEST) {
> +                       /* init timestamp variables  */
> +                       sm_params.dhcp6_retry_start_ms =
> get_timer(0);
> +                       sm_params.dhcp6_retry_ms =
> sm_params.dhcp6_start_ms;
> +                       /* initialize retransmission parameters */
> +                       sm_params.irt_ms = REQ_TIMEOUT_MS;
> +                       sm_params.mrt_ms = REQ_MAX_RT_MS;
> +                       sm_params.mrc = REQ_MAX_RC;
> +                       sm_params.mrd_ms = 0;
> +               }
> +       }
> +
> +       if (timeout)
> +               sm_params.dhcp6_retry_ms = get_timer(0);
> +
> +       /* Check if MRC or MRD have been passed */
> +       if ((sm_params.mrc != 0 &&
> +            sm_params.retry_cnt >= sm_params.mrc) ||
> +           (sm_params.mrd_ms != 0 &&
> +            ((sm_params.dhcp6_retry_ms -
> sm_params.dhcp6_retry_start_ms) >= sm_params.mrd_ms))) {
> +               sm_params.next_state = DHCP6_FAIL;
> +       }
> +
> +       /* calculate retransmission timeout (RT) */
> +       rand_minus_plus_100 = ((rand() % 200) - 100);
> +       if (sm_params.retry_cnt == 0) {
> +               sm_params.rt_ms = sm_params.irt_ms +
> +                                 ((sm_params.irt_ms *
> rand_minus_plus_100) / 1000);
> +       } else {
> +               sm_params.rt_ms = (2 * sm_params.rt_prev_ms) +
> +                                 ((sm_params.rt_prev_ms *
> rand_minus_plus_100) / 1000);
> +       }
> +
> +       if (sm_params.rt_ms > sm_params.mrt_ms) {
> +               sm_params.rt_ms = sm_params.mrt_ms +
> +                                 ((sm_params.mrt_ms *
> rand_minus_plus_100) / 1000);
> +       }
> +
> +       sm_params.rt_prev_ms = sm_params.rt_ms;
> +
> +       net_set_timeout_handler(sm_params.rt_ms,
> dhcp6_timeout_handler);
> +
> +       /* send transmit/retransmit message or fail */
> +       sm_params.curr_state = sm_params.next_state;
> +
> +       if (sm_params.curr_state == DHCP6_SOLICIT) {
> +               /* send solicit packet */
> +               dhcp6_send_solicit_packet();
> +               printf("DHCP6 SOLICIT %d\n", sm_params.retry_cnt);
> +       } else if (sm_params.curr_state == DHCP6_REQUEST) {
> +               /* send request packet */
> +               dhcp6_send_request_packet();
> +               printf("DHCP6 REQUEST %d\n", sm_params.retry_cnt);
> +       } else if (sm_params.curr_state == DHCP6_DONE) {
> +               net_set_timeout_handler(0, 0);
> +
> +               /* Duplicate address detection (DAD) should be
> +                * performed here before setting net_ip6
> +                * (enhancement should be considered)
> +                */
> +               net_copy_ip6(&net_ip6,
> &sm_params.rx_status.ia_addr_ipv6);
> +               printf("DHCP6 client bound to %pI6c\n", &net_ip6);
> +               /* will load with TFTP6 */
> +               net_auto_load();
> +       } else if (sm_params.curr_state == DHCP6_FAIL) {
> +               printf("DHCP6 FAILED, TERMINATING\n");
> +               net_set_state(NETLOOP_FAIL);
> +       }
> +       sm_params.retry_cnt++;
> +}
> +
> +/*
> + * Timeout for DHCP6 SOLICIT/REQUEST.
> + */
> +static void dhcp6_timeout_handler(void)
> +{
> +       /* call state machine with the timeout flag */
> +       dhcp6_state_machine(true, NULL, 0);
> +}
> +
> +/*
> + * Start or restart DHCP6
> + */
> +void dhcp6_start(void)
> +{
> +       memset(&sm_params, 0, sizeof(struct dhcp6_sm_params));
> +
> +       /* seed the RNG with MAC address */
> +       srand_mac();
> +
> +       sm_params.curr_state = DHCP6_INIT;
> +       dhcp6_state_machine(false, NULL, 0);
> +}
> diff --git a/net/dhcpv6.h b/net/dhcpv6.h
> new file mode 100644
> index 0000000000..da722feff7
> --- /dev/null
> +++ b/net/dhcpv6.h
> @@ -0,0 +1,212 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Copyright (C) Microsoft Corporation
> + * Author: Sean Edmond <seanedmond@microsoft.com>
> + *
> + */
> +
> +#ifndef __DHCP6_H__
> +#define __DHCP6_H__
> +
> +#include <net6.h>
> +#include <net.h>
> +
> +/* Message types */
> +#define DHCP6_MSG_SOLICIT      1
> +#define DHCP6_MSG_ADVERTISE    2
> +#define DHCP6_MSG_REQUEST      3
> +#define DHCP6_MSG_REPLY                7
> +
> +/* Option Codes */
> +#define DHCP6_OPTION_CLIENTID          1
> +#define DHCP6_OPTION_SERVERID          2
> +#define DHCP6_OPTION_IA_NA             3
> +#define DHCP6_OPTION_IA_TA             4
> +#define DHCP6_OPTION_IAADDR            5
> +#define DHCP6_OPTION_ORO               6
> +#define DHCP6_OPTION_PREFERENCE                7
> +#define DHCP6_OPTION_ELAPSED_TIME      8
> +#define DHCP6_OPTION_STATUS_CODE       13
> +#define DHCP6_OPTION_OPT_BOOTFILE_URL  59
> +#define DHCP6_OPTION_OPT_BOOTFILE_PARAM        60
> +#define DHCP6_OPTION_SOL_MAX_RT                82
> +#define DHCP6_OPTION_CLIENT_ARCH_TYPE  61
> +#define DHCP6_OPTION_VENDOR_CLASS      16
> +#define DHCP6_OPTION_NII               62
> +
> +/* DUID */
> +#define DUID_TYPE_LL           3
> +#define DUID_HW_TYPE_ENET      1
> +#define DUID_LL_SIZE           (sizeof(struct dhcp6_option_duid_ll)
> + ETH_ALEN)
> +#define DUID_MAX_SIZE          DUID_LL_SIZE /* only supports DUID-LL 
> currently */
> +
> +/* vendor-class-data to send in vendor clas option */
> +#define DHCP6_VCI_STRING       "U-boot"
> +
> +#define DHCP6_MULTICAST_ADDR   "ff02::1:2"     /* DHCP multicast
> address */
> +
> +/* DHCP6 States supported */
> +enum dhcp6_state {
> +       DHCP6_INIT,
> +       DHCP6_SOLICIT,
> +       DHCP6_REQUEST,
> +       DHCP6_DONE,
> +       DHCP6_FAIL,
> +};
> +
> +/* DHCP6 Status codes */
> +enum dhcp6_status {
> +       DHCP6_SUCCESS = 0,
> +       DHCP6_UNSPEC_FAIL = 1,
> +       DHCP6_NO_ADDRS_AVAIL = 2,
> +       DHCP6_NO_BINDING = 3,
> +       DHCP6_NOT_ON_LINK = 4,
> +       DHCP6_USE_MULTICAST = 5,
> +       DHCP6_NO_PREFIX_AVAIL = 6,
> +};
> +
> +/* DHCP6 message header format */
> +struct dhcp6_hdr {
> +       unsigned int msg_type : 8;      /* message type */
> +       unsigned int trans_id : 24;     /* transaction ID */
> +} __packed;
> +
> +/* DHCP6 option header format */
> +struct dhcp6_option_hdr {
> +       __be16  option_id;      /* option id */
> +       __be16  option_len;     /* Option length */
> +       u8      option_data[0]; /* Option data */
> +} __packed;
> +
> +/* DHCP6_OPTION_CLIENTID option (DUID-LL) */
> +struct dhcp6_option_duid_ll {
> +       __be16  duid_type;
> +       __be16  hw_type;
> +       u8      ll_addr[0];
> +} __packed;
> +
> +/* DHCP6_OPTION_ELAPSED_TIME option */
> +struct dhcp6_option_elapsed_time {
> +       __be16  elapsed_time;
> +} __packed;
> +
> +/* DHCP6_OPTION_IA_TA option */
> +struct dhcp6_option_ia_ta {
> +       __be32  iaid;
> +       u8      ia_ta_options[0];
> +} __packed;
> +
> +/* DHCP6_OPTION_IA_NA option */
> +struct dhcp6_option_ia_na {
> +       __be32  iaid;
> +       __be32  t1;
> +       __be32  t2;
> +       u8      ia_na_options[0];
> +} __packed;
> +
> +/* OPTION_ORO option */
> +struct dhcp6_option_oro  {
> +       __be16  req_option_code[0];
> +} __packed;
> +
> +/* DHCP6_OPTION_CLIENT_ARCH_TYPE option */
> +struct dhcp6_option_client_arch {
> +       __be16  arch_type[0];
> +} __packed;
> +
> +/* vendor-class-data inside OPTION_VENDOR_CLASS option */
> +struct vendor_class_data {
> +       __be16  vendor_class_len;
> +       u8      opaque_data[0];
> +} __packed;
> +
> +/* DHCP6_OPTION_VENDOR_CLASS option */
> +struct dhcp6_option_vendor_class {
> +       __be32                          enterprise_number;
> +       struct vendor_class_data        vendor_class_data[0];
> +} __packed;
> +
> +/**
> + * struct dhcp6_rx_pkt_status - Structure that holds status
> + *                              from a received message
> + * @client_id_match: Client ID was found and matches DUID sent
> + * @server_id_found: Server ID was found in the message
> + * @server_uid_ptr: Pointer to received server ID
> + * @server_uid_size: Size of received server ID
> + * @ia_addr_found: IA addr option was found in received message
> + * @ia_addr_ipv6: The IPv6 address received in IA
> + * @ia_status_code: Status code received in the IA
> + * @status_code: Top-level status code received
> + * @preference: Preference code received
> + */
> +struct dhcp6_rx_pkt_status {
> +       bool                    client_id_match;
> +       bool                    server_id_found;
> +       uchar                   *server_uid_ptr;
> +       u16                     server_uid_size;
> +       bool                    ia_addr_found;
> +       struct in6_addr         ia_addr_ipv6;
> +       enum dhcp6_status       ia_status_code;
> +       enum dhcp6_status       status_code;
> +       u8                      preference;
> +};
> +
> +/**
> + * struct dhcp6_server_uid - Structure that holds the server UID
> + *                             received from an ADVERTISE and saved
> + *                             given the server selection criteria.
> + * @uid_ptr: Dynamically allocated and copied server UID
> + * @uid_size: Size of the server UID in uid_ptr (in bytes)
> + * @preference: Preference code associated with this server UID
> + */
> +struct dhcp6_server_uid {
> +       uchar   *uid_ptr;
> +       u16     uid_size;
> +       u8      preference;
> +};
> +
> +/**
> + * struct dhcp6_rx_pkt_status - Structure that holds DHCP6
> + *                              state machine parameters
> + * @curr_state: current DHCP6 state
> + * @next_state: next DHCP6 state
> + * @dhcp6_start_ms: timestamp DHCP6 start
> + * @dhcp6_retry_start_ms: timestamp of current TX message start
> + * @dhcp6_retry_ms: timestamp of last retransmission
> + * @retry_cnt: retry count
> + * @trans_id: transaction ID
> + * @ia_id: transmitted IA ID
> + * @irt_ms: Initial retransmission time (in ms)
> + * @mrt_ms: Maximum retransmission time (in ms)
> + * @mrc: Maximum retransmission count
> + * @mrd_ms: Maximum retransmission duration (in ms)
> + * @rt_ms: retransmission timeout (is ms)
> + * @rt_prev_ms: previous retransmission timeout
> + * @rx_status: Status from received message
> + * @server_uid: Saved Server UID for selected server
> + * @duid: pointer to transmitted Client DUID
> + */
> +struct dhcp6_sm_params {
> +       enum dhcp6_state                curr_state;
> +       enum dhcp6_state                next_state;
> +       ulong                           dhcp6_start_ms;
> +       ulong                           dhcp6_retry_start_ms;
> +       ulong                           dhcp6_retry_ms;
> +       u32                             retry_cnt;
> +       u32                             trans_id;
> +       u32                             ia_id;
> +       int                             irt_ms;
> +       int                             mrt_ms;
> +       int                             mrc;
> +       int                             mrd_ms;
> +       int                             rt_ms;
> +       int                             rt_prev_ms;
> +       struct dhcp6_rx_pkt_status      rx_status;
> +       struct dhcp6_server_uid         server_uid;
> +       char                            duid[DUID_MAX_SIZE];
> +};
> +
> +/* Send a DHCPv6 request */
> +void dhcp6_start(void);
> +
> +#endif /* __DHCP6_H__ */
> diff --git a/net/net.c b/net/net.c
> index c9a749f6cc..73d5b2bc80 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -122,6 +122,9 @@
>  #endif
>  #include <net/tcp.h>
>  #include <net/wget.h>
> +#if defined(CONFIG_CMD_DHCP6)
> +#include "dhcpv6.h"
> +#endif
> 
>  /** BOOTP EXTENTIONS **/
> 
> @@ -135,6 +138,10 @@ struct in_addr net_dns_server;
>  /* Our 2nd DNS IP address */
>  struct in_addr net_dns_server2;
>  #endif
> +#if defined(CONFIG_DHCP6_PXE_DHCP_OPTION)
> +/* Indicates whether the pxe path prefix / config file was specified
> in dhcp option */
> +char *pxelinux_configfile;
> +#endif
> 
>  /** END OF BOOTP EXTENTIONS **/
> 
> @@ -510,6 +517,11 @@ restart:
>                         dhcp_request();         /* Basically same as
> BOOTP */
>                         break;
>  #endif
> +#if defined(CONFIG_CMD_DHCP6)
> +               case DHCP6:
> +                       dhcp6_start();
> +                       break;
> +#endif
>  #if defined(CONFIG_CMD_BOOTP)
>                 case BOOTP:
>                         bootp_reset();
> --
> 2.39.0
> 
> 
Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH 2/2] net: dhcp6: pxe: Add DHCP/PXE commands for IPv6
  2023-02-02  0:26 ` [PATCH 2/2] net: dhcp6: pxe: Add DHCP/PXE commands for IPv6 seanedmond
@ 2023-03-05 11:44   ` Vyacheslav V. Mitrofanov
  0 siblings, 0 replies; 8+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-03-05 11:44 UTC (permalink / raw)
  To: u-boot, seanedmond; +Cc: sjg, seanedmond, joe.hershberger, rfried.dev

> From: Sean Edmond <seanedmond@microsoft.com>
> 
> Adds commands to support DHCP and PXE with IPv6.
> 
> New commands added (when IPv6 is enabled):
> - dhcp6
> - pxe get -ipv6
> - pxe boot -ipv6
> 
> Signed-off-by: Sean Edmond <seanedmond@microsoft.com>
> ---
>  boot/bootmeth_distro.c |  2 +-
>  boot/bootmeth_pxe.c    |  4 +-
>  boot/pxe_utils.c       |  3 +-
>  cmd/net.c              | 22 +++++++++++
>  cmd/pxe.c              | 86 +++++++++++++++++++++++++++++++++++++---
> --
>  cmd/sysboot.c          |  2 +-
>  include/net.h          |  2 +
>  include/net6.h         |  2 -
>  include/pxe_utils.h    | 10 ++++-
>  9 files changed, 115 insertions(+), 18 deletions(-)
> 
> diff --git a/boot/bootmeth_distro.c b/boot/bootmeth_distro.c
> index 356929828b..b4b73ecbf5 100644
> --- a/boot/bootmeth_distro.c
> +++ b/boot/bootmeth_distro.c
> @@ -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);
> 
> diff --git a/boot/bootmeth_pxe.c b/boot/bootmeth_pxe.c
> index ecf8557af8..5a8af2bbd0 100644
> --- a/boot/bootmeth_pxe.c
> +++ b/boot/bootmeth_pxe.c
> @@ -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);
> 
> diff --git a/boot/pxe_utils.c b/boot/pxe_utils.c
> index 3a1e50f2b1..d13c47dd94 100644
> --- a/boot/pxe_utils.c
> +++ b/boot/pxe_utils.c
> @@ -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)
> diff --git a/cmd/net.c b/cmd/net.c
> index 4227321871..88d53d14d5 100644
> --- a/cmd/net.c
> +++ b/cmd/net.c
> @@ -111,6 +111,28 @@ 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",
> +          "[loadAddress] [[hostIPaddr:]bootfilename]");
> +#endif
> +
>  #if defined(CONFIG_CMD_DHCP)
>  static int do_dhcp(struct cmd_tbl *cmdtp, int flag, int argc,
>                    char *const argv[])
> diff --git a/cmd/pxe.c b/cmd/pxe.c
> index db8e4697f2..ebc44fd661 100644
> --- a/cmd/pxe.c
> +++ b/cmd/pxe.c
> @@ -11,6 +11,10 @@
> 
>  #include "pxe_utils.h"
> 
> +#if IS_ENABLED(CONFIG_IPV6)
> +#include <net6.h>
> +#endif
> +
>  #ifdef CONFIG_CMD_NET
>  const char *pxe_default_paths[] = {
>  #ifdef CONFIG_SYS_SOC
> @@ -29,12 +33,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);
> @@ -43,6 +55,22 @@ static int do_get_tftp(struct pxe_context *ctx,
> const char *file_path,
>         return 1;
>  }
> 
> +#if defined(CONFIG_DHCP6_PXE_DHCP_OPTION)
> +/*
> + * Looks for a pxe file with specified config file name,
> + * which is received from DHCP option 209.
> + *
> + * Returns 1 on success or < 0 on error.
> + */
> +static inline 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;
> +}
> +#endif
>  /*
>   * Looks for a pxe file with a name based on the pxeuuid environment
> variable.
>   *
> @@ -105,15 +133,25 @@ 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 defined(CONFIG_DHCP6_PXE_DHCP_OPTION)
> +       if (pxelinux_configfile && use_ipv6) {
> +               if (pxe_dhcp_option_path(&ctx, pxefile_addr_r) > 0)
> +                       goto done;
> +
> +               goto error_exit;
> +       }
> +#endif
> +
>         /*
>          * Keep trying paths until we successfully get a file we're
> looking
>          * for.
> @@ -131,6 +169,7 @@ int pxe_get(ulong pxefile_addr_r, char
> **bootdirp, ulong *sizep)
>                 i++;
>         }
> 
> +error_exit:
>         pxe_destroy_ctx(&ctx);
> 
>         return -ENOENT;
> @@ -169,9 +208,17 @@ 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 (argc != 1 && argc != 2)
> +                       return CMD_RET_USAGE;
> +               if (!strcmp(argv[argc - 1], USE_IP6_CMD_PARAM))
> +                       use_ipv6 = true;
> +       } else {
> +               if (argc != 1)
> +                       return CMD_RET_USAGE;
> +       }
> 
>         pxefile_addr_str = from_env("pxefile_addr_r");
> 
> @@ -183,7 +230,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 +258,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 +282,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 +297,13 @@ do_pxe_boot(struct cmd_tbl *cmdtp, int flag, int
> argc, char *const argv[])
>  }
> 
>  static struct cmd_tbl cmd_pxe_sub[] = {
> +#if IS_ENABLED(CONFIG_IPV6)
> +       U_BOOT_CMD_MKENT(get, 2, 1, do_pxe_get, "", ""),
> +       U_BOOT_CMD_MKENT(boot, 3, 1, do_pxe_boot, "", "")
> +#else
>         U_BOOT_CMD_MKENT(get, 1, 1, do_pxe_get, "", ""),
>         U_BOOT_CMD_MKENT(boot, 2, 1, do_pxe_boot, "", "")
> +#endif
>  };
> 
>  static void __maybe_unused pxe_reloc(void)
> @@ -281,9 +339,19 @@ static int do_pxe(struct cmd_tbl *cmdtp, int
> flag, int argc, char *const argv[])
>         return CMD_RET_USAGE;
>  }
> 
> +#if IS_ENABLED(CONFIG_IPV6)
> +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"
> +);
> +#else
>  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"
>  );
>  #endif
> +
> +#endif /* CONFIG_CMD_NET */
> diff --git a/cmd/sysboot.c b/cmd/sysboot.c
> index 04c0702026..63a7806deb 100644
> --- a/cmd/sysboot.c
> +++ b/cmd/sysboot.c
> @@ -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;
>         }
> diff --git a/include/net.h b/include/net.h
> index cac818e292..7463b1285b 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -49,6 +49,8 @@ struct udevice;
>   */
>  #define ARP_HLEN_ASCII (ARP_HLEN * 2) + (ARP_HLEN - 1)
> 
> +#define USE_IP6_CMD_PARAM      "-ipv6"
> +
>  /* IPv4 addresses are always 32 bits in size */
>  struct in_addr {
>         __be32 s_addr;
> diff --git a/include/net6.h b/include/net6.h
> index 9b3de028e6..571c0593e5 100644
> --- a/include/net6.h
> +++ b/include/net6.h
> @@ -39,8 +39,6 @@ struct in6_addr {
>  #define IPV6_ADDRSCOPE_ORG     0x08
>  #define IPV6_ADDRSCOPE_GLOBAL  0x0E
> 
> -#define USE_IP6_CMD_PARAM      "-ipv6"
> -
>  /**
>   * struct ipv6hdr - Internet Protocol V6 (IPv6) header.
>   *
> diff --git a/include/pxe_utils.h b/include/pxe_utils.h
> index 1e5e8424f5..9f19593048 100644
> --- a/include/pxe_utils.h
> +++ b/include/pxe_utils.h
> @@ -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 */
> --
> 2.39.0
> 
Tested on Si-Five Hi-Five Unmatched board (RISC-V)
Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2023-03-05 11:44 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-02-02  0:26 [PATCH 0/2] net: DHCPv6 protocol and commands seanedmond
2023-02-02  0:26 ` [PATCH 1/2] net: dhcp6: Add DHCPv6 (DHCP for IPv6) seanedmond
2023-02-02 17:12   ` Simon Glass
2023-02-03 22:12     ` Tom Rini
2023-03-05 11:41   ` Vyacheslav V. Mitrofanov
2023-02-02  0:26 ` [PATCH 2/2] net: dhcp6: pxe: Add DHCP/PXE commands for IPv6 seanedmond
2023-03-05 11:44   ` Vyacheslav V. Mitrofanov
2023-03-05 11:39 ` [PATCH 0/2] net: DHCPv6 protocol and commands Vyacheslav V. Mitrofanov

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.