u-boot.lists.denx.de archive mirror
 help / color / mirror / Atom feed
* [PATCH] net: ipv6: Add support for default gateway discovery.
@ 2023-03-02 16:58 emohandesi
  2023-03-16  8:47 ` Vyacheslav V. Mitrofanov
                   ` (4 more replies)
  0 siblings, 5 replies; 35+ messages in thread
From: emohandesi @ 2023-03-02 16:58 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, sjg, xypron.glpk, ilias.apalodimas,
	masahisa.kojima, john, emohandesi, v.v.mitrofanov, saproj,
	dphadke

From: Ehsan Mohandesi <emohandesi@microsoft.com>

In IPv6, the default gateway and prefix length are determined by receiving
a router advertisement as defined in -
https://www.rfc-editor.org/rfc/rfc4861.

Add support for sending router solicitation (RS) and processing router
advertisements (RA).

If the RA has prefix info option and following conditions are met, then
gatewayip6 and net_prefix_length of ip6addr env variables are initialized.
These are later consumed by IPv6 code for non-local destination IP.

- "Router Lifetime" != 0
- Prefix is NOT link-local prefix (0xfe80::/10)
- L flag is 1
- "Valid Lifetime" != 0

Timing Parameters:
- MAX_RTR_SOLICITATION_DELAY (0-1s)
- RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
- MAX_RTR_SOLICITATIONS (3 RS transmissions)

The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and invoked
automatically from net_init_loop().

Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>

Conflicts:
	cmd/Kconfig
	include/net.h
	net/net.c
---
 cmd/Kconfig     |   7 ++
 include/ndisc.h |  23 ++++++
 include/net.h   |   2 +-
 include/net6.h  |  40 ++++++++++
 net/ndisc.c     | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 net/net.c       |  23 +++++-
 net/net6.c      |   1 +
 7 files changed, 327 insertions(+), 12 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 2caa4af..c46613e 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1894,6 +1894,13 @@ config CMD_NCSI
 	  Normally this happens automatically before other network
 	  operations.
 
+config IPV6_ROUTER_DISCOVERY
+	bool "Do router discovery"
+	depends on IPV6
+	help
+	  Will automatically perform router solicitation on first IPv6
+	  network operation
+
 endif
 
 config CMD_ETHSW
diff --git a/include/ndisc.h b/include/ndisc.h
index f6f8eb6..362d707 100644
--- a/include/ndisc.h
+++ b/include/ndisc.h
@@ -19,6 +19,20 @@ struct nd_msg {
 	__u8		opt[0];
 };
 
+/* struct rs_msg - ICMPv6 Router Solicitation message format */
+struct rs_msg {
+	struct icmp6hdr	icmph;
+	__u8		opt[0];
+};
+
+/* struct ra_msg - ICMPv6 Router Advertisement message format */
+struct ra_msg {
+	struct icmp6hdr	icmph;
+	__u32	reachable_time;
+	__u32	retransmission_timer;
+	__u8	opt[0];
+};
+
 /* struct echo_msg - ICMPv6 echo request/reply message format */
 struct echo_msg {
 	struct icmp6hdr	icmph;
@@ -57,6 +71,11 @@ extern int net_nd_try;
  */
 void ndisc_init(void);
 
+/*
+ * ip6_send_rs() - Send IPv6 Router Solicitation Message
+ */
+void ip6_send_rs(void);
+
 /**
  * ndisc_receive() - Handle ND packet
  *
@@ -97,6 +116,10 @@ static inline int ndisc_timeout_check(void)
 {
 	return 0;
 }
+
+void ip6_send_rs(void)
+{
+}
 #endif
 
 #endif /* __NDISC_H__ */
diff --git a/include/net.h b/include/net.h
index 399af5e..25c43b3 100644
--- a/include/net.h
+++ b/include/net.h
@@ -505,7 +505,7 @@ 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
+	SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET, RS
 };
 
 extern char	net_boot_file_name[1024];/* Boot File name */
diff --git a/include/net6.h b/include/net6.h
index 2d7c5a0..beafc05 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -81,8 +81,17 @@ struct udp_hdr {
 			  0x00, 0x00, 0x00, 0x00, \
 			  0x00, 0x00, 0x00, 0x00, \
 			  0x00, 0x00, 0x00, 0x00 } } }
+/*
+ * All-routers multicast address is the link-local scope address to reach all
+ * routers.
+ */
+#define ALL_ROUTERS_MULT_ADDR { { { 0xFF, 0x02, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x02 } } }
 
 #define IPV6_LINK_LOCAL_PREFIX	0xfe80
+#define IPV6_LINK_LOCAL_MASK	0xffb0 /* The first 10-bit of address mask. */
 
 /* hop limit for neighbour discovery packets */
 #define IPV6_NDISC_HOPLIMIT             255
@@ -166,6 +175,37 @@ struct icmp6hdr {
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 } __packed;
 
+/*
+ * struct icmp6_ra_prefix_info - Prefix Information option of the ICMPv6 message
+ * The Prefix Information option provides hosts with on-link prefixes and
+ * prefixes for Address Autoconfiguration. Refer to RFC 4861 for more info.
+ */
+struct icmp6_ra_prefix_info {
+	u8	type;		/* Type is 3 for Prefix Information. */
+	u8	len;		/* Len is 4 for Prefix Information. */
+	/* The number of leading bits in the Prefix that are valid. */
+	u8	prefix_len;
+	u8	reserved1:6,	/* MUST be ignored by the receiver. */
+		aac:1,		/* autonomous address-configuration flag */
+	/* Indicates that this prefix can be used for on-link determination. */
+		on_link:1;
+	/*
+	 * The length of time in seconds that the prefix is valid for the
+	 * purpose of on-link determination.
+	 */
+	__be32	valid_lifetime;
+	/* The length of time addresses remain preferred. */
+	__be32	preferred_lifetime;
+	__be32	reserved2;	/* MUST be ignored by the receiver. */
+	/*
+	 * Prefix is an IP address or a prefix of an IP address. The Prefix
+	 * Length field contains the number of valid leading bits in the prefix.
+	 * The bits in the prefix after the prefix length are reserved and MUST
+	 * be initialized to zero by the sender and ignored by the receiver.
+	 */
+	struct in6_addr prefix;
+};
+
 extern struct in6_addr const net_null_addr_ip6;	/* NULL IPv6 address */
 extern struct in6_addr net_gateway6;	/* Our gateways IPv6 address */
 extern struct in6_addr net_ip6;	/* Our IPv6 addr (0 = unknown) */
diff --git a/net/ndisc.c b/net/ndisc.c
index 367dae7..db76c4b 100644
--- a/net/ndisc.c
+++ b/net/ndisc.c
@@ -13,6 +13,8 @@
 #include <net.h>
 #include <net6.h>
 #include <ndisc.h>
+#include <stdlib.h>
+#include <linux/delay.h>
 
 /* IPv6 destination address of packet waiting for ND */
 struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
@@ -29,31 +31,37 @@ int net_nd_tx_packet_size;
 ulong net_nd_timer_start;
 /* the number of requests we have sent so far */
 int net_nd_try;
+struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR;
+
+#define MAX_RTR_SOLICITATIONS		3
+/* The maximum time to delay sending the first router solicitation message. */
+#define MAX_SOLICITATION_DELAY		1 // 1 second
+/* The time to wait before sending the next router solicitation message. */
+#define RTR_SOLICITATION_INTERVAL	4000 // 4 seconds
 
 #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7)
 
 /**
  * ndisc_insert_option() - Insert an option into a neighbor discovery packet
  *
- * @ndisc:	pointer to ND packet
+ * @opt:	pointer to the option element of the neighbor discovery packet
  * @type:	option type to insert
  * @data:	option data to insert
  * @len:	data length
  * Return: the number of bytes inserted (which may be >= len)
  */
-static int
-ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
+static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int len)
 {
 	int space = IP6_NDISC_OPT_SPACE(len);
 
-	ndisc->opt[0] = type;
-	ndisc->opt[1] = space >> 3;
-	memcpy(&ndisc->opt[2], data, len);
+	opt[0] = type;
+	opt[1] = space >> 3;
+	memcpy(&opt[2], data, len);
 	len += 2;
 
 	/* fill the remainder with 0 */
 	if (space - len > 0)
-		memset(&ndisc->opt[len], '\0', space - len);
+		memset(&opt[len], '\0', space - len);
 
 	return space;
 }
@@ -123,7 +131,7 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
 
 	/* Set the target address and llsaddr option */
 	net_copy_ip6(&msg->target, neigh_addr);
-	ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+	ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
 			    INETHADDRSZ);
 
 	/* checksum */
@@ -137,6 +145,76 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
 	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
 }
 
+/*
+ * ip6_send_rs() - Send IPv6 Router Solicitation Message.
+ *
+ * A router solicitation is sent to discover a router. RS message creation is
+ * based on RFC 4861 section 4.1. Router Solicitation Message Format.
+ */
+void ip6_send_rs(void)
+{
+	unsigned char enetaddr[6];
+	struct rs_msg *msg;
+	__u16 icmp_len;
+	uchar *pkt;
+	unsigned short csum;
+	unsigned int pcsum;
+	static unsigned int retry_count;
+
+	if (!ip6_is_unspecified_addr(&net_gateway6) &&
+	    net_prefix_length != 0) {
+		net_set_state(NETLOOP_SUCCESS);
+		return;
+	} else if (retry_count >= MAX_RTR_SOLICITATIONS) {
+		net_set_state(NETLOOP_FAIL);
+		net_set_timeout_handler(0, 0);
+		retry_count = 0;
+		return;
+	}
+
+	printf("ROUTER SOLICITATION %d\n", retry_count + 1);
+
+	ip6_make_mult_ethdstaddr(enetaddr, &all_routers);
+	/*
+	 * ICMP length is the size of ICMP header (8) + one option (8) = 16.
+	 * The option is 2 bytes of type and length + 6 bytes for MAC.
+	 */
+	icmp_len = sizeof(struct icmp6hdr) + IP6_NDISC_OPT_SPACE(INETHADDRSZ);
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers, PROT_ICMPV6,
+			   IPV6_NDISC_HOPLIMIT, icmp_len);
+
+	/* ICMPv6 - RS */
+	msg = (struct rs_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION;
+	msg->icmph.icmp6_code = 0;
+	memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
+	memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
+
+	/* Set the llsaddr option */
+	ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+			    INETHADDRSZ);
+
+	/* checksum */
+	pcsum = csum_partial((__u8 *)msg, icmp_len, 0);
+	csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers,
+			       icmp_len, PROT_ICMPV6, pcsum);
+	msg->icmph.icmp6_cksum = csum;
+	pkt += icmp_len;
+
+	/* Wait up to 1 second if it is the first try to get the RA */
+	if (retry_count == 0)
+		udelay(((unsigned int)rand() % 1000000) * MAX_SOLICITATION_DELAY);
+
+	/* send it! */
+	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
+
+	retry_count++;
+	net_set_timeout_handler(RTR_SOLICITATION_INTERVAL, ip6_send_rs);
+}
+
 static void
 ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
 	    struct in6_addr *target)
@@ -167,7 +245,7 @@ ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
 	msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
 	/* Set the target address and lltargetaddr option */
 	net_copy_ip6(&msg->target, target);
-	ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
+	ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
 			    INETHADDRSZ);
 
 	/* checksum */
@@ -223,6 +301,10 @@ int ndisc_timeout_check(void)
 	return 1;
 }
 
+/*
+ * ndisc_init() - Make initial steps for ND state machine.
+ * Usually move variables into initial state.
+ */
 void ndisc_init(void)
 {
 	net_nd_packet_mac = NULL;
@@ -234,12 +316,125 @@ void ndisc_init(void)
 	net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
 }
 
+/*
+ * validate_ra() - Validate the router advertisement message.
+ *
+ * @ip6: Pointer to the router advertisement packet
+ * @len: Length of the router advertisement packet
+ *
+ * Check if the router advertisement message is valid. Conditions are
+ * according to RFC 4861 section 6.1.2. Validation of Router Advertisement
+ * Messages.
+ *
+ * Return: true if the message is valid and false if it is invalid.
+ */
+static bool validate_ra(struct ip6_hdr *ip6, int len)
+{
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+
+	/* ICMP length (derived from the IP length) should be 16 or more octets. */
+	if (ip6->payload_len < 16)
+		return false;
+
+	/* Source IP Address should be a valid link-local address. */
+	if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK) !=
+	    IPV6_LINK_LOCAL_PREFIX)
+		return false;
+
+	/*
+	 * The IP Hop Limit field should have a value of 255, i.e., the packet
+	 * could not possibly have been forwarded by a router.
+	 */
+	if (ip6->hop_limit != 255)
+		return false;
+
+	/* ICMP checksum has already been checked in net_ip6_handler. */
+
+	if (icmp->icmp6_code != 0)
+		return false;
+
+	return true;
+}
+
+/*
+ * process_ra() - Process the router advertisement packet.
+ *
+ * @ip6: Pointer to the router advertisement packet
+ * @len: Length of the router advertisement packet
+ *
+ * Process the received router advertisement message.
+ * Although RFC 4861 requires retaining at least two router addresses, we only
+ * keep one because of the U-Boot limitations and its goal of lightweight code.
+ *
+ * Return: 0 - RA is a default router and contains valid prefix information.
+ * Non-zero - RA options are invalid or do not indicate it is a default router
+ * or do not contain valid prefix information.
+ */
+static int process_ra(struct ip6_hdr *ip6, int len)
+{
+	/* Pointer to the ICMP section of the packet */
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	struct ra_msg *msg = (struct ra_msg *)icmp;
+	int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct ra_msg);
+	unsigned short int option_len;	/* Length of each option */
+	/* Pointer to the ICMPv6 message options */
+	unsigned char *option = NULL;
+	/* 8-bit identifier of the type of ICMPv6 option */
+	unsigned char type = 0;
+	struct icmp6_ra_prefix_info *prefix = NULL;
+
+	/* Ignore the packet if router lifetime is 0. */
+	if (!icmp->icmp6_rt_lifetime)
+		return -EOPNOTSUPP;
+
+	/* Processing the options */
+	option = msg->opt;
+	while (remaining_option_len > 0) {
+		/* The 2nd byte of the option is its length. */
+		option_len = option[1];
+		/* All included options should have a positive length. */
+		if (option_len == 0)
+			return -EINVAL;
+
+		type = option[0];
+		/* All option types except Prefix Information are ignored. */
+		switch (type) {
+		case ND_OPT_SOURCE_LL_ADDR:
+		case ND_OPT_TARGET_LL_ADDR:
+		case ND_OPT_REDIRECT_HDR:
+		case ND_OPT_MTU:
+			break;
+		case ND_OPT_PREFIX_INFO:
+			prefix = (struct icmp6_ra_prefix_info *)option;
+			/* The link-local prefix 0xfe80::/10 is ignored. */
+			if ((ntohs(prefix->prefix.s6_addr16[0]) &
+			     IPV6_LINK_LOCAL_MASK) == IPV6_LINK_LOCAL_PREFIX)
+				break;
+			if (prefix->on_link && ntohl(prefix->valid_lifetime)) {
+				net_prefix_length = prefix->prefix_len;
+				net_gateway6 = ip6->saddr;
+				return 0;
+			}
+			break;
+		default:
+			debug("Unknown IPv6 Neighbor Discovery Option 0x%x\n",
+			      type);
+		}
+
+		option_len <<= 3; /* Option length is a multiple of 8. */
+		remaining_option_len -= option_len;
+		option += option_len;
+	}
+	return -EADDRNOTAVAIL;
+}
+
 int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 {
 	struct icmp6hdr *icmp =
 	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
 	struct nd_msg *ndisc = (struct nd_msg *)icmp;
 	uchar neigh_eth_addr[6];
+	int err = 0;	// The error code returned calling functions.
 
 	switch (icmp->icmp6_type) {
 	case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
@@ -280,6 +475,36 @@ int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 			net_nd_packet_mac = NULL;
 		}
 		break;
+	case IPV6_NDISC_ROUTER_SOLICITATION:
+		break;
+	case IPV6_NDISC_ROUTER_ADVERTISEMENT:
+		debug("Received router advertisement for %pI6c from %pI6c\n",
+		      &ip6->daddr, &ip6->saddr);
+		/*
+		 * If gateway and prefix are set, the RA packet is ignored. The
+		 * reason is that the U-Boot code is supposed to be as compact
+		 * as possible and does not need to take care of multiple
+		 * routers. In addition to that, U-Boot does not want to handle
+		 * scenarios like a router setting its lifetime to zero to
+		 * indicate it is not routing anymore. U-Boot program has a
+		 * short life when the system boots up and does not need such
+		 * sophistication.
+		 */
+		if (!ip6_is_unspecified_addr(&net_gateway6) &&
+		    net_prefix_length != 0) {
+			break;
+		}
+		if (!validate_ra(ip6, len)) {
+			debug("Invalid router advertisement message.\n");
+			break;
+		}
+		err = process_ra(ip6, len);
+		if (err)
+			debug("Ignored router advertisement. Error: %d\n", err);
+		else
+			printf("Set gatewayip6: %pI6c, prefix_length: %d\n",
+			       &net_gateway6, net_prefix_length);
+		break;
 	default:
 		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
 		return -1;
diff --git a/net/net.c b/net/net.c
index c9a749f..39f0b81 100644
--- a/net/net.c
+++ b/net/net.c
@@ -24,7 +24,7 @@
  *			- name of bootfile
  *	Next step:	ARP
  *
- * LINK_LOCAL:
+ * LINKLOCAL:
  *
  *	Prerequisites:	- own ethernet address
  *	We want:	- own IP address
@@ -122,6 +122,7 @@
 #endif
 #include <net/tcp.h>
 #include <net/wget.h>
+#include "net_rand.h"
 
 /** BOOTP EXTENTIONS **/
 
@@ -346,6 +347,8 @@ void net_auto_load(void)
 
 static int net_init_loop(void)
 {
+	static bool first_call = true;
+
 	if (eth_get_dev()) {
 		memcpy(net_ethaddr, eth_get_ethaddr(), 6);
 
@@ -365,6 +368,12 @@ static int net_init_loop(void)
 		 */
 		return -ENONET;
 
+	if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+		if (first_call && use_ip6) {
+			first_call = false;
+			srand_mac(); /* This is for rand used in ip6_send_rs. */
+			net_loop(RS);
+		}
 	return 0;
 }
 
@@ -574,6 +583,10 @@ restart:
 			ncsi_probe_packages();
 			break;
 #endif
+		case RS:
+			if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+				ip6_send_rs();
+			break;
 		default:
 			break;
 		}
@@ -671,7 +684,13 @@ restart:
 			x = time_handler;
 			time_handler = (thand_f *)0;
 			(*x)();
-		}
+		} else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+			if (time_handler && protocol == RS)
+				if (!ip6_is_unspecified_addr(&net_gateway6) &&
+				    net_prefix_length != 0) {
+					net_set_state(NETLOOP_SUCCESS);
+					net_set_timeout_handler(0, 0);
+				}
 
 		if (net_state == NETLOOP_FAIL)
 			ret = net_start_again();
diff --git a/net/net6.c b/net/net6.c
index 75577bc..2dd64c0 100644
--- a/net/net6.c
+++ b/net/net6.c
@@ -413,6 +413,7 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 			break;
 		case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
 		case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+		case IPV6_NDISC_ROUTER_ADVERTISEMENT:
 			ndisc_receive(et, ip6, len);
 			break;
 		default:
-- 
1.8.3.1


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

* Re: [PATCH] net: ipv6: Add support for default gateway discovery.
  2023-03-02 16:58 [PATCH] net: ipv6: Add support for default gateway discovery emohandesi
@ 2023-03-16  8:47 ` Vyacheslav V. Mitrofanov
  2023-03-23 16:44   ` Ehsan Mohandesi
  2023-04-01 19:02 ` Ramon Fried
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-03-16  8:47 UTC (permalink / raw)
  To: u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, dphadke, saproj, rfried.dev,
	ilias.apalodimas, emohandesi, john, sjg, masahisa.kojima

On Thu, 2023-03-02 at 08:58 -0800, emohandesi@linux.microsoft.com
wrote:
> 
> From: Ehsan Mohandesi <emohandesi@microsoft.com>
> 
> In IPv6, the default gateway and prefix length are determined by
> receiving
> a router advertisement as defined in -
> https://www.rfc-editor.org/rfc/rfc4861.
> 
> Add support for sending router solicitation (RS) and processing
> router
> advertisements (RA).
> 
> If the RA has prefix info option and following conditions are met,
> then
> gatewayip6 and net_prefix_length of ip6addr env variables are
> initialized.
> These are later consumed by IPv6 code for non-local destination IP.
> 
> - "Router Lifetime" != 0
> - Prefix is NOT link-local prefix (0xfe80::/10)
> - L flag is 1
> - "Valid Lifetime" != 0
> 
> Timing Parameters:
> - MAX_RTR_SOLICITATION_DELAY (0-1s)
> - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
> - MAX_RTR_SOLICITATIONS (3 RS transmissions)
> 
> The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and
> invoked
> automatically from net_init_loop().
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
> 
> Conflicts:
>         cmd/Kconfig
>         include/net.h
>         net/net.c
> ---
>  cmd/Kconfig     |   7 ++
>  include/ndisc.h |  23 ++++++
>  include/net.h   |   2 +-
>  include/net6.h  |  40 ++++++++++
>  net/ndisc.c     | 243
> +++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  net/net.c       |  23 +++++-
>  net/net6.c      |   1 +
>  7 files changed, 327 insertions(+), 12 deletions(-)
> 

I reviewed this patch and it looks good. I have no critical remarks,
only some small notes. 

I've tested it on SiFive Unmatched board.


> 
> +config IPV6_ROUTER_DISCOVERY
> +       bool "Do router discovery"
> +       depends on IPV6
> +       help
> +         Will automatically perform router solicitation on first
> IPv6
> +         network operation
> +
>  endif
> 
I think it is better to write sth like Do IPv6 router discovery because
IPv4 has also router discovery protocol and it could lead to
misunderstanding


> 
> net_set_timeout_handler(0, 0);
> 
Maybe net_set_timeout_handler(0, NULL); is better



> +/*
> + * validate_ra() - Validate the router advertisement message.
> + *
> + * @ip6:
> + * @len: Length of the router advertisement packet
> + *
> + * Check if the router advertisement message is valid. Conditions
> are
> + * according to RFC 4861 section 6.1.2. Validation of Router
> Advertisement
> + * Messages.
> + *
> + * Return: true if the message is valid and false if it is invalid.
> + */
> +static bool validate_ra(struct ip6_hdr *ip6, int len)
> +{
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +
> +       /* ICMP length (derived from the IP length) should be 16 or
> more octets. */
> +       if (ip6->payload_len < 16)
> +               return false;
> +
> +       /* Source IP Address should be a valid link-local address. */
> +       if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK)
> !=
> +           IPV6_LINK_LOCAL_PREFIX)
> +               return false;
> +
> +       /*
> +        * The IP Hop Limit field should have a value of 255, i.e.,
> the packet
> +        * could not possibly have been forwarded by a router.
> +        */
> +       if (ip6->hop_limit != 255)
> +               return false;
> +
Unicast hop limit only?

> diff --git a/net/net.c b/net/net.c
> index c9a749f..39f0b81 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -24,7 +24,7 @@
>   *                     - name of bootfile
>   *     Next step:      ARP
>   *
> - * LINK_LOCAL:
> + * LINKLOCAL:
> 
Maybe it is better to move to other patch?!


Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

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

* RE: [PATCH] net: ipv6: Add support for default gateway discovery.
  2023-03-16  8:47 ` Vyacheslav V. Mitrofanov
@ 2023-03-23 16:44   ` Ehsan Mohandesi
  2023-03-23 16:59     ` Vyacheslav V. Mitrofanov
  0 siblings, 1 reply; 35+ messages in thread
From: Ehsan Mohandesi @ 2023-03-23 16:44 UTC (permalink / raw)
  To: Vyacheslav V. Mitrofanov, u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, dphadke, saproj, rfried.dev,
	ilias.apalodimas, john, sjg, masahisa.kojima

Hi Viacheslav,

> -----Original Message-----
> From: Vyacheslav V. Mitrofanov <v.v.mitrofanov@yadro.com>
> Sent: Thursday, March 16, 2023 3:47 AM
> To: u-boot@lists.denx.de; emohandesi@linux.microsoft.com
> Cc: joe.hershberger@ni.com; xypron.glpk@gmx.de;
> dphadke@linux.microsoft.com; saproj@gmail.com; rfried.dev@gmail.com;
> ilias.apalodimas@linaro.org; Ehsan Mohandesi <emohandesi@microsoft.com>;
> john@metanate.com; sjg@chromium.org; masahisa.kojima@linaro.org
> Subject: [EXTERNAL] Re: [PATCH] net: ipv6: Add support for default gateway
> discovery.
>
> On Thu, 2023-03-02 at 08:58 -0800, emohandesi@linux.microsoft.com
> wrote:
> >
> > From: Ehsan Mohandesi <emohandesi@microsoft.com>
> >
> > In IPv6, the default gateway and prefix length are determined by
> > receiving a router advertisement as defined in -
> >
> https://www.rf/
> c-
> editor.org%2Frfc%2Frfc4861&data=05%7C01%7Cemohandesi%40microsoft.co
> m%7C6dec635abc8c4861feb708db25fb05d6%7C72f988bf86f141af91ab2d7cd01
> 1db47%7C1%7C0%7C638145532341238481%7CUnknown%7CTWFpbGZsb3d8ey
> JWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C
> 3000%7C%7C%7C&sdata=tAhREBvBgVQKOFqEQT2%2FKphGxYXUMo3UF5vvQpY
> B%2Be0%3D&reserved=0.
> >
> > Add support for sending router solicitation (RS) and processing router
> > advertisements (RA).
> >
> > If the RA has prefix info option and following conditions are met,
> > then
> > gatewayip6 and net_prefix_length of ip6addr env variables are
> > initialized.
> > These are later consumed by IPv6 code for non-local destination IP.
> >
> > - "Router Lifetime" != 0
> > - Prefix is NOT link-local prefix (0xfe80::/10)
> > - L flag is 1
> > - "Valid Lifetime" != 0
> >
> > Timing Parameters:
> > - MAX_RTR_SOLICITATION_DELAY (0-1s)
> > - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
> > - MAX_RTR_SOLICITATIONS (3 RS transmissions)
> >
> > The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and
> > invoked automatically from net_init_loop().
> >
> > Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
> >
> > Conflicts:
> >         cmd/Kconfig
> >         include/net.h
> >         net/net.c
> > ---
> >  cmd/Kconfig     |   7 ++
> >  include/ndisc.h |  23 ++++++
> >  include/net.h   |   2 +-
> >  include/net6.h  |  40 ++++++++++
> >  net/ndisc.c     | 243
> > +++++++++++++++++++++++++++++++++++++++++++++++++++++---
> >  net/net.c       |  23 +++++-
> >  net/net6.c      |   1 +
> >  7 files changed, 327 insertions(+), 12 deletions(-)
> >
>
> I reviewed this patch and it looks good. I have no critical remarks, only some
> small notes.
>
> I've tested it on SiFive Unmatched board.
>
>
> >
> > +config IPV6_ROUTER_DISCOVERY
> > +       bool "Do router discovery"
> > +       depends on IPV6
> > +       help
> > +         Will automatically perform router solicitation on first
> > IPv6
> > +         network operation
> > +
> >  endif
> >
> I think it is better to write sth like Do IPv6 router discovery because
> IPv4 has also router discovery protocol and it could lead to misunderstanding
>
>
> >
> > net_set_timeout_handler(0, 0);
> >
> Maybe net_set_timeout_handler(0, NULL); is better
>
>
>
> > +/*
> > + * validate_ra() - Validate the router advertisement message.
> > + *
> > + * @ip6:
> > + * @len: Length of the router advertisement packet
> > + *
> > + * Check if the router advertisement message is valid. Conditions
> > are
> > + * according to RFC 4861 section 6.1.2. Validation of Router
> > Advertisement
> > + * Messages.
> > + *
> > + * Return: true if the message is valid and false if it is invalid.
> > + */
> > +static bool validate_ra(struct ip6_hdr *ip6, int len) {
> > +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> > +
> > +       /* ICMP length (derived from the IP length) should be 16 or
> > more octets. */
> > +       if (ip6->payload_len < 16)
> > +               return false;
> > +
> > +       /* Source IP Address should be a valid link-local address. */
> > +       if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK)
> > !=
> > +           IPV6_LINK_LOCAL_PREFIX)
> > +               return false;
> > +
> > +       /*
> > +        * The IP Hop Limit field should have a value of 255, i.e.,
> > the packet
> > +        * could not possibly have been forwarded by a router.
> > +        */
> > +       if (ip6->hop_limit != 255)
> > +               return false;
> > +
> Unicast hop limit only?

Sorry, I do not understand what you mean here. What kind of scenario are you talking about?
It does not matter if the router advertisement is unicast or multicast. In both cases, the hop limit needs to be 255. A router always sets the hop limit to 255. We do not want an advertisement that is forwarded from another node. Refer to this for more information.
https://www.rfc-editor.org/rfc/rfc4861#section-6.1.2

>
> > diff --git a/net/net.c b/net/net.c
> > index c9a749f..39f0b81 100644
> > --- a/net/net.c
> > +++ b/net/net.c
> > @@ -24,7 +24,7 @@
> >   *                     - name of bootfile
> >   *     Next step:      ARP
> >   *
> > - * LINK_LOCAL:
> > + * LINKLOCAL:
> >
> Maybe it is better to move to other patch?!

The underscore change on this line is a needed change. LINKLOCAL is used this way in the code. In the comment, it has an extra _ in it. It causes confusion. An important feature of Linux coding is being able to grep for strings. When I grepped for LINK_LOCAL I did not find what I was looking for. If there are more important reasons not to make this change, please let me know to revert it. I did not directly change any LINKLOCAL code, but I needed it for writing my code. It could happen to anyone. I think it is better to fix it here and save everyone's time now.

>
>
> Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
> Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

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

* Re: [PATCH] net: ipv6: Add support for default gateway discovery.
  2023-03-23 16:44   ` Ehsan Mohandesi
@ 2023-03-23 16:59     ` Vyacheslav V. Mitrofanov
  0 siblings, 0 replies; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-03-23 16:59 UTC (permalink / raw)
  To: emohandesi, u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, dphadke, saproj, rfried.dev,
	ilias.apalodimas, john, sjg, masahisa.kojima

On Thu, 2023-03-23 at 16:44 +0000, Ehsan Mohandesi wrote:
> 
> Hi Viacheslav,
> 
> > -----Original Message-----
> > From: Vyacheslav V. Mitrofanov <v.v.mitrofanov@yadro.com>
> > Sent: Thursday, March 16, 2023 3:47 AM
> > To: u-boot@lists.denx.de; emohandesi@linux.microsoft.com
> > Cc: joe.hershberger@ni.com; xypron.glpk@gmx.de;
> > dphadke@linux.microsoft.com; saproj@gmail.com; rfried.dev@gmail.com
> > ;
> > ilias.apalodimas@linaro.org; Ehsan Mohandesi <
> > emohandesi@microsoft.com>;
> > john@metanate.com; sjg@chromium.org; masahisa.kojima@linaro.org
> > Subject: [EXTERNAL] Re: [PATCH] net: ipv6: Add support for default
> > gateway
> > discovery.
> > 
> > On Thu, 2023-03-02 at 08:58 -0800, emohandesi@linux.microsoft.com
> > wrote:
> > > From: Ehsan Mohandesi <emohandesi@microsoft.com>
> > > 
> > > In IPv6, the default gateway and prefix length are determined by
> > > receiving a router advertisement as defined in -
> > > 
> > https://www.rf/
> > c-
> > editor.org%2Frfc%2Frfc4861&data=05%7C01%7Cemohandesi%40microsoft.co
> > m%7C6dec635abc8c4861feb708db25fb05d6%7C72f988bf86f141af91ab2d7cd01
> > 1db47%7C1%7C0%7C638145532341238481%7CUnknown%7CTWFpbGZsb3d8ey
> > JWIjoiMC4wLjAwMDAiLCJQIjoiV2luMzIiLCJBTiI6Ik1haWwiLCJXVCI6Mn0%3D%7C
> > 3000%7C%7C%7C&sdata=tAhREBvBgVQKOFqEQT2%2FKphGxYXUMo3UF5vvQpY
> > B%2Be0%3D&reserved=0.
> > > Add support for sending router solicitation (RS) and processing
> > > router
> > > advertisements (RA).
> > > 
> > > If the RA has prefix info option and following conditions are
> > > met,
> > > then
> > > gatewayip6 and net_prefix_length of ip6addr env variables are
> > > initialized.
> > > These are later consumed by IPv6 code for non-local destination
> > > IP.
> > > 
> > > - "Router Lifetime" != 0
> > > - Prefix is NOT link-local prefix (0xfe80::/10)
> > > - L flag is 1
> > > - "Valid Lifetime" != 0
> > > 
> > > Timing Parameters:
> > > - MAX_RTR_SOLICITATION_DELAY (0-1s)
> > > - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
> > > - MAX_RTR_SOLICITATIONS (3 RS transmissions)
> > > 
> > > The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and
> > > invoked automatically from net_init_loop().
> > > 
> > > Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
> > > 
> > > Conflicts:
> > >         cmd/Kconfig
> > >         include/net.h
> > >         net/net.c
> > > ---
> > >  cmd/Kconfig     |   7 ++
> > >  include/ndisc.h |  23 ++++++
> > >  include/net.h   |   2 +-
> > >  include/net6.h  |  40 ++++++++++
> > >  net/ndisc.c     | 243
> > > +++++++++++++++++++++++++++++++++++++++++++++++++++++---
> > >  net/net.c       |  23 +++++-
> > >  net/net6.c      |   1 +
> > >  7 files changed, 327 insertions(+), 12 deletions(-)
> > > 
> > 
> > I reviewed this patch and it looks good. I have no critical
> > remarks, only some
> > small notes.
> > 
> > I've tested it on SiFive Unmatched board.
> > 
> > 
> > > +config IPV6_ROUTER_DISCOVERY
> > > +       bool "Do router discovery"
> > > +       depends on IPV6
> > > +       help
> > > +         Will automatically perform router solicitation on first
> > > IPv6
> > > +         network operation
> > > +
> > >  endif
> > > 
> > I think it is better to write sth like Do IPv6 router discovery
> > because
> > IPv4 has also router discovery protocol and it could lead to
> > misunderstanding
> > 
> > 
> > > net_set_timeout_handler(0, 0);
> > > 
> > Maybe net_set_timeout_handler(0, NULL); is better
> > 
> > 
> > 
> > > +/*
> > > + * validate_ra() - Validate the router advertisement message.
> > > + *
> > > + * @ip6:
> > > + * @len: Length of the router advertisement packet
> > > + *
> > > + * Check if the router advertisement message is valid.
> > > Conditions
> > > are
> > > + * according to RFC 4861 section 6.1.2. Validation of Router
> > > Advertisement
> > > + * Messages.
> > > + *
> > > + * Return: true if the message is valid and false if it is
> > > invalid.
> > > + */
> > > +static bool validate_ra(struct ip6_hdr *ip6, int len) {
> > > +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> > > +
> > > +       /* ICMP length (derived from the IP length) should be 16
> > > or
> > > more octets. */
> > > +       if (ip6->payload_len < 16)
> > > +               return false;
> > > +
> > > +       /* Source IP Address should be a valid link-local
> > > address. */
> > > +       if ((ntohs(ip6->saddr.s6_addr16[0]) &
> > > IPV6_LINK_LOCAL_MASK)
> > > !=
> > > +           IPV6_LINK_LOCAL_PREFIX)
> > > +               return false;
> > > +
> > > +       /*
> > > +        * The IP Hop Limit field should have a value of 255,
> > > i.e.,
> > > the packet
> > > +        * could not possibly have been forwarded by a router.
> > > +        */
> > > +       if (ip6->hop_limit != 255)
> > > +               return false;
> > > +
> > Unicast hop limit only?
> 
> Sorry, I do not understand what you mean here. What kind of scenario
> are you talking about?
> It does not matter if the router advertisement is unicast or
> multicast. In both cases, the hop limit needs to be 255. A router
> always sets the hop limit to 255. We do not want an advertisement
> that is forwarded from another node. Refer to this for more
> information.
> https://www.rfc-editor.org/rfc/rfc4861#section-6.1.2
> 

Yes, I got you. I have no objections.
Thanks!

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

* Re: [PATCH] net: ipv6: Add support for default gateway discovery.
  2023-03-02 16:58 [PATCH] net: ipv6: Add support for default gateway discovery emohandesi
  2023-03-16  8:47 ` Vyacheslav V. Mitrofanov
@ 2023-04-01 19:02 ` Ramon Fried
  2023-04-10 19:34 ` [PATCH v2 0/4] Add IPv6 Network Discovery emohandesi
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 35+ messages in thread
From: Ramon Fried @ 2023-04-01 19:02 UTC (permalink / raw)
  To: emohandesi
  Cc: u-boot, joe.hershberger, sjg, xypron.glpk, ilias.apalodimas,
	masahisa.kojima, john, emohandesi, v.v.mitrofanov, saproj,
	dphadke

On Thu, Mar 2, 2023 at 6:58 PM <emohandesi@linux.microsoft.com> wrote:
>
> From: Ehsan Mohandesi <emohandesi@microsoft.com>
>
> In IPv6, the default gateway and prefix length are determined by receiving
> a router advertisement as defined in -
> https://www.rfc-editor.org/rfc/rfc4861.
>
> Add support for sending router solicitation (RS) and processing router
> advertisements (RA).
>
> If the RA has prefix info option and following conditions are met, then
> gatewayip6 and net_prefix_length of ip6addr env variables are initialized.
> These are later consumed by IPv6 code for non-local destination IP.
>
> - "Router Lifetime" != 0
> - Prefix is NOT link-local prefix (0xfe80::/10)
> - L flag is 1
> - "Valid Lifetime" != 0
>
> Timing Parameters:
> - MAX_RTR_SOLICITATION_DELAY (0-1s)
> - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
> - MAX_RTR_SOLICITATIONS (3 RS transmissions)
>
> The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and invoked
> automatically from net_init_loop().
>
> Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
>
> Conflicts:
>         cmd/Kconfig
>         include/net.h
>         net/net.c
> ---
>  cmd/Kconfig     |   7 ++
>  include/ndisc.h |  23 ++++++
>  include/net.h   |   2 +-
>  include/net6.h  |  40 ++++++++++
>  net/ndisc.c     | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  net/net.c       |  23 +++++-
>  net/net6.c      |   1 +
>  7 files changed, 327 insertions(+), 12 deletions(-)
>
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index 2caa4af..c46613e 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1894,6 +1894,13 @@ config CMD_NCSI
>           Normally this happens automatically before other network
>           operations.
>
> +config IPV6_ROUTER_DISCOVERY
> +       bool "Do router discovery"
> +       depends on IPV6
> +       help
> +         Will automatically perform router solicitation on first IPv6
> +         network operation
> +
>  endif
>
>  config CMD_ETHSW
> diff --git a/include/ndisc.h b/include/ndisc.h
> index f6f8eb6..362d707 100644
> --- a/include/ndisc.h
> +++ b/include/ndisc.h
> @@ -19,6 +19,20 @@ struct nd_msg {
>         __u8            opt[0];
>  };
>
> +/* struct rs_msg - ICMPv6 Router Solicitation message format */
> +struct rs_msg {
> +       struct icmp6hdr icmph;
> +       __u8            opt[0];
> +};
> +
> +/* struct ra_msg - ICMPv6 Router Advertisement message format */
> +struct ra_msg {
> +       struct icmp6hdr icmph;
> +       __u32   reachable_time;
> +       __u32   retransmission_timer;
> +       __u8    opt[0];
> +};
> +
>  /* struct echo_msg - ICMPv6 echo request/reply message format */
>  struct echo_msg {
>         struct icmp6hdr icmph;
> @@ -57,6 +71,11 @@ extern int net_nd_try;
>   */
>  void ndisc_init(void);
>
> +/*
> + * ip6_send_rs() - Send IPv6 Router Solicitation Message
> + */
> +void ip6_send_rs(void);
> +
>  /**
>   * ndisc_receive() - Handle ND packet
>   *
> @@ -97,6 +116,10 @@ static inline int ndisc_timeout_check(void)
>  {
>         return 0;
>  }
> +
> +void ip6_send_rs(void)
> +{
> +}
>  #endif
>
>  #endif /* __NDISC_H__ */
> diff --git a/include/net.h b/include/net.h
> index 399af5e..25c43b3 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -505,7 +505,7 @@ 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
> +       SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET, RS
>  };
>
>  extern char    net_boot_file_name[1024];/* Boot File name */
> diff --git a/include/net6.h b/include/net6.h
> index 2d7c5a0..beafc05 100644
> --- a/include/net6.h
> +++ b/include/net6.h
> @@ -81,8 +81,17 @@ struct udp_hdr {
>                           0x00, 0x00, 0x00, 0x00, \
>                           0x00, 0x00, 0x00, 0x00, \
>                           0x00, 0x00, 0x00, 0x00 } } }
> +/*
> + * All-routers multicast address is the link-local scope address to reach all
> + * routers.
> + */
> +#define ALL_ROUTERS_MULT_ADDR { { { 0xFF, 0x02, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x02 } } }
>
>  #define IPV6_LINK_LOCAL_PREFIX 0xfe80
> +#define IPV6_LINK_LOCAL_MASK   0xffb0 /* The first 10-bit of address mask. */
>
>  /* hop limit for neighbour discovery packets */
>  #define IPV6_NDISC_HOPLIMIT             255
> @@ -166,6 +175,37 @@ struct icmp6hdr {
>  #define icmp6_rt_lifetime      icmp6_dataun.u_nd_ra.rt_lifetime
>  } __packed;
>
> +/*
> + * struct icmp6_ra_prefix_info - Prefix Information option of the ICMPv6 message
> + * The Prefix Information option provides hosts with on-link prefixes and
> + * prefixes for Address Autoconfiguration. Refer to RFC 4861 for more info.
> + */
> +struct icmp6_ra_prefix_info {
> +       u8      type;           /* Type is 3 for Prefix Information. */
> +       u8      len;            /* Len is 4 for Prefix Information. */
> +       /* The number of leading bits in the Prefix that are valid. */
> +       u8      prefix_len;
> +       u8      reserved1:6,    /* MUST be ignored by the receiver. */
> +               aac:1,          /* autonomous address-configuration flag */
> +       /* Indicates that this prefix can be used for on-link determination. */
> +               on_link:1;
> +       /*
> +        * The length of time in seconds that the prefix is valid for the
> +        * purpose of on-link determination.
> +        */
> +       __be32  valid_lifetime;
> +       /* The length of time addresses remain preferred. */
> +       __be32  preferred_lifetime;
> +       __be32  reserved2;      /* MUST be ignored by the receiver. */
> +       /*
> +        * Prefix is an IP address or a prefix of an IP address. The Prefix
> +        * Length field contains the number of valid leading bits in the prefix.
> +        * The bits in the prefix after the prefix length are reserved and MUST
> +        * be initialized to zero by the sender and ignored by the receiver.
> +        */
> +       struct in6_addr prefix;
> +};
> +
>  extern struct in6_addr const net_null_addr_ip6;        /* NULL IPv6 address */
>  extern struct in6_addr net_gateway6;   /* Our gateways IPv6 address */
>  extern struct in6_addr net_ip6;        /* Our IPv6 addr (0 = unknown) */
> diff --git a/net/ndisc.c b/net/ndisc.c
> index 367dae7..db76c4b 100644
> --- a/net/ndisc.c
> +++ b/net/ndisc.c
> @@ -13,6 +13,8 @@
>  #include <net.h>
>  #include <net6.h>
>  #include <ndisc.h>
> +#include <stdlib.h>
> +#include <linux/delay.h>
>
>  /* IPv6 destination address of packet waiting for ND */
>  struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
> @@ -29,31 +31,37 @@ int net_nd_tx_packet_size;
>  ulong net_nd_timer_start;
>  /* the number of requests we have sent so far */
>  int net_nd_try;
> +struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR;
> +
> +#define MAX_RTR_SOLICITATIONS          3
> +/* The maximum time to delay sending the first router solicitation message. */
> +#define MAX_SOLICITATION_DELAY         1 // 1 second
> +/* The time to wait before sending the next router solicitation message. */
> +#define RTR_SOLICITATION_INTERVAL      4000 // 4 seconds
>
>  #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7)
>
>  /**
>   * ndisc_insert_option() - Insert an option into a neighbor discovery packet
>   *
> - * @ndisc:     pointer to ND packet
> + * @opt:       pointer to the option element of the neighbor discovery packet
>   * @type:      option type to insert
>   * @data:      option data to insert
>   * @len:       data length
>   * Return: the number of bytes inserted (which may be >= len)
>   */
> -static int
> -ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
> +static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int len)
>  {
>         int space = IP6_NDISC_OPT_SPACE(len);
>
> -       ndisc->opt[0] = type;
> -       ndisc->opt[1] = space >> 3;
> -       memcpy(&ndisc->opt[2], data, len);
> +       opt[0] = type;
> +       opt[1] = space >> 3;
> +       memcpy(&opt[2], data, len);
>         len += 2;
>
>         /* fill the remainder with 0 */
>         if (space - len > 0)
> -               memset(&ndisc->opt[len], '\0', space - len);
> +               memset(&opt[len], '\0', space - len);
>
>         return space;
>  }
> @@ -123,7 +131,7 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
>
>         /* Set the target address and llsaddr option */
>         net_copy_ip6(&msg->target, neigh_addr);
> -       ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
> +       ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
>                             INETHADDRSZ);
>
>         /* checksum */
> @@ -137,6 +145,76 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
>         net_send_packet(net_tx_packet, (pkt - net_tx_packet));
>  }
>
> +/*
> + * ip6_send_rs() - Send IPv6 Router Solicitation Message.
> + *
> + * A router solicitation is sent to discover a router. RS message creation is
> + * based on RFC 4861 section 4.1. Router Solicitation Message Format.
> + */
> +void ip6_send_rs(void)
> +{
> +       unsigned char enetaddr[6];
> +       struct rs_msg *msg;
> +       __u16 icmp_len;
> +       uchar *pkt;
> +       unsigned short csum;
> +       unsigned int pcsum;
> +       static unsigned int retry_count;
> +
> +       if (!ip6_is_unspecified_addr(&net_gateway6) &&
> +           net_prefix_length != 0) {
> +               net_set_state(NETLOOP_SUCCESS);
> +               return;
> +       } else if (retry_count >= MAX_RTR_SOLICITATIONS) {
> +               net_set_state(NETLOOP_FAIL);
> +               net_set_timeout_handler(0, 0);
> +               retry_count = 0;
> +               return;
> +       }
> +
> +       printf("ROUTER SOLICITATION %d\n", retry_count + 1);
> +
> +       ip6_make_mult_ethdstaddr(enetaddr, &all_routers);
> +       /*
> +        * ICMP length is the size of ICMP header (8) + one option (8) = 16.
> +        * The option is 2 bytes of type and length + 6 bytes for MAC.
> +        */
> +       icmp_len = sizeof(struct icmp6hdr) + IP6_NDISC_OPT_SPACE(INETHADDRSZ);
> +
> +       pkt = (uchar *)net_tx_packet;
> +       pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
> +       pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers, PROT_ICMPV6,
> +                          IPV6_NDISC_HOPLIMIT, icmp_len);
> +
> +       /* ICMPv6 - RS */
> +       msg = (struct rs_msg *)pkt;
> +       msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION;
> +       msg->icmph.icmp6_code = 0;
> +       memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
> +       memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
> +
> +       /* Set the llsaddr option */
> +       ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
> +                           INETHADDRSZ);
> +
> +       /* checksum */
> +       pcsum = csum_partial((__u8 *)msg, icmp_len, 0);
> +       csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers,
> +                              icmp_len, PROT_ICMPV6, pcsum);
> +       msg->icmph.icmp6_cksum = csum;
> +       pkt += icmp_len;
> +
> +       /* Wait up to 1 second if it is the first try to get the RA */
> +       if (retry_count == 0)
> +               udelay(((unsigned int)rand() % 1000000) * MAX_SOLICITATION_DELAY);
> +
> +       /* send it! */
> +       net_send_packet(net_tx_packet, (pkt - net_tx_packet));
> +
> +       retry_count++;
> +       net_set_timeout_handler(RTR_SOLICITATION_INTERVAL, ip6_send_rs);
> +}
> +
>  static void
>  ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
>             struct in6_addr *target)
> @@ -167,7 +245,7 @@ ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
>         msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
>         /* Set the target address and lltargetaddr option */
>         net_copy_ip6(&msg->target, target);
> -       ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
> +       ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
>                             INETHADDRSZ);
>
>         /* checksum */
> @@ -223,6 +301,10 @@ int ndisc_timeout_check(void)
>         return 1;
>  }
>
> +/*
> + * ndisc_init() - Make initial steps for ND state machine.
> + * Usually move variables into initial state.
> + */
>  void ndisc_init(void)
>  {
>         net_nd_packet_mac = NULL;
> @@ -234,12 +316,125 @@ void ndisc_init(void)
>         net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
>  }
>
> +/*
> + * validate_ra() - Validate the router advertisement message.
> + *
> + * @ip6: Pointer to the router advertisement packet
> + * @len: Length of the router advertisement packet
> + *
> + * Check if the router advertisement message is valid. Conditions are
> + * according to RFC 4861 section 6.1.2. Validation of Router Advertisement
> + * Messages.
> + *
> + * Return: true if the message is valid and false if it is invalid.
> + */
> +static bool validate_ra(struct ip6_hdr *ip6, int len)
> +{
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +
> +       /* ICMP length (derived from the IP length) should be 16 or more octets. */
> +       if (ip6->payload_len < 16)
> +               return false;
> +
> +       /* Source IP Address should be a valid link-local address. */
> +       if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK) !=
> +           IPV6_LINK_LOCAL_PREFIX)
> +               return false;
> +
> +       /*
> +        * The IP Hop Limit field should have a value of 255, i.e., the packet
> +        * could not possibly have been forwarded by a router.
> +        */
> +       if (ip6->hop_limit != 255)
> +               return false;
> +
> +       /* ICMP checksum has already been checked in net_ip6_handler. */
> +
> +       if (icmp->icmp6_code != 0)
> +               return false;
> +
> +       return true;
> +}
> +
> +/*
> + * process_ra() - Process the router advertisement packet.
> + *
> + * @ip6: Pointer to the router advertisement packet
> + * @len: Length of the router advertisement packet
> + *
> + * Process the received router advertisement message.
> + * Although RFC 4861 requires retaining at least two router addresses, we only
> + * keep one because of the U-Boot limitations and its goal of lightweight code.
> + *
> + * Return: 0 - RA is a default router and contains valid prefix information.
> + * Non-zero - RA options are invalid or do not indicate it is a default router
> + * or do not contain valid prefix information.
> + */
> +static int process_ra(struct ip6_hdr *ip6, int len)
> +{
> +       /* Pointer to the ICMP section of the packet */
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +       struct ra_msg *msg = (struct ra_msg *)icmp;
> +       int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct ra_msg);
> +       unsigned short int option_len;  /* Length of each option */
> +       /* Pointer to the ICMPv6 message options */
> +       unsigned char *option = NULL;
> +       /* 8-bit identifier of the type of ICMPv6 option */
> +       unsigned char type = 0;
> +       struct icmp6_ra_prefix_info *prefix = NULL;
> +
> +       /* Ignore the packet if router lifetime is 0. */
> +       if (!icmp->icmp6_rt_lifetime)
> +               return -EOPNOTSUPP;
> +
> +       /* Processing the options */
> +       option = msg->opt;
> +       while (remaining_option_len > 0) {
> +               /* The 2nd byte of the option is its length. */
> +               option_len = option[1];
> +               /* All included options should have a positive length. */
> +               if (option_len == 0)
> +                       return -EINVAL;
> +
> +               type = option[0];
> +               /* All option types except Prefix Information are ignored. */
> +               switch (type) {
> +               case ND_OPT_SOURCE_LL_ADDR:
> +               case ND_OPT_TARGET_LL_ADDR:
> +               case ND_OPT_REDIRECT_HDR:
> +               case ND_OPT_MTU:
> +                       break;
> +               case ND_OPT_PREFIX_INFO:
> +                       prefix = (struct icmp6_ra_prefix_info *)option;
> +                       /* The link-local prefix 0xfe80::/10 is ignored. */
> +                       if ((ntohs(prefix->prefix.s6_addr16[0]) &
> +                            IPV6_LINK_LOCAL_MASK) == IPV6_LINK_LOCAL_PREFIX)
> +                               break;
> +                       if (prefix->on_link && ntohl(prefix->valid_lifetime)) {
> +                               net_prefix_length = prefix->prefix_len;
> +                               net_gateway6 = ip6->saddr;
> +                               return 0;
> +                       }
> +                       break;
> +               default:
> +                       debug("Unknown IPv6 Neighbor Discovery Option 0x%x\n",
> +                             type);
> +               }
> +
> +               option_len <<= 3; /* Option length is a multiple of 8. */
> +               remaining_option_len -= option_len;
> +               option += option_len;
> +       }
> +       return -EADDRNOTAVAIL;
> +}
> +
>  int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
>  {
>         struct icmp6hdr *icmp =
>             (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
>         struct nd_msg *ndisc = (struct nd_msg *)icmp;
>         uchar neigh_eth_addr[6];
> +       int err = 0;    // The error code returned calling functions.
>
>         switch (icmp->icmp6_type) {
>         case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
> @@ -280,6 +475,36 @@ int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
>                         net_nd_packet_mac = NULL;
>                 }
>                 break;
> +       case IPV6_NDISC_ROUTER_SOLICITATION:
> +               break;
> +       case IPV6_NDISC_ROUTER_ADVERTISEMENT:
> +               debug("Received router advertisement for %pI6c from %pI6c\n",
> +                     &ip6->daddr, &ip6->saddr);
> +               /*
> +                * If gateway and prefix are set, the RA packet is ignored. The
> +                * reason is that the U-Boot code is supposed to be as compact
> +                * as possible and does not need to take care of multiple
> +                * routers. In addition to that, U-Boot does not want to handle
> +                * scenarios like a router setting its lifetime to zero to
> +                * indicate it is not routing anymore. U-Boot program has a
> +                * short life when the system boots up and does not need such
> +                * sophistication.
> +                */
> +               if (!ip6_is_unspecified_addr(&net_gateway6) &&
> +                   net_prefix_length != 0) {
> +                       break;
> +               }
> +               if (!validate_ra(ip6, len)) {
> +                       debug("Invalid router advertisement message.\n");
> +                       break;
> +               }
> +               err = process_ra(ip6, len);
> +               if (err)
> +                       debug("Ignored router advertisement. Error: %d\n", err);
> +               else
> +                       printf("Set gatewayip6: %pI6c, prefix_length: %d\n",
> +                              &net_gateway6, net_prefix_length);
> +               break;
>         default:
>                 debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
>                 return -1;
> diff --git a/net/net.c b/net/net.c
> index c9a749f..39f0b81 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -24,7 +24,7 @@
>   *                     - name of bootfile
>   *     Next step:      ARP
>   *
> - * LINK_LOCAL:
> + * LINKLOCAL:
>   *
>   *     Prerequisites:  - own ethernet address
>   *     We want:        - own IP address
> @@ -122,6 +122,7 @@
>  #endif
>  #include <net/tcp.h>
>  #include <net/wget.h>
> +#include "net_rand.h"
>
>  /** BOOTP EXTENTIONS **/
>
> @@ -346,6 +347,8 @@ void net_auto_load(void)
>
>  static int net_init_loop(void)
>  {
> +       static bool first_call = true;
> +
>         if (eth_get_dev()) {
>                 memcpy(net_ethaddr, eth_get_ethaddr(), 6);
>
> @@ -365,6 +368,12 @@ static int net_init_loop(void)
>                  */
>                 return -ENONET;
>
> +       if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +               if (first_call && use_ip6) {
> +                       first_call = false;
> +                       srand_mac(); /* This is for rand used in ip6_send_rs. */
> +                       net_loop(RS);
> +               }
>         return 0;
>  }
>
> @@ -574,6 +583,10 @@ restart:
>                         ncsi_probe_packages();
>                         break;
>  #endif
> +               case RS:
> +                       if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +                               ip6_send_rs();
> +                       break;
>                 default:
>                         break;
>                 }
> @@ -671,7 +684,13 @@ restart:
>                         x = time_handler;
>                         time_handler = (thand_f *)0;
>                         (*x)();
> -               }
> +               } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +                       if (time_handler && protocol == RS)
> +                               if (!ip6_is_unspecified_addr(&net_gateway6) &&
> +                                   net_prefix_length != 0) {
> +                                       net_set_state(NETLOOP_SUCCESS);
> +                                       net_set_timeout_handler(0, 0);
> +                               }
>
>                 if (net_state == NETLOOP_FAIL)
>                         ret = net_start_again();
> diff --git a/net/net6.c b/net/net6.c
> index 75577bc..2dd64c0 100644
> --- a/net/net6.c
> +++ b/net/net6.c
> @@ -413,6 +413,7 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
>                         break;
>                 case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
>                 case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
> +               case IPV6_NDISC_ROUTER_ADVERTISEMENT:
>                         ndisc_receive(et, ip6, len);
>                         break;
>                 default:
> --
> 1.8.3.1
>
Reviewed-by: Ramon Fried <rfried.dev@gmail.com>

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

* [PATCH v2 0/4] Add IPv6 Network Discovery
  2023-03-02 16:58 [PATCH] net: ipv6: Add support for default gateway discovery emohandesi
  2023-03-16  8:47 ` Vyacheslav V. Mitrofanov
  2023-04-01 19:02 ` Ramon Fried
@ 2023-04-10 19:34 ` emohandesi
  2023-04-10 19:34   ` [PATCH v2 1/4] Revert "net: ipv6: Add support for default gateway discovery." emohandesi
                     ` (3 more replies)
  2023-04-12 16:10 ` [PATCH v3 0/3] Add IPv6 Network Discovery emohandesi
  2023-04-22  0:08 ` [PATCH v4 0/3] Add IPv6 Network Discovery emohandesi
  4 siblings, 4 replies; 35+ messages in thread
From: emohandesi @ 2023-04-10 19:34 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, sjg, xypron.glpk, ilias.apalodimas,
	masahisa.kojima, tobias, john, v.v.mitrofanov, saproj, mario.six

From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>

This series adds IPv6 network discovery to U-Boot. When an IPv6 command is
run in U-Boot, it sends a router solicitation (RS) message to the network.
The router on the network responds with a router advertisement (RA)
message. Then U-Boot processes the RA message and sets the gatewayip6 and
net_prefix_length environment variables.
It is based on RFC 4861, but not everything in the RFC is supported here.
https://www.rfc-editor.org/rfc/rfc4861

Changes in v2:
- Improved IPv6 network discovery code.
- Added IPv6 network discovery feature test (Python test).
- Added unit tests (C code).

Ehsan Mohandesi (4):
  Revert "net: ipv6: Add support for default gateway discovery."
  net: ipv6: Add support for default gateway discovery.
  test/py: IPv6 network discovery test
  test: eth: IPv6 network discovery unit test

 cmd/Kconfig                        |   6 +
 configs/sandbox64_defconfig        |   2 +
 configs/sandbox_defconfig          |   2 +
 configs/sandbox_flattree_defconfig |   2 +
 include/ndisc.h                    |  35 ++++++
 include/net.h                      |   5 +-
 include/net6.h                     |  40 ++++++
 net/ndisc.c                        | 243 +++++++++++++++++++++++++++++++++++--
 net/net.c                          |  26 +++-
 net/net6.c                         |   1 +
 test/dm/eth.c                      |  88 ++++++++++++++
 test/py/tests/test_net.py          |  31 ++++-
 12 files changed, 467 insertions(+), 14 deletions(-)

-- 
1.8.3.1


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

* [PATCH v2 1/4] Revert "net: ipv6: Add support for default gateway discovery."
  2023-04-10 19:34 ` [PATCH v2 0/4] Add IPv6 Network Discovery emohandesi
@ 2023-04-10 19:34   ` emohandesi
  2023-04-11  7:01     ` Vyacheslav V. Mitrofanov
  2023-04-10 19:34   ` [PATCH v2 2/4] net: ipv6: Add support for default gateway discovery emohandesi
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 35+ messages in thread
From: emohandesi @ 2023-04-10 19:34 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, sjg, xypron.glpk, ilias.apalodimas,
	masahisa.kojima, tobias, john, v.v.mitrofanov, saproj, mario.six

From: Ehsan Mohandesi <emohandesi@microsoft.com>

This reverts commit 0af1035a55d9c1486b2db43ee70ff0a63affd4f4.

Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>

Conflicts:
	cmd/Kconfig
	include/net.h
	include/net6.h
	net/net.c
---
 include/net.h | 4 ++--
 net/net.c     | 3 +++
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/include/net.h b/include/net.h
index 399af5e..8ba50a0 100644
--- a/include/net.h
+++ b/include/net.h
@@ -504,8 +504,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/net.c b/net/net.c
index c9a749f..d69bfb0 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 **/
 
-- 
1.8.3.1


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

* [PATCH v2 2/4] net: ipv6: Add support for default gateway discovery.
  2023-04-10 19:34 ` [PATCH v2 0/4] Add IPv6 Network Discovery emohandesi
  2023-04-10 19:34   ` [PATCH v2 1/4] Revert "net: ipv6: Add support for default gateway discovery." emohandesi
@ 2023-04-10 19:34   ` emohandesi
  2023-04-10 19:34   ` [PATCH v2 3/4] test/py: IPv6 network discovery test emohandesi
  2023-04-10 19:34   ` [PATCH v2 4/4] test: eth: IPv6 network discovery unit test emohandesi
  3 siblings, 0 replies; 35+ messages in thread
From: emohandesi @ 2023-04-10 19:34 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, sjg, xypron.glpk, ilias.apalodimas,
	masahisa.kojima, tobias, john, v.v.mitrofanov, saproj, mario.six

From: Ehsan Mohandesi <emohandesi@microsoft.com>

In IPv6, the default gateway and prefix length are determined by receiving
a router advertisement as defined in -
https://www.rfc-editor.org/rfc/rfc4861.

Add support for sending router solicitation (RS) and processing router
advertisements (RA).

If the RA has prefix info option and following conditions are met, then
gatewayip6 and net_prefix_length of ip6addr env variables are initialized.
These are later consumed by IPv6 code for non-local destination IP.

- "Router Lifetime" != 0
- Prefix is NOT link-local prefix (0xfe80::/10)
- L flag is 1
- "Valid Lifetime" != 0

Timing Parameters:
- MAX_RTR_SOLICITATION_DELAY (0-1s)
- RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
- MAX_RTR_SOLICITATIONS (3 RS transmissions)

The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and invoked
automatically from net_init_loop().

Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>

Conflicts:
	cmd/Kconfig
	net/net.c
---
Changes in v2:
- A few of cosmetic changes.
- Made some functions not static in order to be able to test them.

 cmd/Kconfig     |   6 ++
 include/ndisc.h |  35 ++++++++
 include/net.h   |   3 +-
 include/net6.h  |  40 ++++++++++
 net/ndisc.c     | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 net/net.c       |  23 +++++-
 net/net6.c      |   1 +
 7 files changed, 339 insertions(+), 12 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index e45b884..6919d31 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1916,6 +1916,12 @@ config CMD_NCSI
 	  Normally this happens automatically before other network
 	  operations.
 
+config IPV6_ROUTER_DISCOVERY
+	bool "Do IPv6 router discovery"
+	depends on IPV6
+	help
+	  Will automatically perform router solicitation on first IPv6
+	  network operation
 endif
 
 config CMD_ETHSW
diff --git a/include/ndisc.h b/include/ndisc.h
index f6f8eb6..12fa9e7 100644
--- a/include/ndisc.h
+++ b/include/ndisc.h
@@ -19,6 +19,20 @@ struct nd_msg {
 	__u8		opt[0];
 };
 
+/* struct rs_msg - ICMPv6 Router Solicitation message format */
+struct rs_msg {
+	struct icmp6hdr	icmph;
+	__u8		opt[0];
+};
+
+/* struct ra_msg - ICMPv6 Router Advertisement message format */
+struct ra_msg {
+	struct icmp6hdr	icmph;
+	__u32	reachable_time;
+	__u32	retransmission_timer;
+	__u8	opt[0];
+};
+
 /* struct echo_msg - ICMPv6 echo request/reply message format */
 struct echo_msg {
 	struct icmp6hdr	icmph;
@@ -57,6 +71,11 @@ extern int net_nd_try;
  */
 void ndisc_init(void);
 
+/*
+ * ip6_send_rs() - Send IPv6 Router Solicitation Message
+ */
+void ip6_send_rs(void);
+
 /**
  * ndisc_receive() - Handle ND packet
  *
@@ -78,6 +97,8 @@ void ndisc_request(void);
  * Return: 0 if no timeout, -1 otherwise
  */
 int ndisc_timeout_check(void);
+bool validate_ra(struct ip6_hdr *ip6);
+int process_ra(struct ip6_hdr *ip6, int len);
 #else
 static inline void ndisc_init(void)
 {
@@ -97,6 +118,20 @@ static inline int ndisc_timeout_check(void)
 {
 	return 0;
 }
+
+void ip6_send_rs(void)
+{
+}
+
+static inline bool validate_ra(struct ip6_hdr *ip6)
+{
+	return true;
+}
+
+static inline int process_ra(struct ip6_hdr *ip6, int len)
+{
+	return 0;
+}
 #endif
 
 #endif /* __NDISC_H__ */
diff --git a/include/net.h b/include/net.h
index 8ba50a0..58774f6 100644
--- a/include/net.h
+++ b/include/net.h
@@ -505,7 +505,8 @@ extern int		net_restart_wrap;	/* Tried all network devices */
 
 enum proto_t {
 	BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP,
-	NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
+	NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI,
+	WGET, RS
 };
 
 extern char	net_boot_file_name[1024];/* Boot File name */
diff --git a/include/net6.h b/include/net6.h
index 2d7c5a0..beafc05 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -81,8 +81,17 @@ struct udp_hdr {
 			  0x00, 0x00, 0x00, 0x00, \
 			  0x00, 0x00, 0x00, 0x00, \
 			  0x00, 0x00, 0x00, 0x00 } } }
+/*
+ * All-routers multicast address is the link-local scope address to reach all
+ * routers.
+ */
+#define ALL_ROUTERS_MULT_ADDR { { { 0xFF, 0x02, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x02 } } }
 
 #define IPV6_LINK_LOCAL_PREFIX	0xfe80
+#define IPV6_LINK_LOCAL_MASK	0xffb0 /* The first 10-bit of address mask. */
 
 /* hop limit for neighbour discovery packets */
 #define IPV6_NDISC_HOPLIMIT             255
@@ -166,6 +175,37 @@ struct icmp6hdr {
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 } __packed;
 
+/*
+ * struct icmp6_ra_prefix_info - Prefix Information option of the ICMPv6 message
+ * The Prefix Information option provides hosts with on-link prefixes and
+ * prefixes for Address Autoconfiguration. Refer to RFC 4861 for more info.
+ */
+struct icmp6_ra_prefix_info {
+	u8	type;		/* Type is 3 for Prefix Information. */
+	u8	len;		/* Len is 4 for Prefix Information. */
+	/* The number of leading bits in the Prefix that are valid. */
+	u8	prefix_len;
+	u8	reserved1:6,	/* MUST be ignored by the receiver. */
+		aac:1,		/* autonomous address-configuration flag */
+	/* Indicates that this prefix can be used for on-link determination. */
+		on_link:1;
+	/*
+	 * The length of time in seconds that the prefix is valid for the
+	 * purpose of on-link determination.
+	 */
+	__be32	valid_lifetime;
+	/* The length of time addresses remain preferred. */
+	__be32	preferred_lifetime;
+	__be32	reserved2;	/* MUST be ignored by the receiver. */
+	/*
+	 * Prefix is an IP address or a prefix of an IP address. The Prefix
+	 * Length field contains the number of valid leading bits in the prefix.
+	 * The bits in the prefix after the prefix length are reserved and MUST
+	 * be initialized to zero by the sender and ignored by the receiver.
+	 */
+	struct in6_addr prefix;
+};
+
 extern struct in6_addr const net_null_addr_ip6;	/* NULL IPv6 address */
 extern struct in6_addr net_gateway6;	/* Our gateways IPv6 address */
 extern struct in6_addr net_ip6;	/* Our IPv6 addr (0 = unknown) */
diff --git a/net/ndisc.c b/net/ndisc.c
index 367dae7..0b27779 100644
--- a/net/ndisc.c
+++ b/net/ndisc.c
@@ -13,6 +13,8 @@
 #include <net.h>
 #include <net6.h>
 #include <ndisc.h>
+#include <stdlib.h>
+#include <linux/delay.h>
 
 /* IPv6 destination address of packet waiting for ND */
 struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
@@ -29,31 +31,37 @@ int net_nd_tx_packet_size;
 ulong net_nd_timer_start;
 /* the number of requests we have sent so far */
 int net_nd_try;
+struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR;
+
+#define MAX_RTR_SOLICITATIONS		3
+/* The maximum time to delay sending the first router solicitation message. */
+#define MAX_SOLICITATION_DELAY		1 // 1 second
+/* The time to wait before sending the next router solicitation message. */
+#define RTR_SOLICITATION_INTERVAL	4000 // 4 seconds
 
 #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7)
 
 /**
  * ndisc_insert_option() - Insert an option into a neighbor discovery packet
  *
- * @ndisc:	pointer to ND packet
+ * @opt:	pointer to the option element of the neighbor discovery packet
  * @type:	option type to insert
  * @data:	option data to insert
  * @len:	data length
  * Return: the number of bytes inserted (which may be >= len)
  */
-static int
-ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
+static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int len)
 {
 	int space = IP6_NDISC_OPT_SPACE(len);
 
-	ndisc->opt[0] = type;
-	ndisc->opt[1] = space >> 3;
-	memcpy(&ndisc->opt[2], data, len);
+	opt[0] = type;
+	opt[1] = space >> 3;
+	memcpy(&opt[2], data, len);
 	len += 2;
 
 	/* fill the remainder with 0 */
 	if (space - len > 0)
-		memset(&ndisc->opt[len], '\0', space - len);
+		memset(&opt[len], '\0', space - len);
 
 	return space;
 }
@@ -123,7 +131,7 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
 
 	/* Set the target address and llsaddr option */
 	net_copy_ip6(&msg->target, neigh_addr);
-	ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+	ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
 			    INETHADDRSZ);
 
 	/* checksum */
@@ -137,6 +145,76 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
 	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
 }
 
+/*
+ * ip6_send_rs() - Send IPv6 Router Solicitation Message.
+ *
+ * A router solicitation is sent to discover a router. RS message creation is
+ * based on RFC 4861 section 4.1. Router Solicitation Message Format.
+ */
+void ip6_send_rs(void)
+{
+	unsigned char enetaddr[6];
+	struct rs_msg *msg;
+	__u16 icmp_len;
+	uchar *pkt;
+	unsigned short csum;
+	unsigned int pcsum;
+	static unsigned int retry_count;
+
+	if (!ip6_is_unspecified_addr(&net_gateway6) &&
+	    net_prefix_length != 0) {
+		net_set_state(NETLOOP_SUCCESS);
+		return;
+	} else if (retry_count >= MAX_RTR_SOLICITATIONS) {
+		net_set_state(NETLOOP_FAIL);
+		net_set_timeout_handler(0, NULL);
+		retry_count = 0;
+		return;
+	}
+
+	printf("ROUTER SOLICITATION %d\n", retry_count + 1);
+
+	ip6_make_mult_ethdstaddr(enetaddr, &all_routers);
+	/*
+	 * ICMP length is the size of ICMP header (8) + one option (8) = 16.
+	 * The option is 2 bytes of type and length + 6 bytes for MAC.
+	 */
+	icmp_len = sizeof(struct icmp6hdr) + IP6_NDISC_OPT_SPACE(INETHADDRSZ);
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers, PROT_ICMPV6,
+			   IPV6_NDISC_HOPLIMIT, icmp_len);
+
+	/* ICMPv6 - RS */
+	msg = (struct rs_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION;
+	msg->icmph.icmp6_code = 0;
+	memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
+	memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
+
+	/* Set the llsaddr option */
+	ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+			    INETHADDRSZ);
+
+	/* checksum */
+	pcsum = csum_partial((__u8 *)msg, icmp_len, 0);
+	csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers,
+			       icmp_len, PROT_ICMPV6, pcsum);
+	msg->icmph.icmp6_cksum = csum;
+	pkt += icmp_len;
+
+	/* Wait up to 1 second if it is the first try to get the RA */
+	if (retry_count == 0)
+		udelay(((unsigned int)rand() % 1000000) * MAX_SOLICITATION_DELAY);
+
+	/* send it! */
+	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
+
+	retry_count++;
+	net_set_timeout_handler(RTR_SOLICITATION_INTERVAL, ip6_send_rs);
+}
+
 static void
 ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
 	    struct in6_addr *target)
@@ -167,7 +245,7 @@ ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
 	msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
 	/* Set the target address and lltargetaddr option */
 	net_copy_ip6(&msg->target, target);
-	ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
+	ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
 			    INETHADDRSZ);
 
 	/* checksum */
@@ -223,6 +301,10 @@ int ndisc_timeout_check(void)
 	return 1;
 }
 
+/*
+ * ndisc_init() - Make initial steps for ND state machine.
+ * Usually move variables into initial state.
+ */
 void ndisc_init(void)
 {
 	net_nd_packet_mac = NULL;
@@ -234,12 +316,125 @@ void ndisc_init(void)
 	net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
 }
 
+/*
+ * validate_ra() - Validate the router advertisement message.
+ *
+ * @ip6: Pointer to the router advertisement packet
+ *
+ * Check if the router advertisement message is valid. Conditions are
+ * according to RFC 4861 section 6.1.2. Validation of Router Advertisement
+ * Messages.
+ *
+ * Return: true if the message is valid and false if it is invalid.
+ */
+bool validate_ra(struct ip6_hdr *ip6)
+{
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+
+	/* ICMP length (derived from the IP length) should be 16 or more octets. */
+	if (ip6->payload_len < 16)
+		return false;
+
+	/* Source IP Address should be a valid link-local address. */
+	if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK) !=
+	    IPV6_LINK_LOCAL_PREFIX)
+		return false;
+
+	/*
+	 * The IP Hop Limit field should have a value of 255, i.e., the packet
+	 * could not possibly have been forwarded by a router.
+	 */
+	if (ip6->hop_limit != 255)
+		return false;
+
+	/* ICMP checksum has already been checked in net_ip6_handler. */
+
+	if (icmp->icmp6_code != 0)
+		return false;
+
+	return true;
+}
+
+/*
+ * process_ra() - Process the router advertisement packet.
+ *
+ * @ip6: Pointer to the router advertisement packet
+ * @len: Length of the router advertisement packet
+ *
+ * Process the received router advertisement message.
+ * Although RFC 4861 requires retaining at least two router addresses, we only
+ * keep one because of the U-Boot limitations and its goal of lightweight code.
+ *
+ * Return: 0 - RA is a default router and contains valid prefix information.
+ * Non-zero - RA options are invalid or do not indicate it is a default router
+ * or do not contain valid prefix information.
+ */
+int process_ra(struct ip6_hdr *ip6, int len)
+{
+	/* Pointer to the ICMP section of the packet */
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	struct ra_msg *msg = (struct ra_msg *)icmp;
+	int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct ra_msg);
+	unsigned short int option_len;	/* Length of each option */
+	/* Pointer to the ICMPv6 message options */
+	unsigned char *option = NULL;
+	/* 8-bit identifier of the type of ICMPv6 option */
+	unsigned char type = 0;
+	struct icmp6_ra_prefix_info *prefix = NULL;
+
+	/* Ignore the packet if router lifetime is 0. */
+	if (!icmp->icmp6_rt_lifetime)
+		return -EOPNOTSUPP;
+
+	/* Processing the options */
+	option = msg->opt;
+	while (remaining_option_len > 0) {
+		/* The 2nd byte of the option is its length. */
+		option_len = option[1];
+		/* All included options should have a positive length. */
+		if (option_len == 0)
+			return -EINVAL;
+
+		type = option[0];
+		/* All option types except Prefix Information are ignored. */
+		switch (type) {
+		case ND_OPT_SOURCE_LL_ADDR:
+		case ND_OPT_TARGET_LL_ADDR:
+		case ND_OPT_REDIRECT_HDR:
+		case ND_OPT_MTU:
+			break;
+		case ND_OPT_PREFIX_INFO:
+			prefix = (struct icmp6_ra_prefix_info *)option;
+			/* The link-local prefix 0xfe80::/10 is ignored. */
+			if ((ntohs(prefix->prefix.s6_addr16[0]) &
+			     IPV6_LINK_LOCAL_MASK) == IPV6_LINK_LOCAL_PREFIX)
+				break;
+			if (prefix->on_link && ntohl(prefix->valid_lifetime)) {
+				net_prefix_length = prefix->prefix_len;
+				net_gateway6 = ip6->saddr;
+				return 0;
+			}
+			break;
+		default:
+			debug("Unknown IPv6 Neighbor Discovery Option 0x%x\n",
+			      type);
+		}
+
+		option_len <<= 3; /* Option length is a multiple of 8. */
+		remaining_option_len -= option_len;
+		option += option_len;
+	}
+
+	return -EADDRNOTAVAIL;
+}
+
 int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 {
 	struct icmp6hdr *icmp =
 	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
 	struct nd_msg *ndisc = (struct nd_msg *)icmp;
 	uchar neigh_eth_addr[6];
+	int err = 0;	// The error code returned calling functions.
 
 	switch (icmp->icmp6_type) {
 	case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
@@ -280,6 +475,36 @@ int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 			net_nd_packet_mac = NULL;
 		}
 		break;
+	case IPV6_NDISC_ROUTER_SOLICITATION:
+		break;
+	case IPV6_NDISC_ROUTER_ADVERTISEMENT:
+		debug("Received router advertisement for %pI6c from %pI6c\n",
+		      &ip6->daddr, &ip6->saddr);
+		/*
+		 * If gateway and prefix are set, the RA packet is ignored. The
+		 * reason is that the U-Boot code is supposed to be as compact
+		 * as possible and does not need to take care of multiple
+		 * routers. In addition to that, U-Boot does not want to handle
+		 * scenarios like a router setting its lifetime to zero to
+		 * indicate it is not routing anymore. U-Boot program has a
+		 * short life when the system boots up and does not need such
+		 * sophistication.
+		 */
+		if (!ip6_is_unspecified_addr(&net_gateway6) &&
+		    net_prefix_length != 0) {
+			break;
+		}
+		if (!validate_ra(ip6)) {
+			debug("Invalid router advertisement message.\n");
+			break;
+		}
+		err = process_ra(ip6, len);
+		if (err)
+			debug("Ignored router advertisement. Error: %d\n", err);
+		else
+			printf("Set gatewayip6: %pI6c, prefix_length: %d\n",
+			       &net_gateway6, net_prefix_length);
+		break;
 	default:
 		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
 		return -1;
diff --git a/net/net.c b/net/net.c
index d69bfb0..abdb7e4 100644
--- a/net/net.c
+++ b/net/net.c
@@ -24,7 +24,7 @@
  *			- name of bootfile
  *	Next step:	ARP
  *
- * LINK_LOCAL:
+ * LINKLOCAL:
  *
  *	Prerequisites:	- own ethernet address
  *	We want:	- own IP address
@@ -125,6 +125,7 @@
 #if defined(CONFIG_CMD_DHCP6)
 #include "dhcpv6.h"
 #endif
+#include "net_rand.h"
 
 /** BOOTP EXTENTIONS **/
 
@@ -349,6 +350,8 @@ void net_auto_load(void)
 
 static int net_init_loop(void)
 {
+	static bool first_call = true;
+
 	if (eth_get_dev()) {
 		memcpy(net_ethaddr, eth_get_ethaddr(), 6);
 
@@ -368,6 +371,12 @@ static int net_init_loop(void)
 		 */
 		return -ENONET;
 
+	if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+		if (first_call && use_ip6) {
+			first_call = false;
+			srand_mac(); /* This is for rand used in ip6_send_rs. */
+			net_loop(RS);
+		}
 	return 0;
 }
 
@@ -577,6 +586,10 @@ restart:
 			ncsi_probe_packages();
 			break;
 #endif
+		case RS:
+			if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+				ip6_send_rs();
+			break;
 		default:
 			break;
 		}
@@ -674,7 +687,13 @@ restart:
 			x = time_handler;
 			time_handler = (thand_f *)0;
 			(*x)();
-		}
+		} else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+			if (time_handler && protocol == RS)
+				if (!ip6_is_unspecified_addr(&net_gateway6) &&
+				    net_prefix_length != 0) {
+					net_set_state(NETLOOP_SUCCESS);
+					net_set_timeout_handler(0, NULL);
+				}
 
 		if (net_state == NETLOOP_FAIL)
 			ret = net_start_again();
diff --git a/net/net6.c b/net/net6.c
index 75577bc..2dd64c0 100644
--- a/net/net6.c
+++ b/net/net6.c
@@ -413,6 +413,7 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 			break;
 		case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
 		case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+		case IPV6_NDISC_ROUTER_ADVERTISEMENT:
 			ndisc_receive(et, ip6, len);
 			break;
 		default:
-- 
1.8.3.1


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

* [PATCH v2 3/4] test/py: IPv6 network discovery test
  2023-04-10 19:34 ` [PATCH v2 0/4] Add IPv6 Network Discovery emohandesi
  2023-04-10 19:34   ` [PATCH v2 1/4] Revert "net: ipv6: Add support for default gateway discovery." emohandesi
  2023-04-10 19:34   ` [PATCH v2 2/4] net: ipv6: Add support for default gateway discovery emohandesi
@ 2023-04-10 19:34   ` emohandesi
  2023-04-10 19:34   ` [PATCH v2 4/4] test: eth: IPv6 network discovery unit test emohandesi
  3 siblings, 0 replies; 35+ messages in thread
From: emohandesi @ 2023-04-10 19:34 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, sjg, xypron.glpk, ilias.apalodimas,
	masahisa.kojima, tobias, john, v.v.mitrofanov, saproj, mario.six

From: Ehsan Mohandesi <emohandesi@microsoft.com>

Test the IPv6 network discovery feature if indicated by boardenv file.

Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>

Conflicts:
	configs/sandbox64_defconfig
	configs/sandbox_defconfig
	configs/sandbox_flattree_defconfig
---
 configs/sandbox64_defconfig        |  2 ++
 configs/sandbox_defconfig          |  2 ++
 configs/sandbox_flattree_defconfig |  2 ++
 test/py/tests/test_net.py          | 31 ++++++++++++++++++++++++++++++-
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index af2c56a..be36ede 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -260,3 +260,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ca95b2c..0673c69 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -341,3 +341,5 @@ CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
 CONFIG_CMD_2048=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index e9fcc5b..d6c8dd2 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -229,3 +229,5 @@ CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py
index 9ca6743..f85071d 100644
--- a/test/py/tests/test_net.py
+++ b/test/py/tests/test_net.py
@@ -9,7 +9,7 @@ import u_boot_utils
 
 """
 Note: This test relies on boardenv_* containing configuration values to define
-which the network environment available for testing. Without this, this test
+which network environment is available for testing. Without this, this test
 will be automatically skipped.
 
 For example:
@@ -55,6 +55,11 @@ env__net_nfs_readable_file = {
     'size': 5058624,
     'crc32': 'c2244b26',
 }
+
+# True if a router advertisement service is connected to the network, and should
+# be tested. If router advertisement testing is not possible or desired, this
+variable may be omitted or set to False.
+env__router_on_net = True
 """
 
 net_set_up = False
@@ -126,6 +131,30 @@ def test_net_ping(u_boot_console):
     output = u_boot_console.run_command('ping $serverip')
     assert 'is alive' in output
 
+@pytest.mark.buildconfigspec('IPV6_ROUTER_DISCOVERY')
+def test_net_network_discovery(u_boot_console):
+    """Test the network discovery feature of IPv6.
+
+    An IPv6 network command (ping6 in this case) is run to make U-Boot send a
+    router solicitation packet, receive a router advertisement message, and
+    parse it.
+    A router advertisement service needs to be running for this test to succeed.
+    U-Boot receives the RA, processes it, and if successful, assigns the gateway
+    IP and prefix length.
+    The configuration is provided by the boardenv_* file; see the comment at
+    the beginning of this file.
+    """
+
+    router_on_net = u_boot_console.config.env.get('env__router_on_net', False)
+    if not router_on_net:
+        pytest.skip('No router on network')
+
+    fake_host_ip = 'fe80::215:5dff:fef6:2ec6'
+    output = u_boot_console.run_command('ping6 ' + fake_host_ip)
+    assert 'ROUTER SOLICITATION 1' in output
+    assert 'Set gatewayip6:' in output
+    assert '0000:0000:0000:0000:0000:0000:0000:0000' not in output
+
 @pytest.mark.buildconfigspec('cmd_net')
 def test_net_tftpboot(u_boot_console):
     """Test the tftpboot command.
-- 
1.8.3.1


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

* [PATCH v2 4/4] test: eth: IPv6 network discovery unit test
  2023-04-10 19:34 ` [PATCH v2 0/4] Add IPv6 Network Discovery emohandesi
                     ` (2 preceding siblings ...)
  2023-04-10 19:34   ` [PATCH v2 3/4] test/py: IPv6 network discovery test emohandesi
@ 2023-04-10 19:34   ` emohandesi
  3 siblings, 0 replies; 35+ messages in thread
From: emohandesi @ 2023-04-10 19:34 UTC (permalink / raw)
  To: u-boot
  Cc: joe.hershberger, rfried.dev, sjg, xypron.glpk, ilias.apalodimas,
	masahisa.kojima, tobias, john, v.v.mitrofanov, saproj, mario.six

From: Ehsan Mohandesi <emohandesi@microsoft.com>

Test router advertisement validation and processing functions.

Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
---
 test/dm/eth.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/test/dm/eth.c b/test/dm/eth.c
index ebf01d8..d05d2a9 100644
--- a/test/dm/eth.c
+++ b/test/dm/eth.c
@@ -20,6 +20,7 @@
 #include <dm/uclass-internal.h>
 #include <test/test.h>
 #include <test/ut.h>
+#include <ndisc.h>
 
 #define DM_TEST_ETH_NUM		4
 
@@ -607,3 +608,90 @@ static int dm_test_eth_async_ping_reply(struct unit_test_state *uts)
 }
 
 DM_TEST(dm_test_eth_async_ping_reply, UT_TESTF_SCAN_FDT);
+
+#if IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)
+
+static u8 ip6_ra_buf[] = {0x60, 0xf, 0xc5, 0x4a, 0x0, 0x38, 0x3a, 0xff, 0xfe,
+			  0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x85, 0xe6,
+			  0x29, 0x77, 0xcb, 0xc8, 0x53, 0xff, 0x2, 0x0, 0x0,
+			  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+			  0x1, 0x86, 0x0, 0xdc, 0x90, 0x40, 0x80, 0x15, 0x18,
+			  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x4,
+			  0x40, 0xc0, 0x0, 0x0, 0x37, 0xdc, 0x0, 0x0, 0x37,
+			  0x78, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1, 0xca, 0xfe, 0xca,
+			  0xfe, 0xca, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+			  0x0, 0x1, 0x1, 0x0, 0x15, 0x5d, 0xe2, 0x8a, 0x2};
+
+static int dm_test_validate_ra(struct unit_test_state *uts)
+{
+	struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	__be16 temp = 0;
+
+	ut_assert(validate_ra(ip6) == true);
+
+	temp = ip6->payload_len;
+	ip6->payload_len = 15;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->payload_len = temp;
+
+	temp = ip6->saddr.s6_addr16[0];
+	ip6->saddr.s6_addr16[0] = 0x2001;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->saddr.s6_addr16[0] = temp;
+
+	temp = ip6->hop_limit;
+	ip6->hop_limit = 15;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->hop_limit = temp;
+
+	temp = icmp->icmp6_code;
+	icmp->icmp6_code = 15;
+	ut_assert(validate_ra(ip6) == false);
+	icmp->icmp6_code = temp;
+
+	return 0;
+}
+
+DM_TEST(dm_test_validate_ra, 0);
+
+static int dm_test_process_ra(struct unit_test_state *uts)
+{
+	int len = sizeof(ip6_ra_buf);
+	struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	struct ra_msg *msg = (struct ra_msg *)icmp;
+	unsigned char *option = msg->opt;
+	struct icmp6_ra_prefix_info *prefix =
+					(struct icmp6_ra_prefix_info *)option;
+	__be16 temp = 0;
+	unsigned char option_len = option[1];
+
+	ut_assert(process_ra(ip6, len) == 0);
+
+	temp = icmp->icmp6_rt_lifetime;
+	icmp->icmp6_rt_lifetime = 0;
+	ut_assert(process_ra(ip6, len) != 0);
+	icmp->icmp6_rt_lifetime = temp;
+
+	ut_assert(process_ra(ip6, 0) != 0);
+
+	option[1] = 0;
+	ut_assert(process_ra(ip6, len) != 0);
+	option[1] = option_len;
+
+	prefix->on_link = false;
+	ut_assert(process_ra(ip6, len) != 0);
+	prefix->on_link = true;
+
+	temp = prefix->prefix.s6_addr16[0];
+	prefix->prefix.s6_addr16[0] = 0x80fe;
+	ut_assert(process_ra(ip6, len) != 0);
+	prefix->prefix.s6_addr16[0] = temp;
+
+	return 0;
+}
+
+DM_TEST(dm_test_process_ra, 0);
+
+#endif
-- 
1.8.3.1


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

* Re: [PATCH v2 1/4] Revert "net: ipv6: Add support for default gateway discovery."
  2023-04-10 19:34   ` [PATCH v2 1/4] Revert "net: ipv6: Add support for default gateway discovery." emohandesi
@ 2023-04-11  7:01     ` Vyacheslav V. Mitrofanov
  2023-04-11 14:51       ` Ehsan Mohandesi
  0 siblings, 1 reply; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-04-11  7:01 UTC (permalink / raw)
  To: u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, tobias, saproj, rfried.dev,
	ilias.apalodimas, mario.six, john, sjg, masahisa.kojima

On Mon, 2023-04-10 at 12:34 -0700, emohandesi@linux.microsoft.com
wrote:
> 
> From: Ehsan Mohandesi <emohandesi@microsoft.com>
> 
> This reverts commit 0af1035a55d9c1486b2db43ee70ff0a63affd4f4.
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
> 
> Conflicts:
>         cmd/Kconfig
>         include/net.h
>         include/net6.h
>         net/net.c
> ---
>  include/net.h | 4 ++--
>  net/net.c     | 3 +++
>  2 files changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/include/net.h b/include/net.h
> index 399af5e..8ba50a0 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -504,8 +504,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/net.c b/net/net.c
> index c9a749f..d69bfb0 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 **/
> 
> --
> 1.8.3.1
> 
> 
Hello, Ehsan!

Is everything ok with this patch set?
You reverted the commit that is absent in upstream.

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

* Re: [PATCH v2 1/4] Revert "net: ipv6: Add support for default gateway discovery."
  2023-04-11  7:01     ` Vyacheslav V. Mitrofanov
@ 2023-04-11 14:51       ` Ehsan Mohandesi
  0 siblings, 0 replies; 35+ messages in thread
From: Ehsan Mohandesi @ 2023-04-11 14:51 UTC (permalink / raw)
  To: Vyacheslav V. Mitrofanov, u-boot
  Cc: joe.hershberger, xypron.glpk, tobias, saproj, rfried.dev,
	ilias.apalodimas, mario.six, john, sjg, masahisa.kojima

You are right, Viacheslav. I should not have included the revert commit 
in my patch. I think I need to remove it and send a v3 of my patch series.

On 4/11/2023 2:01 AM, Vyacheslav V. Mitrofanov wrote:
> On Mon, 2023-04-10 at 12:34 -0700, emohandesi@linux.microsoft.com
> wrote:
>> From: Ehsan Mohandesi <emohandesi@microsoft.com>
>>
>> This reverts commit 0af1035a55d9c1486b2db43ee70ff0a63affd4f4.
>>
>> Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
>>
>> Conflicts:
>>          cmd/Kconfig
>>          include/net.h
>>          include/net6.h
>>          net/net.c
>> ---
>>   include/net.h | 4 ++--
>>   net/net.c     | 3 +++
>>   2 files changed, 5 insertions(+), 2 deletions(-)
>>
>> diff --git a/include/net.h b/include/net.h
>> index 399af5e..8ba50a0 100644
>> --- a/include/net.h
>> +++ b/include/net.h
>> @@ -504,8 +504,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/net.c b/net/net.c
>> index c9a749f..d69bfb0 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 **/
>>
>> --
>> 1.8.3.1
>>
>>
> Hello, Ehsan!
>
> Is everything ok with this patch set?
> You reverted the commit that is absent in upstream.

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

* [PATCH v3 0/3] Add IPv6 Network Discovery
  2023-03-02 16:58 [PATCH] net: ipv6: Add support for default gateway discovery emohandesi
                   ` (2 preceding siblings ...)
  2023-04-10 19:34 ` [PATCH v2 0/4] Add IPv6 Network Discovery emohandesi
@ 2023-04-12 16:10 ` emohandesi
  2023-04-12 16:10   ` [PATCH v3 1/3] net: ipv6: Add support for default gateway discovery emohandesi
                     ` (2 more replies)
  2023-04-22  0:08 ` [PATCH v4 0/3] Add IPv6 Network Discovery emohandesi
  4 siblings, 3 replies; 35+ messages in thread
From: emohandesi @ 2023-04-12 16:10 UTC (permalink / raw)
  To: u-boot
  Cc: sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, tobias, john, v.v.mitrofanov,
	saproj

From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>

This series adds IPv6 network discovery to U-Boot. When an IPv6 command is
run in U-Boot, it sends a router solicitation (RS) message to the network.
The router on the network responds with a router advertisement (RA)
message. Then U-Boot processes the RA message and sets the gatewayip6 and
net_prefix_length environment variables.
It is based on RFC 4861, but not everything in the RFC is supported here.
https://www.rfc-editor.org/rfc/rfc4861

Changes in v3:
- Removed the extra revert commit that was mistakenly added in v2.

Changes in v2:
- Improved IPv6 network discovery code.
- Added IPv6 network discovery feature test (Python test).
- Added unit tests (C code).

Ehsan Mohandesi (3):
  net: ipv6: Add support for default gateway discovery.
  test/py: IPv6 network discovery test
  test: eth: IPv6 network discovery unit test

 cmd/Kconfig                        |   6 +
 configs/sandbox64_defconfig        |   2 +
 configs/sandbox_defconfig          |   2 +
 configs/sandbox_flattree_defconfig |   2 +
 include/ndisc.h                    |  35 ++++++
 include/net.h                      |   3 +-
 include/net6.h                     |  40 ++++++
 net/ndisc.c                        | 243 +++++++++++++++++++++++++++++++++++--
 net/net.c                          |  23 +++-
 net/net6.c                         |   1 +
 test/dm/eth.c                      |  88 ++++++++++++++
 test/py/tests/test_net.py          |  31 ++++-
 12 files changed, 463 insertions(+), 13 deletions(-)

-- 
1.8.3.1


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

* [PATCH v3 1/3] net: ipv6: Add support for default gateway discovery.
  2023-04-12 16:10 ` [PATCH v3 0/3] Add IPv6 Network Discovery emohandesi
@ 2023-04-12 16:10   ` emohandesi
  2023-04-20 13:44     ` Vyacheslav V. Mitrofanov
  2023-04-12 16:10   ` [PATCH v3 2/3] test/py: IPv6 network discovery test emohandesi
  2023-04-12 16:10   ` [PATCH v3 3/3] test: eth: IPv6 network discovery unit test emohandesi
  2 siblings, 1 reply; 35+ messages in thread
From: emohandesi @ 2023-04-12 16:10 UTC (permalink / raw)
  To: u-boot
  Cc: sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, tobias, john, v.v.mitrofanov,
	saproj

From: Ehsan Mohandesi <emohandesi@microsoft.com>

In IPv6, the default gateway and prefix length are determined by receiving
a router advertisement as defined in -
https://www.rfc-editor.org/rfc/rfc4861.

Add support for sending router solicitation (RS) and processing router
advertisements (RA).

If the RA has prefix info option and following conditions are met, then
gatewayip6 and net_prefix_length of ip6addr env variables are initialized.
These are later consumed by IPv6 code for non-local destination IP.

- "Router Lifetime" != 0
- Prefix is NOT link-local prefix (0xfe80::/10)
- L flag is 1
- "Valid Lifetime" != 0

Timing Parameters:
- MAX_RTR_SOLICITATION_DELAY (0-1s)
- RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
- MAX_RTR_SOLICITATIONS (3 RS transmissions)

The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and invoked
automatically from net_init_loop().

Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>

Conflicts:
	cmd/Kconfig
	net/net.c
---
 cmd/Kconfig     |   6 ++
 include/ndisc.h |  35 ++++++++
 include/net.h   |   3 +-
 include/net6.h  |  40 ++++++++++
 net/ndisc.c     | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 net/net.c       |  23 +++++-
 net/net6.c      |   1 +
 7 files changed, 339 insertions(+), 12 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index e45b884..6919d31 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1916,6 +1916,12 @@ config CMD_NCSI
 	  Normally this happens automatically before other network
 	  operations.
 
+config IPV6_ROUTER_DISCOVERY
+	bool "Do IPv6 router discovery"
+	depends on IPV6
+	help
+	  Will automatically perform router solicitation on first IPv6
+	  network operation
 endif
 
 config CMD_ETHSW
diff --git a/include/ndisc.h b/include/ndisc.h
index f6f8eb6..12fa9e7 100644
--- a/include/ndisc.h
+++ b/include/ndisc.h
@@ -19,6 +19,20 @@ struct nd_msg {
 	__u8		opt[0];
 };
 
+/* struct rs_msg - ICMPv6 Router Solicitation message format */
+struct rs_msg {
+	struct icmp6hdr	icmph;
+	__u8		opt[0];
+};
+
+/* struct ra_msg - ICMPv6 Router Advertisement message format */
+struct ra_msg {
+	struct icmp6hdr	icmph;
+	__u32	reachable_time;
+	__u32	retransmission_timer;
+	__u8	opt[0];
+};
+
 /* struct echo_msg - ICMPv6 echo request/reply message format */
 struct echo_msg {
 	struct icmp6hdr	icmph;
@@ -57,6 +71,11 @@ extern int net_nd_try;
  */
 void ndisc_init(void);
 
+/*
+ * ip6_send_rs() - Send IPv6 Router Solicitation Message
+ */
+void ip6_send_rs(void);
+
 /**
  * ndisc_receive() - Handle ND packet
  *
@@ -78,6 +97,8 @@ void ndisc_request(void);
  * Return: 0 if no timeout, -1 otherwise
  */
 int ndisc_timeout_check(void);
+bool validate_ra(struct ip6_hdr *ip6);
+int process_ra(struct ip6_hdr *ip6, int len);
 #else
 static inline void ndisc_init(void)
 {
@@ -97,6 +118,20 @@ static inline int ndisc_timeout_check(void)
 {
 	return 0;
 }
+
+void ip6_send_rs(void)
+{
+}
+
+static inline bool validate_ra(struct ip6_hdr *ip6)
+{
+	return true;
+}
+
+static inline int process_ra(struct ip6_hdr *ip6, int len)
+{
+	return 0;
+}
 #endif
 
 #endif /* __NDISC_H__ */
diff --git a/include/net.h b/include/net.h
index 8ba50a0..58774f6 100644
--- a/include/net.h
+++ b/include/net.h
@@ -505,7 +505,8 @@ extern int		net_restart_wrap;	/* Tried all network devices */
 
 enum proto_t {
 	BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS, NFS, CDP,
-	NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET
+	NETCONS, SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI,
+	WGET, RS
 };
 
 extern char	net_boot_file_name[1024];/* Boot File name */
diff --git a/include/net6.h b/include/net6.h
index 2d7c5a0..beafc05 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -81,8 +81,17 @@ struct udp_hdr {
 			  0x00, 0x00, 0x00, 0x00, \
 			  0x00, 0x00, 0x00, 0x00, \
 			  0x00, 0x00, 0x00, 0x00 } } }
+/*
+ * All-routers multicast address is the link-local scope address to reach all
+ * routers.
+ */
+#define ALL_ROUTERS_MULT_ADDR { { { 0xFF, 0x02, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x02 } } }
 
 #define IPV6_LINK_LOCAL_PREFIX	0xfe80
+#define IPV6_LINK_LOCAL_MASK	0xffb0 /* The first 10-bit of address mask. */
 
 /* hop limit for neighbour discovery packets */
 #define IPV6_NDISC_HOPLIMIT             255
@@ -166,6 +175,37 @@ struct icmp6hdr {
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 } __packed;
 
+/*
+ * struct icmp6_ra_prefix_info - Prefix Information option of the ICMPv6 message
+ * The Prefix Information option provides hosts with on-link prefixes and
+ * prefixes for Address Autoconfiguration. Refer to RFC 4861 for more info.
+ */
+struct icmp6_ra_prefix_info {
+	u8	type;		/* Type is 3 for Prefix Information. */
+	u8	len;		/* Len is 4 for Prefix Information. */
+	/* The number of leading bits in the Prefix that are valid. */
+	u8	prefix_len;
+	u8	reserved1:6,	/* MUST be ignored by the receiver. */
+		aac:1,		/* autonomous address-configuration flag */
+	/* Indicates that this prefix can be used for on-link determination. */
+		on_link:1;
+	/*
+	 * The length of time in seconds that the prefix is valid for the
+	 * purpose of on-link determination.
+	 */
+	__be32	valid_lifetime;
+	/* The length of time addresses remain preferred. */
+	__be32	preferred_lifetime;
+	__be32	reserved2;	/* MUST be ignored by the receiver. */
+	/*
+	 * Prefix is an IP address or a prefix of an IP address. The Prefix
+	 * Length field contains the number of valid leading bits in the prefix.
+	 * The bits in the prefix after the prefix length are reserved and MUST
+	 * be initialized to zero by the sender and ignored by the receiver.
+	 */
+	struct in6_addr prefix;
+};
+
 extern struct in6_addr const net_null_addr_ip6;	/* NULL IPv6 address */
 extern struct in6_addr net_gateway6;	/* Our gateways IPv6 address */
 extern struct in6_addr net_ip6;	/* Our IPv6 addr (0 = unknown) */
diff --git a/net/ndisc.c b/net/ndisc.c
index 367dae7..0b27779 100644
--- a/net/ndisc.c
+++ b/net/ndisc.c
@@ -13,6 +13,8 @@
 #include <net.h>
 #include <net6.h>
 #include <ndisc.h>
+#include <stdlib.h>
+#include <linux/delay.h>
 
 /* IPv6 destination address of packet waiting for ND */
 struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
@@ -29,31 +31,37 @@ int net_nd_tx_packet_size;
 ulong net_nd_timer_start;
 /* the number of requests we have sent so far */
 int net_nd_try;
+struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR;
+
+#define MAX_RTR_SOLICITATIONS		3
+/* The maximum time to delay sending the first router solicitation message. */
+#define MAX_SOLICITATION_DELAY		1 // 1 second
+/* The time to wait before sending the next router solicitation message. */
+#define RTR_SOLICITATION_INTERVAL	4000 // 4 seconds
 
 #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7)
 
 /**
  * ndisc_insert_option() - Insert an option into a neighbor discovery packet
  *
- * @ndisc:	pointer to ND packet
+ * @opt:	pointer to the option element of the neighbor discovery packet
  * @type:	option type to insert
  * @data:	option data to insert
  * @len:	data length
  * Return: the number of bytes inserted (which may be >= len)
  */
-static int
-ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
+static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int len)
 {
 	int space = IP6_NDISC_OPT_SPACE(len);
 
-	ndisc->opt[0] = type;
-	ndisc->opt[1] = space >> 3;
-	memcpy(&ndisc->opt[2], data, len);
+	opt[0] = type;
+	opt[1] = space >> 3;
+	memcpy(&opt[2], data, len);
 	len += 2;
 
 	/* fill the remainder with 0 */
 	if (space - len > 0)
-		memset(&ndisc->opt[len], '\0', space - len);
+		memset(&opt[len], '\0', space - len);
 
 	return space;
 }
@@ -123,7 +131,7 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
 
 	/* Set the target address and llsaddr option */
 	net_copy_ip6(&msg->target, neigh_addr);
-	ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+	ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
 			    INETHADDRSZ);
 
 	/* checksum */
@@ -137,6 +145,76 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
 	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
 }
 
+/*
+ * ip6_send_rs() - Send IPv6 Router Solicitation Message.
+ *
+ * A router solicitation is sent to discover a router. RS message creation is
+ * based on RFC 4861 section 4.1. Router Solicitation Message Format.
+ */
+void ip6_send_rs(void)
+{
+	unsigned char enetaddr[6];
+	struct rs_msg *msg;
+	__u16 icmp_len;
+	uchar *pkt;
+	unsigned short csum;
+	unsigned int pcsum;
+	static unsigned int retry_count;
+
+	if (!ip6_is_unspecified_addr(&net_gateway6) &&
+	    net_prefix_length != 0) {
+		net_set_state(NETLOOP_SUCCESS);
+		return;
+	} else if (retry_count >= MAX_RTR_SOLICITATIONS) {
+		net_set_state(NETLOOP_FAIL);
+		net_set_timeout_handler(0, NULL);
+		retry_count = 0;
+		return;
+	}
+
+	printf("ROUTER SOLICITATION %d\n", retry_count + 1);
+
+	ip6_make_mult_ethdstaddr(enetaddr, &all_routers);
+	/*
+	 * ICMP length is the size of ICMP header (8) + one option (8) = 16.
+	 * The option is 2 bytes of type and length + 6 bytes for MAC.
+	 */
+	icmp_len = sizeof(struct icmp6hdr) + IP6_NDISC_OPT_SPACE(INETHADDRSZ);
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers, PROT_ICMPV6,
+			   IPV6_NDISC_HOPLIMIT, icmp_len);
+
+	/* ICMPv6 - RS */
+	msg = (struct rs_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION;
+	msg->icmph.icmp6_code = 0;
+	memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
+	memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
+
+	/* Set the llsaddr option */
+	ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+			    INETHADDRSZ);
+
+	/* checksum */
+	pcsum = csum_partial((__u8 *)msg, icmp_len, 0);
+	csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers,
+			       icmp_len, PROT_ICMPV6, pcsum);
+	msg->icmph.icmp6_cksum = csum;
+	pkt += icmp_len;
+
+	/* Wait up to 1 second if it is the first try to get the RA */
+	if (retry_count == 0)
+		udelay(((unsigned int)rand() % 1000000) * MAX_SOLICITATION_DELAY);
+
+	/* send it! */
+	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
+
+	retry_count++;
+	net_set_timeout_handler(RTR_SOLICITATION_INTERVAL, ip6_send_rs);
+}
+
 static void
 ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
 	    struct in6_addr *target)
@@ -167,7 +245,7 @@ ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
 	msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
 	/* Set the target address and lltargetaddr option */
 	net_copy_ip6(&msg->target, target);
-	ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
+	ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
 			    INETHADDRSZ);
 
 	/* checksum */
@@ -223,6 +301,10 @@ int ndisc_timeout_check(void)
 	return 1;
 }
 
+/*
+ * ndisc_init() - Make initial steps for ND state machine.
+ * Usually move variables into initial state.
+ */
 void ndisc_init(void)
 {
 	net_nd_packet_mac = NULL;
@@ -234,12 +316,125 @@ void ndisc_init(void)
 	net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
 }
 
+/*
+ * validate_ra() - Validate the router advertisement message.
+ *
+ * @ip6: Pointer to the router advertisement packet
+ *
+ * Check if the router advertisement message is valid. Conditions are
+ * according to RFC 4861 section 6.1.2. Validation of Router Advertisement
+ * Messages.
+ *
+ * Return: true if the message is valid and false if it is invalid.
+ */
+bool validate_ra(struct ip6_hdr *ip6)
+{
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+
+	/* ICMP length (derived from the IP length) should be 16 or more octets. */
+	if (ip6->payload_len < 16)
+		return false;
+
+	/* Source IP Address should be a valid link-local address. */
+	if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK) !=
+	    IPV6_LINK_LOCAL_PREFIX)
+		return false;
+
+	/*
+	 * The IP Hop Limit field should have a value of 255, i.e., the packet
+	 * could not possibly have been forwarded by a router.
+	 */
+	if (ip6->hop_limit != 255)
+		return false;
+
+	/* ICMP checksum has already been checked in net_ip6_handler. */
+
+	if (icmp->icmp6_code != 0)
+		return false;
+
+	return true;
+}
+
+/*
+ * process_ra() - Process the router advertisement packet.
+ *
+ * @ip6: Pointer to the router advertisement packet
+ * @len: Length of the router advertisement packet
+ *
+ * Process the received router advertisement message.
+ * Although RFC 4861 requires retaining at least two router addresses, we only
+ * keep one because of the U-Boot limitations and its goal of lightweight code.
+ *
+ * Return: 0 - RA is a default router and contains valid prefix information.
+ * Non-zero - RA options are invalid or do not indicate it is a default router
+ * or do not contain valid prefix information.
+ */
+int process_ra(struct ip6_hdr *ip6, int len)
+{
+	/* Pointer to the ICMP section of the packet */
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	struct ra_msg *msg = (struct ra_msg *)icmp;
+	int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct ra_msg);
+	unsigned short int option_len;	/* Length of each option */
+	/* Pointer to the ICMPv6 message options */
+	unsigned char *option = NULL;
+	/* 8-bit identifier of the type of ICMPv6 option */
+	unsigned char type = 0;
+	struct icmp6_ra_prefix_info *prefix = NULL;
+
+	/* Ignore the packet if router lifetime is 0. */
+	if (!icmp->icmp6_rt_lifetime)
+		return -EOPNOTSUPP;
+
+	/* Processing the options */
+	option = msg->opt;
+	while (remaining_option_len > 0) {
+		/* The 2nd byte of the option is its length. */
+		option_len = option[1];
+		/* All included options should have a positive length. */
+		if (option_len == 0)
+			return -EINVAL;
+
+		type = option[0];
+		/* All option types except Prefix Information are ignored. */
+		switch (type) {
+		case ND_OPT_SOURCE_LL_ADDR:
+		case ND_OPT_TARGET_LL_ADDR:
+		case ND_OPT_REDIRECT_HDR:
+		case ND_OPT_MTU:
+			break;
+		case ND_OPT_PREFIX_INFO:
+			prefix = (struct icmp6_ra_prefix_info *)option;
+			/* The link-local prefix 0xfe80::/10 is ignored. */
+			if ((ntohs(prefix->prefix.s6_addr16[0]) &
+			     IPV6_LINK_LOCAL_MASK) == IPV6_LINK_LOCAL_PREFIX)
+				break;
+			if (prefix->on_link && ntohl(prefix->valid_lifetime)) {
+				net_prefix_length = prefix->prefix_len;
+				net_gateway6 = ip6->saddr;
+				return 0;
+			}
+			break;
+		default:
+			debug("Unknown IPv6 Neighbor Discovery Option 0x%x\n",
+			      type);
+		}
+
+		option_len <<= 3; /* Option length is a multiple of 8. */
+		remaining_option_len -= option_len;
+		option += option_len;
+	}
+
+	return -EADDRNOTAVAIL;
+}
+
 int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 {
 	struct icmp6hdr *icmp =
 	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
 	struct nd_msg *ndisc = (struct nd_msg *)icmp;
 	uchar neigh_eth_addr[6];
+	int err = 0;	// The error code returned calling functions.
 
 	switch (icmp->icmp6_type) {
 	case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
@@ -280,6 +475,36 @@ int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 			net_nd_packet_mac = NULL;
 		}
 		break;
+	case IPV6_NDISC_ROUTER_SOLICITATION:
+		break;
+	case IPV6_NDISC_ROUTER_ADVERTISEMENT:
+		debug("Received router advertisement for %pI6c from %pI6c\n",
+		      &ip6->daddr, &ip6->saddr);
+		/*
+		 * If gateway and prefix are set, the RA packet is ignored. The
+		 * reason is that the U-Boot code is supposed to be as compact
+		 * as possible and does not need to take care of multiple
+		 * routers. In addition to that, U-Boot does not want to handle
+		 * scenarios like a router setting its lifetime to zero to
+		 * indicate it is not routing anymore. U-Boot program has a
+		 * short life when the system boots up and does not need such
+		 * sophistication.
+		 */
+		if (!ip6_is_unspecified_addr(&net_gateway6) &&
+		    net_prefix_length != 0) {
+			break;
+		}
+		if (!validate_ra(ip6)) {
+			debug("Invalid router advertisement message.\n");
+			break;
+		}
+		err = process_ra(ip6, len);
+		if (err)
+			debug("Ignored router advertisement. Error: %d\n", err);
+		else
+			printf("Set gatewayip6: %pI6c, prefix_length: %d\n",
+			       &net_gateway6, net_prefix_length);
+		break;
 	default:
 		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
 		return -1;
diff --git a/net/net.c b/net/net.c
index d69bfb0..abdb7e4 100644
--- a/net/net.c
+++ b/net/net.c
@@ -24,7 +24,7 @@
  *			- name of bootfile
  *	Next step:	ARP
  *
- * LINK_LOCAL:
+ * LINKLOCAL:
  *
  *	Prerequisites:	- own ethernet address
  *	We want:	- own IP address
@@ -125,6 +125,7 @@
 #if defined(CONFIG_CMD_DHCP6)
 #include "dhcpv6.h"
 #endif
+#include "net_rand.h"
 
 /** BOOTP EXTENTIONS **/
 
@@ -349,6 +350,8 @@ void net_auto_load(void)
 
 static int net_init_loop(void)
 {
+	static bool first_call = true;
+
 	if (eth_get_dev()) {
 		memcpy(net_ethaddr, eth_get_ethaddr(), 6);
 
@@ -368,6 +371,12 @@ static int net_init_loop(void)
 		 */
 		return -ENONET;
 
+	if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+		if (first_call && use_ip6) {
+			first_call = false;
+			srand_mac(); /* This is for rand used in ip6_send_rs. */
+			net_loop(RS);
+		}
 	return 0;
 }
 
@@ -577,6 +586,10 @@ restart:
 			ncsi_probe_packages();
 			break;
 #endif
+		case RS:
+			if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+				ip6_send_rs();
+			break;
 		default:
 			break;
 		}
@@ -674,7 +687,13 @@ restart:
 			x = time_handler;
 			time_handler = (thand_f *)0;
 			(*x)();
-		}
+		} else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+			if (time_handler && protocol == RS)
+				if (!ip6_is_unspecified_addr(&net_gateway6) &&
+				    net_prefix_length != 0) {
+					net_set_state(NETLOOP_SUCCESS);
+					net_set_timeout_handler(0, NULL);
+				}
 
 		if (net_state == NETLOOP_FAIL)
 			ret = net_start_again();
diff --git a/net/net6.c b/net/net6.c
index 75577bc..2dd64c0 100644
--- a/net/net6.c
+++ b/net/net6.c
@@ -413,6 +413,7 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 			break;
 		case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
 		case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+		case IPV6_NDISC_ROUTER_ADVERTISEMENT:
 			ndisc_receive(et, ip6, len);
 			break;
 		default:
-- 
1.8.3.1


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

* [PATCH v3 2/3] test/py: IPv6 network discovery test
  2023-04-12 16:10 ` [PATCH v3 0/3] Add IPv6 Network Discovery emohandesi
  2023-04-12 16:10   ` [PATCH v3 1/3] net: ipv6: Add support for default gateway discovery emohandesi
@ 2023-04-12 16:10   ` emohandesi
  2023-04-19  1:46     ` Simon Glass
  2023-04-12 16:10   ` [PATCH v3 3/3] test: eth: IPv6 network discovery unit test emohandesi
  2 siblings, 1 reply; 35+ messages in thread
From: emohandesi @ 2023-04-12 16:10 UTC (permalink / raw)
  To: u-boot
  Cc: sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, tobias, john, v.v.mitrofanov,
	saproj

From: Ehsan Mohandesi <emohandesi@microsoft.com>

Test the IPv6 network discovery feature if indicated by boardenv file.

Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>

Conflicts:
	configs/sandbox64_defconfig
	configs/sandbox_defconfig
	configs/sandbox_flattree_defconfig
---
 configs/sandbox64_defconfig        |  2 ++
 configs/sandbox_defconfig          |  2 ++
 configs/sandbox_flattree_defconfig |  2 ++
 test/py/tests/test_net.py          | 31 ++++++++++++++++++++++++++++++-
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index af2c56a..be36ede 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -260,3 +260,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ca95b2c..0673c69 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -341,3 +341,5 @@ CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
 CONFIG_CMD_2048=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index e9fcc5b..d6c8dd2 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -229,3 +229,5 @@ CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py
index 9ca6743..f85071d 100644
--- a/test/py/tests/test_net.py
+++ b/test/py/tests/test_net.py
@@ -9,7 +9,7 @@ import u_boot_utils
 
 """
 Note: This test relies on boardenv_* containing configuration values to define
-which the network environment available for testing. Without this, this test
+which network environment is available for testing. Without this, this test
 will be automatically skipped.
 
 For example:
@@ -55,6 +55,11 @@ env__net_nfs_readable_file = {
     'size': 5058624,
     'crc32': 'c2244b26',
 }
+
+# True if a router advertisement service is connected to the network, and should
+# be tested. If router advertisement testing is not possible or desired, this
+variable may be omitted or set to False.
+env__router_on_net = True
 """
 
 net_set_up = False
@@ -126,6 +131,30 @@ def test_net_ping(u_boot_console):
     output = u_boot_console.run_command('ping $serverip')
     assert 'is alive' in output
 
+@pytest.mark.buildconfigspec('IPV6_ROUTER_DISCOVERY')
+def test_net_network_discovery(u_boot_console):
+    """Test the network discovery feature of IPv6.
+
+    An IPv6 network command (ping6 in this case) is run to make U-Boot send a
+    router solicitation packet, receive a router advertisement message, and
+    parse it.
+    A router advertisement service needs to be running for this test to succeed.
+    U-Boot receives the RA, processes it, and if successful, assigns the gateway
+    IP and prefix length.
+    The configuration is provided by the boardenv_* file; see the comment at
+    the beginning of this file.
+    """
+
+    router_on_net = u_boot_console.config.env.get('env__router_on_net', False)
+    if not router_on_net:
+        pytest.skip('No router on network')
+
+    fake_host_ip = 'fe80::215:5dff:fef6:2ec6'
+    output = u_boot_console.run_command('ping6 ' + fake_host_ip)
+    assert 'ROUTER SOLICITATION 1' in output
+    assert 'Set gatewayip6:' in output
+    assert '0000:0000:0000:0000:0000:0000:0000:0000' not in output
+
 @pytest.mark.buildconfigspec('cmd_net')
 def test_net_tftpboot(u_boot_console):
     """Test the tftpboot command.
-- 
1.8.3.1


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

* [PATCH v3 3/3] test: eth: IPv6 network discovery unit test
  2023-04-12 16:10 ` [PATCH v3 0/3] Add IPv6 Network Discovery emohandesi
  2023-04-12 16:10   ` [PATCH v3 1/3] net: ipv6: Add support for default gateway discovery emohandesi
  2023-04-12 16:10   ` [PATCH v3 2/3] test/py: IPv6 network discovery test emohandesi
@ 2023-04-12 16:10   ` emohandesi
  2023-04-19  1:46     ` Simon Glass
  2 siblings, 1 reply; 35+ messages in thread
From: emohandesi @ 2023-04-12 16:10 UTC (permalink / raw)
  To: u-boot
  Cc: sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, tobias, john, v.v.mitrofanov,
	saproj

From: Ehsan Mohandesi <emohandesi@microsoft.com>

Test router advertisement validation and processing functions.

Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
---
 test/dm/eth.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/test/dm/eth.c b/test/dm/eth.c
index ebf01d8..d05d2a9 100644
--- a/test/dm/eth.c
+++ b/test/dm/eth.c
@@ -20,6 +20,7 @@
 #include <dm/uclass-internal.h>
 #include <test/test.h>
 #include <test/ut.h>
+#include <ndisc.h>
 
 #define DM_TEST_ETH_NUM		4
 
@@ -607,3 +608,90 @@ static int dm_test_eth_async_ping_reply(struct unit_test_state *uts)
 }
 
 DM_TEST(dm_test_eth_async_ping_reply, UT_TESTF_SCAN_FDT);
+
+#if IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)
+
+static u8 ip6_ra_buf[] = {0x60, 0xf, 0xc5, 0x4a, 0x0, 0x38, 0x3a, 0xff, 0xfe,
+			  0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x85, 0xe6,
+			  0x29, 0x77, 0xcb, 0xc8, 0x53, 0xff, 0x2, 0x0, 0x0,
+			  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+			  0x1, 0x86, 0x0, 0xdc, 0x90, 0x40, 0x80, 0x15, 0x18,
+			  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x4,
+			  0x40, 0xc0, 0x0, 0x0, 0x37, 0xdc, 0x0, 0x0, 0x37,
+			  0x78, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1, 0xca, 0xfe, 0xca,
+			  0xfe, 0xca, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+			  0x0, 0x1, 0x1, 0x0, 0x15, 0x5d, 0xe2, 0x8a, 0x2};
+
+static int dm_test_validate_ra(struct unit_test_state *uts)
+{
+	struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	__be16 temp = 0;
+
+	ut_assert(validate_ra(ip6) == true);
+
+	temp = ip6->payload_len;
+	ip6->payload_len = 15;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->payload_len = temp;
+
+	temp = ip6->saddr.s6_addr16[0];
+	ip6->saddr.s6_addr16[0] = 0x2001;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->saddr.s6_addr16[0] = temp;
+
+	temp = ip6->hop_limit;
+	ip6->hop_limit = 15;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->hop_limit = temp;
+
+	temp = icmp->icmp6_code;
+	icmp->icmp6_code = 15;
+	ut_assert(validate_ra(ip6) == false);
+	icmp->icmp6_code = temp;
+
+	return 0;
+}
+
+DM_TEST(dm_test_validate_ra, 0);
+
+static int dm_test_process_ra(struct unit_test_state *uts)
+{
+	int len = sizeof(ip6_ra_buf);
+	struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	struct ra_msg *msg = (struct ra_msg *)icmp;
+	unsigned char *option = msg->opt;
+	struct icmp6_ra_prefix_info *prefix =
+					(struct icmp6_ra_prefix_info *)option;
+	__be16 temp = 0;
+	unsigned char option_len = option[1];
+
+	ut_assert(process_ra(ip6, len) == 0);
+
+	temp = icmp->icmp6_rt_lifetime;
+	icmp->icmp6_rt_lifetime = 0;
+	ut_assert(process_ra(ip6, len) != 0);
+	icmp->icmp6_rt_lifetime = temp;
+
+	ut_assert(process_ra(ip6, 0) != 0);
+
+	option[1] = 0;
+	ut_assert(process_ra(ip6, len) != 0);
+	option[1] = option_len;
+
+	prefix->on_link = false;
+	ut_assert(process_ra(ip6, len) != 0);
+	prefix->on_link = true;
+
+	temp = prefix->prefix.s6_addr16[0];
+	prefix->prefix.s6_addr16[0] = 0x80fe;
+	ut_assert(process_ra(ip6, len) != 0);
+	prefix->prefix.s6_addr16[0] = temp;
+
+	return 0;
+}
+
+DM_TEST(dm_test_process_ra, 0);
+
+#endif
-- 
1.8.3.1


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

* Re: [PATCH v3 2/3] test/py: IPv6 network discovery test
  2023-04-12 16:10   ` [PATCH v3 2/3] test/py: IPv6 network discovery test emohandesi
@ 2023-04-19  1:46     ` Simon Glass
  0 siblings, 0 replies; 35+ messages in thread
From: Simon Glass @ 2023-04-19  1:46 UTC (permalink / raw)
  To: emohandesi
  Cc: u-boot, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, tobias, john, v.v.mitrofanov,
	saproj

Hi,

On Wed, 12 Apr 2023 at 10:10, <emohandesi@linux.microsoft.com> wrote:
>
> From: Ehsan Mohandesi <emohandesi@microsoft.com>
>
> Test the IPv6 network discovery feature if indicated by boardenv file.
>
> Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
>
> Conflicts:
>         configs/sandbox64_defconfig
>         configs/sandbox_defconfig
>         configs/sandbox_flattree_defconfig

You can drop these

> ---
>  configs/sandbox64_defconfig        |  2 ++
>  configs/sandbox_defconfig          |  2 ++
>  configs/sandbox_flattree_defconfig |  2 ++
>  test/py/tests/test_net.py          | 31 ++++++++++++++++++++++++++++++-
>  4 files changed, 36 insertions(+), 1 deletion(-)
>

Reviewed-by: Simon Glass <sjg@chromium.org>

Regards,
Simon

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

* Re: [PATCH v3 3/3] test: eth: IPv6 network discovery unit test
  2023-04-12 16:10   ` [PATCH v3 3/3] test: eth: IPv6 network discovery unit test emohandesi
@ 2023-04-19  1:46     ` Simon Glass
  0 siblings, 0 replies; 35+ messages in thread
From: Simon Glass @ 2023-04-19  1:46 UTC (permalink / raw)
  To: emohandesi
  Cc: u-boot, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, tobias, john, v.v.mitrofanov,
	saproj

On Wed, 12 Apr 2023 at 10:10, <emohandesi@linux.microsoft.com> wrote:
>
> From: Ehsan Mohandesi <emohandesi@microsoft.com>
>
> Test router advertisement validation and processing functions.
>
> Signed-off-by: Ehsan Mohandesi <emohandesi@microsoft.com>
> ---
>  test/dm/eth.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 88 insertions(+)

Reviewed-by: Simon Glass <sjg@chromium.org>

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

* Re: [PATCH v3 1/3] net: ipv6: Add support for default gateway discovery.
  2023-04-12 16:10   ` [PATCH v3 1/3] net: ipv6: Add support for default gateway discovery emohandesi
@ 2023-04-20 13:44     ` Vyacheslav V. Mitrofanov
  0 siblings, 0 replies; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-04-20 13:44 UTC (permalink / raw)
  To: u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, mario.six, tobias, saproj,
	rfried.dev, ilias.apalodimas, john, sjg, masahisa.kojima

Hello Ehsan! I tried to apply your patches and found out that there are some conflicts. I think you use your custom version. Check it please.

Thanks!

On Wed, 2023-04-12 at 09:10 -0700, emohandesi@linux.microsoft.co
wrote:

From: Ehsan Mohandesi <emohandesi@microsoft.com>
> 
> 
>  #endif /* __NDISC_H__ */
> diff --git a/include/net.h b/include/net.h
> index 8ba50a0..58774f6 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -505,7 +505,8 @@ extern int          net_restart_wrap;       /*
> Tried all network devices */
> 
>  enum proto_t {
>         BOOTP, RARP, ARP, TFTPGET, DHCP, DHCP6, PING, PING6, DNS,

Here DHCP6


>
> diff --git a/net/net.c b/net/net.c
> index d69bfb0..abdb7e4 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -24,7 +24,7 @@
>   *                     - name of bootfile
>   *     Next step:      ARP
>   *
> - * LINK_LOCAL:
> + * LINKLOCAL:
>   *
>   *     Prerequisites:  - own ethernet address
>   *     We want:        - own IP address
> @@ -125,6 +125,7 @@
>  #if defined(CONFIG_CMD_DHCP6)
>  #include "dhcpv6.h"
> 
> 
Here DHCP6

And maybe somewhere else

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

* [PATCH v4 0/3] Add IPv6 Network Discovery
  2023-03-02 16:58 [PATCH] net: ipv6: Add support for default gateway discovery emohandesi
                   ` (3 preceding siblings ...)
  2023-04-12 16:10 ` [PATCH v3 0/3] Add IPv6 Network Discovery emohandesi
@ 2023-04-22  0:08 ` emohandesi
  2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
                     ` (2 more replies)
  4 siblings, 3 replies; 35+ messages in thread
From: emohandesi @ 2023-04-22  0:08 UTC (permalink / raw)
  To: u-boot
  Cc: sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov, saproj

From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>

This series adds IPv6 network discovery to U-Boot. When an IPv6 command is
run in U-Boot, it sends a router solicitation (RS) message to the network.
The router on the network responds with a router advertisement (RA)
message. Then U-Boot processes the RA message and sets the gatewayip6 and
net_prefix_length environment variables.
It is based on RFC 4861, but not everything in the RFC is supported here.
https://www.rfc-editor.org/rfc/rfc4861

Changes in v4:
- Removed the changes that were mistakenly pulled from the local workspace.

Changes in v3:
- Removed the extra revert commit that was mistakenly added in v2.

Changes in v2:
- Improved IPv6 network discovery code.
- Added IPv6 network discovery feature test (Python test).
- Added unit tests (C code).

Ehsan Mohandesi (3):
  net: ipv6: Add support for default gateway discovery.
  test/py: IPv6 network discovery test
  test: eth: IPv6 network discovery unit test

 cmd/Kconfig                        |   6 +
 configs/sandbox64_defconfig        |   2 +
 configs/sandbox_defconfig          |   2 +
 configs/sandbox_flattree_defconfig |   2 +
 include/ndisc.h                    |  35 ++++++
 include/net.h                      |   2 +-
 include/net6.h                     |  40 ++++++
 net/ndisc.c                        | 243 +++++++++++++++++++++++++++++++++++--
 net/net.c                          |  23 +++-
 net/net6.c                         |   1 +
 test/dm/eth.c                      |  88 ++++++++++++++
 test/py/tests/test_net.py          |  31 ++++-
 12 files changed, 462 insertions(+), 13 deletions(-)

-- 
1.8.3.1


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

* [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-04-22  0:08 ` [PATCH v4 0/3] Add IPv6 Network Discovery emohandesi
@ 2023-04-22  0:08   ` emohandesi
  2023-04-24  7:58     ` Vyacheslav V. Mitrofanov
                       ` (4 more replies)
  2023-04-22  0:08   ` [PATCH v4 2/3] test/py: IPv6 network discovery test emohandesi
  2023-04-22  0:08   ` [PATCH v4 3/3] test: eth: IPv6 network discovery unit test emohandesi
  2 siblings, 5 replies; 35+ messages in thread
From: emohandesi @ 2023-04-22  0:08 UTC (permalink / raw)
  To: u-boot
  Cc: sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov, saproj

From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>

In IPv6, the default gateway and prefix length are determined by receiving
a router advertisement as defined in -
https://www.rfc-editor.org/rfc/rfc4861.

Add support for sending router solicitation (RS) and processing router
advertisements (RA).

If the RA has prefix info option and following conditions are met, then
gatewayip6 and net_prefix_length of ip6addr env variables are initialized.
These are later consumed by IPv6 code for non-local destination IP.

- "Router Lifetime" != 0
- Prefix is NOT link-local prefix (0xfe80::/10)
- L flag is 1
- "Valid Lifetime" != 0

Timing Parameters:
- MAX_RTR_SOLICITATION_DELAY (0-1s)
- RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
- MAX_RTR_SOLICITATIONS (3 RS transmissions)

The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and invoked
automatically from net_init_loop().

Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
---
 cmd/Kconfig     |   6 ++
 include/ndisc.h |  35 ++++++++
 include/net.h   |   2 +-
 include/net6.h  |  40 ++++++++++
 net/ndisc.c     | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++---
 net/net.c       |  23 +++++-
 net/net6.c      |   1 +
 7 files changed, 338 insertions(+), 12 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index e45b884..6919d31 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1916,6 +1916,12 @@ config CMD_NCSI
 	  Normally this happens automatically before other network
 	  operations.
 
+config IPV6_ROUTER_DISCOVERY
+	bool "Do IPv6 router discovery"
+	depends on IPV6
+	help
+	  Will automatically perform router solicitation on first IPv6
+	  network operation
 endif
 
 config CMD_ETHSW
diff --git a/include/ndisc.h b/include/ndisc.h
index f6f8eb6..12fa9e7 100644
--- a/include/ndisc.h
+++ b/include/ndisc.h
@@ -19,6 +19,20 @@ struct nd_msg {
 	__u8		opt[0];
 };
 
+/* struct rs_msg - ICMPv6 Router Solicitation message format */
+struct rs_msg {
+	struct icmp6hdr	icmph;
+	__u8		opt[0];
+};
+
+/* struct ra_msg - ICMPv6 Router Advertisement message format */
+struct ra_msg {
+	struct icmp6hdr	icmph;
+	__u32	reachable_time;
+	__u32	retransmission_timer;
+	__u8	opt[0];
+};
+
 /* struct echo_msg - ICMPv6 echo request/reply message format */
 struct echo_msg {
 	struct icmp6hdr	icmph;
@@ -57,6 +71,11 @@ extern int net_nd_try;
  */
 void ndisc_init(void);
 
+/*
+ * ip6_send_rs() - Send IPv6 Router Solicitation Message
+ */
+void ip6_send_rs(void);
+
 /**
  * ndisc_receive() - Handle ND packet
  *
@@ -78,6 +97,8 @@ void ndisc_request(void);
  * Return: 0 if no timeout, -1 otherwise
  */
 int ndisc_timeout_check(void);
+bool validate_ra(struct ip6_hdr *ip6);
+int process_ra(struct ip6_hdr *ip6, int len);
 #else
 static inline void ndisc_init(void)
 {
@@ -97,6 +118,20 @@ static inline int ndisc_timeout_check(void)
 {
 	return 0;
 }
+
+void ip6_send_rs(void)
+{
+}
+
+static inline bool validate_ra(struct ip6_hdr *ip6)
+{
+	return true;
+}
+
+static inline int process_ra(struct ip6_hdr *ip6, int len)
+{
+	return 0;
+}
 #endif
 
 #endif /* __NDISC_H__ */
diff --git a/include/net.h b/include/net.h
index 399af5e..25c43b3 100644
--- a/include/net.h
+++ b/include/net.h
@@ -505,7 +505,7 @@ 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
+	SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI, WGET, RS
 };
 
 extern char	net_boot_file_name[1024];/* Boot File name */
diff --git a/include/net6.h b/include/net6.h
index 2d7c5a0..beafc05 100644
--- a/include/net6.h
+++ b/include/net6.h
@@ -81,8 +81,17 @@ struct udp_hdr {
 			  0x00, 0x00, 0x00, 0x00, \
 			  0x00, 0x00, 0x00, 0x00, \
 			  0x00, 0x00, 0x00, 0x00 } } }
+/*
+ * All-routers multicast address is the link-local scope address to reach all
+ * routers.
+ */
+#define ALL_ROUTERS_MULT_ADDR { { { 0xFF, 0x02, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x00, \
+				  0x00, 0x00, 0x00, 0x02 } } }
 
 #define IPV6_LINK_LOCAL_PREFIX	0xfe80
+#define IPV6_LINK_LOCAL_MASK	0xffb0 /* The first 10-bit of address mask. */
 
 /* hop limit for neighbour discovery packets */
 #define IPV6_NDISC_HOPLIMIT             255
@@ -166,6 +175,37 @@ struct icmp6hdr {
 #define icmp6_rt_lifetime	icmp6_dataun.u_nd_ra.rt_lifetime
 } __packed;
 
+/*
+ * struct icmp6_ra_prefix_info - Prefix Information option of the ICMPv6 message
+ * The Prefix Information option provides hosts with on-link prefixes and
+ * prefixes for Address Autoconfiguration. Refer to RFC 4861 for more info.
+ */
+struct icmp6_ra_prefix_info {
+	u8	type;		/* Type is 3 for Prefix Information. */
+	u8	len;		/* Len is 4 for Prefix Information. */
+	/* The number of leading bits in the Prefix that are valid. */
+	u8	prefix_len;
+	u8	reserved1:6,	/* MUST be ignored by the receiver. */
+		aac:1,		/* autonomous address-configuration flag */
+	/* Indicates that this prefix can be used for on-link determination. */
+		on_link:1;
+	/*
+	 * The length of time in seconds that the prefix is valid for the
+	 * purpose of on-link determination.
+	 */
+	__be32	valid_lifetime;
+	/* The length of time addresses remain preferred. */
+	__be32	preferred_lifetime;
+	__be32	reserved2;	/* MUST be ignored by the receiver. */
+	/*
+	 * Prefix is an IP address or a prefix of an IP address. The Prefix
+	 * Length field contains the number of valid leading bits in the prefix.
+	 * The bits in the prefix after the prefix length are reserved and MUST
+	 * be initialized to zero by the sender and ignored by the receiver.
+	 */
+	struct in6_addr prefix;
+};
+
 extern struct in6_addr const net_null_addr_ip6;	/* NULL IPv6 address */
 extern struct in6_addr net_gateway6;	/* Our gateways IPv6 address */
 extern struct in6_addr net_ip6;	/* Our IPv6 addr (0 = unknown) */
diff --git a/net/ndisc.c b/net/ndisc.c
index 367dae7..0b27779 100644
--- a/net/ndisc.c
+++ b/net/ndisc.c
@@ -13,6 +13,8 @@
 #include <net.h>
 #include <net6.h>
 #include <ndisc.h>
+#include <stdlib.h>
+#include <linux/delay.h>
 
 /* IPv6 destination address of packet waiting for ND */
 struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
@@ -29,31 +31,37 @@ int net_nd_tx_packet_size;
 ulong net_nd_timer_start;
 /* the number of requests we have sent so far */
 int net_nd_try;
+struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR;
+
+#define MAX_RTR_SOLICITATIONS		3
+/* The maximum time to delay sending the first router solicitation message. */
+#define MAX_SOLICITATION_DELAY		1 // 1 second
+/* The time to wait before sending the next router solicitation message. */
+#define RTR_SOLICITATION_INTERVAL	4000 // 4 seconds
 
 #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7)
 
 /**
  * ndisc_insert_option() - Insert an option into a neighbor discovery packet
  *
- * @ndisc:	pointer to ND packet
+ * @opt:	pointer to the option element of the neighbor discovery packet
  * @type:	option type to insert
  * @data:	option data to insert
  * @len:	data length
  * Return: the number of bytes inserted (which may be >= len)
  */
-static int
-ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int len)
+static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int len)
 {
 	int space = IP6_NDISC_OPT_SPACE(len);
 
-	ndisc->opt[0] = type;
-	ndisc->opt[1] = space >> 3;
-	memcpy(&ndisc->opt[2], data, len);
+	opt[0] = type;
+	opt[1] = space >> 3;
+	memcpy(&opt[2], data, len);
 	len += 2;
 
 	/* fill the remainder with 0 */
 	if (space - len > 0)
-		memset(&ndisc->opt[len], '\0', space - len);
+		memset(&opt[len], '\0', space - len);
 
 	return space;
 }
@@ -123,7 +131,7 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
 
 	/* Set the target address and llsaddr option */
 	net_copy_ip6(&msg->target, neigh_addr);
-	ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+	ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
 			    INETHADDRSZ);
 
 	/* checksum */
@@ -137,6 +145,76 @@ static void ip6_send_ns(struct in6_addr *neigh_addr)
 	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
 }
 
+/*
+ * ip6_send_rs() - Send IPv6 Router Solicitation Message.
+ *
+ * A router solicitation is sent to discover a router. RS message creation is
+ * based on RFC 4861 section 4.1. Router Solicitation Message Format.
+ */
+void ip6_send_rs(void)
+{
+	unsigned char enetaddr[6];
+	struct rs_msg *msg;
+	__u16 icmp_len;
+	uchar *pkt;
+	unsigned short csum;
+	unsigned int pcsum;
+	static unsigned int retry_count;
+
+	if (!ip6_is_unspecified_addr(&net_gateway6) &&
+	    net_prefix_length != 0) {
+		net_set_state(NETLOOP_SUCCESS);
+		return;
+	} else if (retry_count >= MAX_RTR_SOLICITATIONS) {
+		net_set_state(NETLOOP_FAIL);
+		net_set_timeout_handler(0, NULL);
+		retry_count = 0;
+		return;
+	}
+
+	printf("ROUTER SOLICITATION %d\n", retry_count + 1);
+
+	ip6_make_mult_ethdstaddr(enetaddr, &all_routers);
+	/*
+	 * ICMP length is the size of ICMP header (8) + one option (8) = 16.
+	 * The option is 2 bytes of type and length + 6 bytes for MAC.
+	 */
+	icmp_len = sizeof(struct icmp6hdr) + IP6_NDISC_OPT_SPACE(INETHADDRSZ);
+
+	pkt = (uchar *)net_tx_packet;
+	pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
+	pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers, PROT_ICMPV6,
+			   IPV6_NDISC_HOPLIMIT, icmp_len);
+
+	/* ICMPv6 - RS */
+	msg = (struct rs_msg *)pkt;
+	msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION;
+	msg->icmph.icmp6_code = 0;
+	memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
+	memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
+
+	/* Set the llsaddr option */
+	ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
+			    INETHADDRSZ);
+
+	/* checksum */
+	pcsum = csum_partial((__u8 *)msg, icmp_len, 0);
+	csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers,
+			       icmp_len, PROT_ICMPV6, pcsum);
+	msg->icmph.icmp6_cksum = csum;
+	pkt += icmp_len;
+
+	/* Wait up to 1 second if it is the first try to get the RA */
+	if (retry_count == 0)
+		udelay(((unsigned int)rand() % 1000000) * MAX_SOLICITATION_DELAY);
+
+	/* send it! */
+	net_send_packet(net_tx_packet, (pkt - net_tx_packet));
+
+	retry_count++;
+	net_set_timeout_handler(RTR_SOLICITATION_INTERVAL, ip6_send_rs);
+}
+
 static void
 ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
 	    struct in6_addr *target)
@@ -167,7 +245,7 @@ ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
 	msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
 	/* Set the target address and lltargetaddr option */
 	net_copy_ip6(&msg->target, target);
-	ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
+	ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
 			    INETHADDRSZ);
 
 	/* checksum */
@@ -223,6 +301,10 @@ int ndisc_timeout_check(void)
 	return 1;
 }
 
+/*
+ * ndisc_init() - Make initial steps for ND state machine.
+ * Usually move variables into initial state.
+ */
 void ndisc_init(void)
 {
 	net_nd_packet_mac = NULL;
@@ -234,12 +316,125 @@ void ndisc_init(void)
 	net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
 }
 
+/*
+ * validate_ra() - Validate the router advertisement message.
+ *
+ * @ip6: Pointer to the router advertisement packet
+ *
+ * Check if the router advertisement message is valid. Conditions are
+ * according to RFC 4861 section 6.1.2. Validation of Router Advertisement
+ * Messages.
+ *
+ * Return: true if the message is valid and false if it is invalid.
+ */
+bool validate_ra(struct ip6_hdr *ip6)
+{
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+
+	/* ICMP length (derived from the IP length) should be 16 or more octets. */
+	if (ip6->payload_len < 16)
+		return false;
+
+	/* Source IP Address should be a valid link-local address. */
+	if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK) !=
+	    IPV6_LINK_LOCAL_PREFIX)
+		return false;
+
+	/*
+	 * The IP Hop Limit field should have a value of 255, i.e., the packet
+	 * could not possibly have been forwarded by a router.
+	 */
+	if (ip6->hop_limit != 255)
+		return false;
+
+	/* ICMP checksum has already been checked in net_ip6_handler. */
+
+	if (icmp->icmp6_code != 0)
+		return false;
+
+	return true;
+}
+
+/*
+ * process_ra() - Process the router advertisement packet.
+ *
+ * @ip6: Pointer to the router advertisement packet
+ * @len: Length of the router advertisement packet
+ *
+ * Process the received router advertisement message.
+ * Although RFC 4861 requires retaining at least two router addresses, we only
+ * keep one because of the U-Boot limitations and its goal of lightweight code.
+ *
+ * Return: 0 - RA is a default router and contains valid prefix information.
+ * Non-zero - RA options are invalid or do not indicate it is a default router
+ * or do not contain valid prefix information.
+ */
+int process_ra(struct ip6_hdr *ip6, int len)
+{
+	/* Pointer to the ICMP section of the packet */
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	struct ra_msg *msg = (struct ra_msg *)icmp;
+	int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct ra_msg);
+	unsigned short int option_len;	/* Length of each option */
+	/* Pointer to the ICMPv6 message options */
+	unsigned char *option = NULL;
+	/* 8-bit identifier of the type of ICMPv6 option */
+	unsigned char type = 0;
+	struct icmp6_ra_prefix_info *prefix = NULL;
+
+	/* Ignore the packet if router lifetime is 0. */
+	if (!icmp->icmp6_rt_lifetime)
+		return -EOPNOTSUPP;
+
+	/* Processing the options */
+	option = msg->opt;
+	while (remaining_option_len > 0) {
+		/* The 2nd byte of the option is its length. */
+		option_len = option[1];
+		/* All included options should have a positive length. */
+		if (option_len == 0)
+			return -EINVAL;
+
+		type = option[0];
+		/* All option types except Prefix Information are ignored. */
+		switch (type) {
+		case ND_OPT_SOURCE_LL_ADDR:
+		case ND_OPT_TARGET_LL_ADDR:
+		case ND_OPT_REDIRECT_HDR:
+		case ND_OPT_MTU:
+			break;
+		case ND_OPT_PREFIX_INFO:
+			prefix = (struct icmp6_ra_prefix_info *)option;
+			/* The link-local prefix 0xfe80::/10 is ignored. */
+			if ((ntohs(prefix->prefix.s6_addr16[0]) &
+			     IPV6_LINK_LOCAL_MASK) == IPV6_LINK_LOCAL_PREFIX)
+				break;
+			if (prefix->on_link && ntohl(prefix->valid_lifetime)) {
+				net_prefix_length = prefix->prefix_len;
+				net_gateway6 = ip6->saddr;
+				return 0;
+			}
+			break;
+		default:
+			debug("Unknown IPv6 Neighbor Discovery Option 0x%x\n",
+			      type);
+		}
+
+		option_len <<= 3; /* Option length is a multiple of 8. */
+		remaining_option_len -= option_len;
+		option += option_len;
+	}
+
+	return -EADDRNOTAVAIL;
+}
+
 int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 {
 	struct icmp6hdr *icmp =
 	    (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
 	struct nd_msg *ndisc = (struct nd_msg *)icmp;
 	uchar neigh_eth_addr[6];
+	int err = 0;	// The error code returned calling functions.
 
 	switch (icmp->icmp6_type) {
 	case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
@@ -280,6 +475,36 @@ int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 			net_nd_packet_mac = NULL;
 		}
 		break;
+	case IPV6_NDISC_ROUTER_SOLICITATION:
+		break;
+	case IPV6_NDISC_ROUTER_ADVERTISEMENT:
+		debug("Received router advertisement for %pI6c from %pI6c\n",
+		      &ip6->daddr, &ip6->saddr);
+		/*
+		 * If gateway and prefix are set, the RA packet is ignored. The
+		 * reason is that the U-Boot code is supposed to be as compact
+		 * as possible and does not need to take care of multiple
+		 * routers. In addition to that, U-Boot does not want to handle
+		 * scenarios like a router setting its lifetime to zero to
+		 * indicate it is not routing anymore. U-Boot program has a
+		 * short life when the system boots up and does not need such
+		 * sophistication.
+		 */
+		if (!ip6_is_unspecified_addr(&net_gateway6) &&
+		    net_prefix_length != 0) {
+			break;
+		}
+		if (!validate_ra(ip6)) {
+			debug("Invalid router advertisement message.\n");
+			break;
+		}
+		err = process_ra(ip6, len);
+		if (err)
+			debug("Ignored router advertisement. Error: %d\n", err);
+		else
+			printf("Set gatewayip6: %pI6c, prefix_length: %d\n",
+			       &net_gateway6, net_prefix_length);
+		break;
 	default:
 		debug("Unexpected ICMPv6 type 0x%x\n", icmp->icmp6_type);
 		return -1;
diff --git a/net/net.c b/net/net.c
index c9a749f..9d15329 100644
--- a/net/net.c
+++ b/net/net.c
@@ -24,7 +24,7 @@
  *			- name of bootfile
  *	Next step:	ARP
  *
- * LINK_LOCAL:
+ * LINKLOCAL:
  *
  *	Prerequisites:	- own ethernet address
  *	We want:	- own IP address
@@ -122,6 +122,7 @@
 #endif
 #include <net/tcp.h>
 #include <net/wget.h>
+#include "net_rand.h"
 
 /** BOOTP EXTENTIONS **/
 
@@ -346,6 +347,8 @@ void net_auto_load(void)
 
 static int net_init_loop(void)
 {
+	static bool first_call = true;
+
 	if (eth_get_dev()) {
 		memcpy(net_ethaddr, eth_get_ethaddr(), 6);
 
@@ -365,6 +368,12 @@ static int net_init_loop(void)
 		 */
 		return -ENONET;
 
+	if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+		if (first_call && use_ip6) {
+			first_call = false;
+			srand_mac(); /* This is for rand used in ip6_send_rs. */
+			net_loop(RS);
+		}
 	return 0;
 }
 
@@ -574,6 +583,10 @@ restart:
 			ncsi_probe_packages();
 			break;
 #endif
+		case RS:
+			if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+				ip6_send_rs();
+			break;
 		default:
 			break;
 		}
@@ -671,7 +684,13 @@ restart:
 			x = time_handler;
 			time_handler = (thand_f *)0;
 			(*x)();
-		}
+		} else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
+			if (time_handler && protocol == RS)
+				if (!ip6_is_unspecified_addr(&net_gateway6) &&
+				    net_prefix_length != 0) {
+					net_set_state(NETLOOP_SUCCESS);
+					net_set_timeout_handler(0, NULL);
+				}
 
 		if (net_state == NETLOOP_FAIL)
 			ret = net_start_again();
diff --git a/net/net6.c b/net/net6.c
index 75577bc..2dd64c0 100644
--- a/net/net6.c
+++ b/net/net6.c
@@ -413,6 +413,7 @@ int net_ip6_handler(struct ethernet_hdr *et, struct ip6_hdr *ip6, int len)
 			break;
 		case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
 		case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
+		case IPV6_NDISC_ROUTER_ADVERTISEMENT:
 			ndisc_receive(et, ip6, len);
 			break;
 		default:
-- 
1.8.3.1


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

* [PATCH v4 2/3] test/py: IPv6 network discovery test
  2023-04-22  0:08 ` [PATCH v4 0/3] Add IPv6 Network Discovery emohandesi
  2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
@ 2023-04-22  0:08   ` emohandesi
  2023-04-24  7:59     ` Vyacheslav V. Mitrofanov
  2023-05-06 14:53     ` Tom Rini
  2023-04-22  0:08   ` [PATCH v4 3/3] test: eth: IPv6 network discovery unit test emohandesi
  2 siblings, 2 replies; 35+ messages in thread
From: emohandesi @ 2023-04-22  0:08 UTC (permalink / raw)
  To: u-boot
  Cc: sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov, saproj

From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>

Test the IPv6 network discovery feature if indicated by boardenv file.

Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
---
 configs/sandbox64_defconfig        |  2 ++
 configs/sandbox_defconfig          |  2 ++
 configs/sandbox_flattree_defconfig |  2 ++
 test/py/tests/test_net.py          | 31 ++++++++++++++++++++++++++++++-
 4 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index af2c56a..be36ede 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -260,3 +260,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index ca95b2c..0673c69 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -341,3 +341,5 @@ CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
 CONFIG_CMD_2048=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/configs/sandbox_flattree_defconfig b/configs/sandbox_flattree_defconfig
index e9fcc5b..d6c8dd2 100644
--- a/configs/sandbox_flattree_defconfig
+++ b/configs/sandbox_flattree_defconfig
@@ -229,3 +229,5 @@ CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
 CONFIG_UNIT_TEST=y
 CONFIG_UT_TIME=y
 CONFIG_UT_DM=y
+CONFIG_IPV6=y
+CONFIG_IPV6_ROUTER_DISCOVERY=y
diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py
index 9ca6743..f85071d 100644
--- a/test/py/tests/test_net.py
+++ b/test/py/tests/test_net.py
@@ -9,7 +9,7 @@ import u_boot_utils
 
 """
 Note: This test relies on boardenv_* containing configuration values to define
-which the network environment available for testing. Without this, this test
+which network environment is available for testing. Without this, this test
 will be automatically skipped.
 
 For example:
@@ -55,6 +55,11 @@ env__net_nfs_readable_file = {
     'size': 5058624,
     'crc32': 'c2244b26',
 }
+
+# True if a router advertisement service is connected to the network, and should
+# be tested. If router advertisement testing is not possible or desired, this
+variable may be omitted or set to False.
+env__router_on_net = True
 """
 
 net_set_up = False
@@ -126,6 +131,30 @@ def test_net_ping(u_boot_console):
     output = u_boot_console.run_command('ping $serverip')
     assert 'is alive' in output
 
+@pytest.mark.buildconfigspec('IPV6_ROUTER_DISCOVERY')
+def test_net_network_discovery(u_boot_console):
+    """Test the network discovery feature of IPv6.
+
+    An IPv6 network command (ping6 in this case) is run to make U-Boot send a
+    router solicitation packet, receive a router advertisement message, and
+    parse it.
+    A router advertisement service needs to be running for this test to succeed.
+    U-Boot receives the RA, processes it, and if successful, assigns the gateway
+    IP and prefix length.
+    The configuration is provided by the boardenv_* file; see the comment at
+    the beginning of this file.
+    """
+
+    router_on_net = u_boot_console.config.env.get('env__router_on_net', False)
+    if not router_on_net:
+        pytest.skip('No router on network')
+
+    fake_host_ip = 'fe80::215:5dff:fef6:2ec6'
+    output = u_boot_console.run_command('ping6 ' + fake_host_ip)
+    assert 'ROUTER SOLICITATION 1' in output
+    assert 'Set gatewayip6:' in output
+    assert '0000:0000:0000:0000:0000:0000:0000:0000' not in output
+
 @pytest.mark.buildconfigspec('cmd_net')
 def test_net_tftpboot(u_boot_console):
     """Test the tftpboot command.
-- 
1.8.3.1


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

* [PATCH v4 3/3] test: eth: IPv6 network discovery unit test
  2023-04-22  0:08 ` [PATCH v4 0/3] Add IPv6 Network Discovery emohandesi
  2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
  2023-04-22  0:08   ` [PATCH v4 2/3] test/py: IPv6 network discovery test emohandesi
@ 2023-04-22  0:08   ` emohandesi
  2023-04-24  8:01     ` Vyacheslav V. Mitrofanov
  2023-05-06 14:53     ` Tom Rini
  2 siblings, 2 replies; 35+ messages in thread
From: emohandesi @ 2023-04-22  0:08 UTC (permalink / raw)
  To: u-boot
  Cc: sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov, saproj

From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>

Test router advertisement validation and processing functions.

Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
---
 test/dm/eth.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 88 insertions(+)

diff --git a/test/dm/eth.c b/test/dm/eth.c
index ebf01d8..d05d2a9 100644
--- a/test/dm/eth.c
+++ b/test/dm/eth.c
@@ -20,6 +20,7 @@
 #include <dm/uclass-internal.h>
 #include <test/test.h>
 #include <test/ut.h>
+#include <ndisc.h>
 
 #define DM_TEST_ETH_NUM		4
 
@@ -607,3 +608,90 @@ static int dm_test_eth_async_ping_reply(struct unit_test_state *uts)
 }
 
 DM_TEST(dm_test_eth_async_ping_reply, UT_TESTF_SCAN_FDT);
+
+#if IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)
+
+static u8 ip6_ra_buf[] = {0x60, 0xf, 0xc5, 0x4a, 0x0, 0x38, 0x3a, 0xff, 0xfe,
+			  0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6, 0x85, 0xe6,
+			  0x29, 0x77, 0xcb, 0xc8, 0x53, 0xff, 0x2, 0x0, 0x0,
+			  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+			  0x1, 0x86, 0x0, 0xdc, 0x90, 0x40, 0x80, 0x15, 0x18,
+			  0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x3, 0x4,
+			  0x40, 0xc0, 0x0, 0x0, 0x37, 0xdc, 0x0, 0x0, 0x37,
+			  0x78, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1, 0xca, 0xfe, 0xca,
+			  0xfe, 0xca, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
+			  0x0, 0x1, 0x1, 0x0, 0x15, 0x5d, 0xe2, 0x8a, 0x2};
+
+static int dm_test_validate_ra(struct unit_test_state *uts)
+{
+	struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	__be16 temp = 0;
+
+	ut_assert(validate_ra(ip6) == true);
+
+	temp = ip6->payload_len;
+	ip6->payload_len = 15;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->payload_len = temp;
+
+	temp = ip6->saddr.s6_addr16[0];
+	ip6->saddr.s6_addr16[0] = 0x2001;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->saddr.s6_addr16[0] = temp;
+
+	temp = ip6->hop_limit;
+	ip6->hop_limit = 15;
+	ut_assert(validate_ra(ip6) == false);
+	ip6->hop_limit = temp;
+
+	temp = icmp->icmp6_code;
+	icmp->icmp6_code = 15;
+	ut_assert(validate_ra(ip6) == false);
+	icmp->icmp6_code = temp;
+
+	return 0;
+}
+
+DM_TEST(dm_test_validate_ra, 0);
+
+static int dm_test_process_ra(struct unit_test_state *uts)
+{
+	int len = sizeof(ip6_ra_buf);
+	struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
+	struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
+	struct ra_msg *msg = (struct ra_msg *)icmp;
+	unsigned char *option = msg->opt;
+	struct icmp6_ra_prefix_info *prefix =
+					(struct icmp6_ra_prefix_info *)option;
+	__be16 temp = 0;
+	unsigned char option_len = option[1];
+
+	ut_assert(process_ra(ip6, len) == 0);
+
+	temp = icmp->icmp6_rt_lifetime;
+	icmp->icmp6_rt_lifetime = 0;
+	ut_assert(process_ra(ip6, len) != 0);
+	icmp->icmp6_rt_lifetime = temp;
+
+	ut_assert(process_ra(ip6, 0) != 0);
+
+	option[1] = 0;
+	ut_assert(process_ra(ip6, len) != 0);
+	option[1] = option_len;
+
+	prefix->on_link = false;
+	ut_assert(process_ra(ip6, len) != 0);
+	prefix->on_link = true;
+
+	temp = prefix->prefix.s6_addr16[0];
+	prefix->prefix.s6_addr16[0] = 0x80fe;
+	ut_assert(process_ra(ip6, len) != 0);
+	prefix->prefix.s6_addr16[0] = temp;
+
+	return 0;
+}
+
+DM_TEST(dm_test_process_ra, 0);
+
+#endif
-- 
1.8.3.1


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

* Re: [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
@ 2023-04-24  7:58     ` Vyacheslav V. Mitrofanov
  2023-04-24  8:03     ` Vyacheslav V. Mitrofanov
                       ` (3 subsequent siblings)
  4 siblings, 0 replies; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-04-24  7:58 UTC (permalink / raw)
  To: u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, mario.six, tobias, saproj,
	rfried.dev, ilias.apalodimas, pali, john, sjg, masahisa.kojima

On Fri, 2023-04-21 at 17:08 -0700, emohandesi@linux.microsoft.com
wrote:
> 
> From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> 
> In IPv6, the default gateway and prefix length are determined by
> receiving
> a router advertisement as defined in -
> https://www.rfc-editor.org/rfc/rfc4861.
> 
> Add support for sending router solicitation (RS) and processing
> router
> advertisements (RA).
> 
> If the RA has prefix info option and following conditions are met,
> then
> gatewayip6 and net_prefix_length of ip6addr env variables are
> initialized.
> These are later consumed by IPv6 code for non-local destination IP.
> 
> - "Router Lifetime" != 0
> - Prefix is NOT link-local prefix (0xfe80::/10)
> - L flag is 1
> - "Valid Lifetime" != 0
> 
> Timing Parameters:
> - MAX_RTR_SOLICITATION_DELAY (0-1s)
> - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
> - MAX_RTR_SOLICITATIONS (3 RS transmissions)
> 
> The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and
> invoked
> automatically from net_init_loop().
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> ---
>  cmd/Kconfig     |   6 ++
>  include/ndisc.h |  35 ++++++++
>  include/net.h   |   2 +-
>  include/net6.h  |  40 ++++++++++
>  net/ndisc.c     | 243
> +++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  net/net.c       |  23 +++++-
>  net/net6.c      |   1 +
>  7 files changed, 338 insertions(+), 12 deletions(-)
> 
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index e45b884..6919d31 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1916,6 +1916,12 @@ config CMD_NCSI
>           Normally this happens automatically before other network
>           operations.
> 
> +config IPV6_ROUTER_DISCOVERY
> +       bool "Do IPv6 router discovery"
> +       depends on IPV6
> +       help
> +         Will automatically perform router solicitation on first
> IPv6
> +         network operation
>  endif
> 
>  config CMD_ETHSW
> diff --git a/include/ndisc.h b/include/ndisc.h
> index f6f8eb6..12fa9e7 100644
> --- a/include/ndisc.h
> +++ b/include/ndisc.h
> @@ -19,6 +19,20 @@ struct nd_msg {
>         __u8            opt[0];
>  };
> 
> +/* struct rs_msg - ICMPv6 Router Solicitation message format */
> +struct rs_msg {
> +       struct icmp6hdr icmph;
> +       __u8            opt[0];
> +};
> +
> +/* struct ra_msg - ICMPv6 Router Advertisement message format */
> +struct ra_msg {
> +       struct icmp6hdr icmph;
> +       __u32   reachable_time;
> +       __u32   retransmission_timer;
> +       __u8    opt[0];
> +};
> +
>  /* struct echo_msg - ICMPv6 echo request/reply message format */
>  struct echo_msg {
>         struct icmp6hdr icmph;
> @@ -57,6 +71,11 @@ extern int net_nd_try;
>   */
>  void ndisc_init(void);
> 
> +/*
> + * ip6_send_rs() - Send IPv6 Router Solicitation Message
> + */
> +void ip6_send_rs(void);
> +
>  /**
>   * ndisc_receive() - Handle ND packet
>   *
> @@ -78,6 +97,8 @@ void ndisc_request(void);
>   * Return: 0 if no timeout, -1 otherwise
>   */
>  int ndisc_timeout_check(void);
> +bool validate_ra(struct ip6_hdr *ip6);
> +int process_ra(struct ip6_hdr *ip6, int len);
>  #else
>  static inline void ndisc_init(void)
>  {
> @@ -97,6 +118,20 @@ static inline int ndisc_timeout_check(void)
>  {
>         return 0;
>  }
> +
> +void ip6_send_rs(void)
> +{
> +}
> +
> +static inline bool validate_ra(struct ip6_hdr *ip6)
> +{
> +       return true;
> +}
> +
> +static inline int process_ra(struct ip6_hdr *ip6, int len)
> +{
> +       return 0;
> +}
>  #endif
> 
>  #endif /* __NDISC_H__ */
> diff --git a/include/net.h b/include/net.h
> index 399af5e..25c43b3 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -505,7 +505,7 @@ 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
> +       SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI,
> WGET, RS
>  };
> 
>  extern char    net_boot_file_name[1024];/* Boot File name */
> diff --git a/include/net6.h b/include/net6.h
> index 2d7c5a0..beafc05 100644
> --- a/include/net6.h
> +++ b/include/net6.h
> @@ -81,8 +81,17 @@ struct udp_hdr {
>                           0x00, 0x00, 0x00, 0x00, \
>                           0x00, 0x00, 0x00, 0x00, \
>                           0x00, 0x00, 0x00, 0x00 } } }
> +/*
> + * All-routers multicast address is the link-local scope address to
> reach all
> + * routers.
> + */
> +#define ALL_ROUTERS_MULT_ADDR { { { 0xFF, 0x02, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x02 } } }
> 
>  #define IPV6_LINK_LOCAL_PREFIX 0xfe80
> +#define IPV6_LINK_LOCAL_MASK   0xffb0 /* The first 10-bit of address
> mask. */
> 
>  /* hop limit for neighbour discovery packets */
>  #define IPV6_NDISC_HOPLIMIT             255
> @@ -166,6 +175,37 @@ struct icmp6hdr {
>  #define icmp6_rt_lifetime      icmp6_dataun.u_nd_ra.rt_lifetime
>  } __packed;
> 
> +/*
> + * struct icmp6_ra_prefix_info - Prefix Information option of the
> ICMPv6 message
> + * The Prefix Information option provides hosts with on-link
> prefixes and
> + * prefixes for Address Autoconfiguration. Refer to RFC 4861 for
> more info.
> + */
> +struct icmp6_ra_prefix_info {
> +       u8      type;           /* Type is 3 for Prefix Information.
> */
> +       u8      len;            /* Len is 4 for Prefix Information.
> */
> +       /* The number of leading bits in the Prefix that are valid.
> */
> +       u8      prefix_len;
> +       u8      reserved1:6,    /* MUST be ignored by the receiver.
> */
> +               aac:1,          /* autonomous address-configuration
> flag */
> +       /* Indicates that this prefix can be used for on-link
> determination. */
> +               on_link:1;
> +       /*
> +        * The length of time in seconds that the prefix is valid for
> the
> +        * purpose of on-link determination.
> +        */
> +       __be32  valid_lifetime;
> +       /* The length of time addresses remain preferred. */
> +       __be32  preferred_lifetime;
> +       __be32  reserved2;      /* MUST be ignored by the receiver.
> */
> +       /*
> +        * Prefix is an IP address or a prefix of an IP address. The
> Prefix
> +        * Length field contains the number of valid leading bits in
> the prefix.
> +        * The bits in the prefix after the prefix length are
> reserved and MUST
> +        * be initialized to zero by the sender and ignored by the
> receiver.
> +        */
> +       struct in6_addr prefix;
> +};
> +
>  extern struct in6_addr const net_null_addr_ip6;        /* NULL IPv6
> address */
>  extern struct in6_addr net_gateway6;   /* Our gateways IPv6 address
> */
>  extern struct in6_addr net_ip6;        /* Our IPv6 addr (0 =
> unknown) */
> diff --git a/net/ndisc.c b/net/ndisc.c
> index 367dae7..0b27779 100644
> --- a/net/ndisc.c
> +++ b/net/ndisc.c
> @@ -13,6 +13,8 @@
>  #include <net.h>
>  #include <net6.h>
>  #include <ndisc.h>
> +#include <stdlib.h>
> +#include <linux/delay.h>
> 
>  /* IPv6 destination address of packet waiting for ND */
>  struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
> @@ -29,31 +31,37 @@ int net_nd_tx_packet_size;
>  ulong net_nd_timer_start;
>  /* the number of requests we have sent so far */
>  int net_nd_try;
> +struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR;
> +
> +#define MAX_RTR_SOLICITATIONS          3
> +/* The maximum time to delay sending the first router solicitation
> message. */
> +#define MAX_SOLICITATION_DELAY         1 // 1 second
> +/* The time to wait before sending the next router solicitation
> message. */
> +#define RTR_SOLICITATION_INTERVAL      4000 // 4 seconds
> 
>  #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7)
> 
>  /**
>   * ndisc_insert_option() - Insert an option into a neighbor
> discovery packet
>   *
> - * @ndisc:     pointer to ND packet
> + * @opt:       pointer to the option element of the neighbor
> discovery packet
>   * @type:      option type to insert
>   * @data:      option data to insert
>   * @len:       data length
>   * Return: the number of bytes inserted (which may be >= len)
>   */
> -static int
> -ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int
> len)
> +static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int
> len)
>  {
>         int space = IP6_NDISC_OPT_SPACE(len);
> 
> -       ndisc->opt[0] = type;
> -       ndisc->opt[1] = space >> 3;
> -       memcpy(&ndisc->opt[2], data, len);
> +       opt[0] = type;
> +       opt[1] = space >> 3;
> +       memcpy(&opt[2], data, len);
>         len += 2;
> 
>         /* fill the remainder with 0 */
>         if (space - len > 0)
> -               memset(&ndisc->opt[len], '\0', space - len);
> +               memset(&opt[len], '\0', space - len);
> 
>         return space;
>  }
> @@ -123,7 +131,7 @@ static void ip6_send_ns(struct in6_addr
> *neigh_addr)
> 
>         /* Set the target address and llsaddr option */
>         net_copy_ip6(&msg->target, neigh_addr);
> -       ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
> +       ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR,
> net_ethaddr,
>                             INETHADDRSZ);
> 
>         /* checksum */
> @@ -137,6 +145,76 @@ static void ip6_send_ns(struct in6_addr
> *neigh_addr)
>         net_send_packet(net_tx_packet, (pkt - net_tx_packet));
>  }
> 
> +/*
> + * ip6_send_rs() - Send IPv6 Router Solicitation Message.
> + *
> + * A router solicitation is sent to discover a router. RS message
> creation is
> + * based on RFC 4861 section 4.1. Router Solicitation Message
> Format.
> + */
> +void ip6_send_rs(void)
> +{
> +       unsigned char enetaddr[6];
> +       struct rs_msg *msg;
> +       __u16 icmp_len;
> +       uchar *pkt;
> +       unsigned short csum;
> +       unsigned int pcsum;
> +       static unsigned int retry_count;
> +
> +       if (!ip6_is_unspecified_addr(&net_gateway6) &&
> +           net_prefix_length != 0) {
> +               net_set_state(NETLOOP_SUCCESS);
> +               return;
> +       } else if (retry_count >= MAX_RTR_SOLICITATIONS) {
> +               net_set_state(NETLOOP_FAIL);
> +               net_set_timeout_handler(0, NULL);
> +               retry_count = 0;
> +               return;
> +       }
> +
> +       printf("ROUTER SOLICITATION %d\n", retry_count + 1);
> +
> +       ip6_make_mult_ethdstaddr(enetaddr, &all_routers);
> +       /*
> +        * ICMP length is the size of ICMP header (8) + one option
> (8) = 16.
> +        * The option is 2 bytes of type and length + 6 bytes for
> MAC.
> +        */
> +       icmp_len = sizeof(struct icmp6hdr) +
> IP6_NDISC_OPT_SPACE(INETHADDRSZ);
> +
> +       pkt = (uchar *)net_tx_packet;
> +       pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
> +       pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers,
> PROT_ICMPV6,
> +                          IPV6_NDISC_HOPLIMIT, icmp_len);
> +
> +       /* ICMPv6 - RS */
> +       msg = (struct rs_msg *)pkt;
> +       msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION;
> +       msg->icmph.icmp6_code = 0;
> +       memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
> +       memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
> +
> +       /* Set the llsaddr option */
> +       ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR,
> net_ethaddr,
> +                           INETHADDRSZ);
> +
> +       /* checksum */
> +       pcsum = csum_partial((__u8 *)msg, icmp_len, 0);
> +       csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers,
> +                              icmp_len, PROT_ICMPV6, pcsum);
> +       msg->icmph.icmp6_cksum = csum;
> +       pkt += icmp_len;
> +
> +       /* Wait up to 1 second if it is the first try to get the RA
> */
> +       if (retry_count == 0)
> +               udelay(((unsigned int)rand() % 1000000) *
> MAX_SOLICITATION_DELAY);
> +
> +       /* send it! */
> +       net_send_packet(net_tx_packet, (pkt - net_tx_packet));
> +
> +       retry_count++;
> +       net_set_timeout_handler(RTR_SOLICITATION_INTERVAL,
> ip6_send_rs);
> +}
> +
>  static void
>  ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
>             struct in6_addr *target)
> @@ -167,7 +245,7 @@ ip6_send_na(uchar *eth_dst_addr, struct in6_addr
> *neigh_addr,
>         msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
>         /* Set the target address and lltargetaddr option */
>         net_copy_ip6(&msg->target, target);
> -       ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
> +       ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR,
> net_ethaddr,
>                             INETHADDRSZ);
> 
>         /* checksum */
> @@ -223,6 +301,10 @@ int ndisc_timeout_check(void)
>         return 1;
>  }
> 
> +/*
> + * ndisc_init() - Make initial steps for ND state machine.
> + * Usually move variables into initial state.
> + */
>  void ndisc_init(void)
>  {
>         net_nd_packet_mac = NULL;
> @@ -234,12 +316,125 @@ void ndisc_init(void)
>         net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
>  }
> 
> +/*
> + * validate_ra() - Validate the router advertisement message.
> + *
> + * @ip6: Pointer to the router advertisement packet
> + *
> + * Check if the router advertisement message is valid. Conditions
> are
> + * according to RFC 4861 section 6.1.2. Validation of Router
> Advertisement
> + * Messages.
> + *
> + * Return: true if the message is valid and false if it is invalid.
> + */
> +bool validate_ra(struct ip6_hdr *ip6)
> +{
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +
> +       /* ICMP length (derived from the IP length) should be 16 or
> more octets. */
> +       if (ip6->payload_len < 16)
> +               return false;
> +
> +       /* Source IP Address should be a valid link-local address. */
> +       if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK)
> !=
> +           IPV6_LINK_LOCAL_PREFIX)
> +               return false;
> +
> +       /*
> +        * The IP Hop Limit field should have a value of 255, i.e.,
> the packet
> +        * could not possibly have been forwarded by a router.
> +        */
> +       if (ip6->hop_limit != 255)
> +               return false;
> +
> +       /* ICMP checksum has already been checked in net_ip6_handler.
> */
> +
> +       if (icmp->icmp6_code != 0)
> +               return false;
> +
> +       return true;
> +}
> +
> +/*
> + * process_ra() - Process the router advertisement packet.
> + *
> + * @ip6: Pointer to the router advertisement packet
> + * @len: Length of the router advertisement packet
> + *
> + * Process the received router advertisement message.
> + * Although RFC 4861 requires retaining at least two router
> addresses, we only
> + * keep one because of the U-Boot limitations and its goal of
> lightweight code.
> + *
> + * Return: 0 - RA is a default router and contains valid prefix
> information.
> + * Non-zero - RA options are invalid or do not indicate it is a
> default router
> + * or do not contain valid prefix information.
> + */
> +int process_ra(struct ip6_hdr *ip6, int len)
> +{
> +       /* Pointer to the ICMP section of the packet */
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +       struct ra_msg *msg = (struct ra_msg *)icmp;
> +       int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct
> ra_msg);
> +       unsigned short int option_len;  /* Length of each option */
> +       /* Pointer to the ICMPv6 message options */
> +       unsigned char *option = NULL;
> +       /* 8-bit identifier of the type of ICMPv6 option */
> +       unsigned char type = 0;
> +       struct icmp6_ra_prefix_info *prefix = NULL;
> +
> +       /* Ignore the packet if router lifetime is 0. */
> +       if (!icmp->icmp6_rt_lifetime)
> +               return -EOPNOTSUPP;
> +
> +       /* Processing the options */
> +       option = msg->opt;
> +       while (remaining_option_len > 0) {
> +               /* The 2nd byte of the option is its length. */
> +               option_len = option[1];
> +               /* All included options should have a positive
> length. */
> +               if (option_len == 0)
> +                       return -EINVAL;
> +
> +               type = option[0];
> +               /* All option types except Prefix Information are
> ignored. */
> +               switch (type) {
> +               case ND_OPT_SOURCE_LL_ADDR:
> +               case ND_OPT_TARGET_LL_ADDR:
> +               case ND_OPT_REDIRECT_HDR:
> +               case ND_OPT_MTU:
> +                       break;
> +               case ND_OPT_PREFIX_INFO:
> +                       prefix = (struct icmp6_ra_prefix_info
> *)option;
> +                       /* The link-local prefix 0xfe80::/10 is
> ignored. */
> +                       if ((ntohs(prefix->prefix.s6_addr16[0]) &
> +                            IPV6_LINK_LOCAL_MASK) ==
> IPV6_LINK_LOCAL_PREFIX)
> +                               break;
> +                       if (prefix->on_link && ntohl(prefix-
> >valid_lifetime)) {
> +                               net_prefix_length = prefix-
> >prefix_len;
> +                               net_gateway6 = ip6->saddr;
> +                               return 0;
> +                       }
> +                       break;
> +               default:
> +                       debug("Unknown IPv6 Neighbor Discovery Option
> 0x%x\n",
> +                             type);
> +               }
> +
> +               option_len <<= 3; /* Option length is a multiple of
> 8. */
> +               remaining_option_len -= option_len;
> +               option += option_len;
> +       }
> +
> +       return -EADDRNOTAVAIL;
> +}
> +
>  int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int
> len)
>  {
>         struct icmp6hdr *icmp =
>             (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
>         struct nd_msg *ndisc = (struct nd_msg *)icmp;
>         uchar neigh_eth_addr[6];
> +       int err = 0;    // The error code returned calling functions.
> 
>         switch (icmp->icmp6_type) {
>         case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
> @@ -280,6 +475,36 @@ int ndisc_receive(struct ethernet_hdr *et,
> struct ip6_hdr *ip6, int len)
>                         net_nd_packet_mac = NULL;
>                 }
>                 break;
> +       case IPV6_NDISC_ROUTER_SOLICITATION:
> +               break;
> +       case IPV6_NDISC_ROUTER_ADVERTISEMENT:
> +               debug("Received router advertisement for %pI6c from
> %pI6c\n",
> +                     &ip6->daddr, &ip6->saddr);
> +               /*
> +                * If gateway and prefix are set, the RA packet is
> ignored. The
> +                * reason is that the U-Boot code is supposed to be
> as compact
> +                * as possible and does not need to take care of
> multiple
> +                * routers. In addition to that, U-Boot does not want
> to handle
> +                * scenarios like a router setting its lifetime to
> zero to
> +                * indicate it is not routing anymore. U-Boot program
> has a
> +                * short life when the system boots up and does not
> need such
> +                * sophistication.
> +                */
> +               if (!ip6_is_unspecified_addr(&net_gateway6) &&
> +                   net_prefix_length != 0) {
> +                       break;
> +               }
> +               if (!validate_ra(ip6)) {
> +                       debug("Invalid router advertisement
> message.\n");
> +                       break;
> +               }
> +               err = process_ra(ip6, len);
> +               if (err)
> +                       debug("Ignored router advertisement. Error:
> %d\n", err);
> +               else
> +                       printf("Set gatewayip6: %pI6c, prefix_length:
> %d\n",
> +                              &net_gateway6, net_prefix_length);
> +               break;
>         default:
>                 debug("Unexpected ICMPv6 type 0x%x\n", icmp-
> >icmp6_type);
>                 return -1;
> diff --git a/net/net.c b/net/net.c
> index c9a749f..9d15329 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -24,7 +24,7 @@
>   *                     - name of bootfile
>   *     Next step:      ARP
>   *
> - * LINK_LOCAL:
> + * LINKLOCAL:
>   *
>   *     Prerequisites:  - own ethernet address
>   *     We want:        - own IP address
> @@ -122,6 +122,7 @@
>  #endif
>  #include <net/tcp.h>
>  #include <net/wget.h>
> +#include "net_rand.h"
> 
>  /** BOOTP EXTENTIONS **/
> 
> @@ -346,6 +347,8 @@ void net_auto_load(void)
> 
>  static int net_init_loop(void)
>  {
> +       static bool first_call = true;
> +
>         if (eth_get_dev()) {
>                 memcpy(net_ethaddr, eth_get_ethaddr(), 6);
> 
> @@ -365,6 +368,12 @@ static int net_init_loop(void)
>                  */
>                 return -ENONET;
> 
> +       if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +               if (first_call && use_ip6) {
> +                       first_call = false;
> +                       srand_mac(); /* This is for rand used in
> ip6_send_rs. */
> +                       net_loop(RS);
> +               }
>         return 0;
>  }
> 
> @@ -574,6 +583,10 @@ restart:
>                         ncsi_probe_packages();
>                         break;
>  #endif
> +               case RS:
> +                       if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +                               ip6_send_rs();
> +                       break;
>                 default:
>                         break;
>                 }
> @@ -671,7 +684,13 @@ restart:
>                         x = time_handler;
>                         time_handler = (thand_f *)0;
>                         (*x)();
> -               }
> +               } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +                       if (time_handler && protocol == RS)
> +                               if
> (!ip6_is_unspecified_addr(&net_gateway6) &&
> +                                   net_prefix_length != 0) {
> +                                       net_set_state(NETLOOP_SUCCESS
> );
> +                                       net_set_timeout_handler(0,
> NULL);
> +                               }
> 
>                 if (net_state == NETLOOP_FAIL)
>                         ret = net_start_again();
> diff --git a/net/net6.c b/net/net6.c
> index 75577bc..2dd64c0 100644
> --- a/net/net6.c
> +++ b/net/net6.c
> @@ -413,6 +413,7 @@ int net_ip6_handler(struct ethernet_hdr *et,
> struct ip6_hdr *ip6, int len)
>                         break;
>                 case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
>                 case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
> +               case IPV6_NDISC_ROUTER_ADVERTISEMENT:
>                         ndisc_receive(et, ip6, len);
>                         break;
>                 default:
> --
> 1.8.3.1
> 
> 

Tested on Si-five Hi-five Unmatched board (RISC-V)

Good. Thanks!

Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>Reviewed-by: 
Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

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

* Re: [PATCH v4 2/3] test/py: IPv6 network discovery test
  2023-04-22  0:08   ` [PATCH v4 2/3] test/py: IPv6 network discovery test emohandesi
@ 2023-04-24  7:59     ` Vyacheslav V. Mitrofanov
  2023-05-06 14:53     ` Tom Rini
  1 sibling, 0 replies; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-04-24  7:59 UTC (permalink / raw)
  To: u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, mario.six, tobias, saproj,
	rfried.dev, ilias.apalodimas, pali, john, sjg, masahisa.kojima

On Fri, 2023-04-21 at 17:08 -0700, emohandesi@linux.microsoft.com
wrote:
> «Внимание! Данное письмо от внешнего адресата!»
> 
> From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> 
> Test the IPv6 network discovery feature if indicated by boardenv
> file.
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> ---
>  configs/sandbox64_defconfig        |  2 ++
>  configs/sandbox_defconfig          |  2 ++
>  configs/sandbox_flattree_defconfig |  2 ++
>  test/py/tests/test_net.py          | 31
> ++++++++++++++++++++++++++++++-
>  4 files changed, 36 insertions(+), 1 deletion(-)
> 
> diff --git a/configs/sandbox64_defconfig
> b/configs/sandbox64_defconfig
> index af2c56a..be36ede 100644
> --- a/configs/sandbox64_defconfig
> +++ b/configs/sandbox64_defconfig
> @@ -260,3 +260,5 @@ CONFIG_FWU_MULTI_BANK_UPDATE=y
>  CONFIG_UNIT_TEST=y
>  CONFIG_UT_TIME=y
>  CONFIG_UT_DM=y
> +CONFIG_IPV6=y
> +CONFIG_IPV6_ROUTER_DISCOVERY=y
> diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
> index ca95b2c..0673c69 100644
> --- a/configs/sandbox_defconfig
> +++ b/configs/sandbox_defconfig
> @@ -341,3 +341,5 @@ CONFIG_UNIT_TEST=y
>  CONFIG_UT_TIME=y
>  CONFIG_UT_DM=y
>  CONFIG_CMD_2048=y
> +CONFIG_IPV6=y
> +CONFIG_IPV6_ROUTER_DISCOVERY=y
> diff --git a/configs/sandbox_flattree_defconfig
> b/configs/sandbox_flattree_defconfig
> index e9fcc5b..d6c8dd2 100644
> --- a/configs/sandbox_flattree_defconfig
> +++ b/configs/sandbox_flattree_defconfig
> @@ -229,3 +229,5 @@ CONFIG_EFI_CAPSULE_FIRMWARE_FIT=y
>  CONFIG_UNIT_TEST=y
>  CONFIG_UT_TIME=y
>  CONFIG_UT_DM=y
> +CONFIG_IPV6=y
> +CONFIG_IPV6_ROUTER_DISCOVERY=y
> diff --git a/test/py/tests/test_net.py b/test/py/tests/test_net.py
> index 9ca6743..f85071d 100644
> --- a/test/py/tests/test_net.py
> +++ b/test/py/tests/test_net.py
> @@ -9,7 +9,7 @@ import u_boot_utils
> 
>  """
>  Note: This test relies on boardenv_* containing configuration values
> to define
> -which the network environment available for testing. Without this,
> this test
> +which network environment is available for testing. Without this,
> this test
>  will be automatically skipped.
> 
>  For example:
> @@ -55,6 +55,11 @@ env__net_nfs_readable_file = {
>      'size': 5058624,
>      'crc32': 'c2244b26',
>  }
> +
> +# True if a router advertisement service is connected to the
> network, and should
> +# be tested. If router advertisement testing is not possible or
> desired, this
> +variable may be omitted or set to False.
> +env__router_on_net = True
>  """
> 
>  net_set_up = False
> @@ -126,6 +131,30 @@ def test_net_ping(u_boot_console):
>      output = u_boot_console.run_command('ping $serverip')
>      assert 'is alive' in output
> 
> +@pytest.mark.buildconfigspec('IPV6_ROUTER_DISCOVERY')
> +def test_net_network_discovery(u_boot_console):
> +    """Test the network discovery feature of IPv6.
> +
> +    An IPv6 network command (ping6 in this case) is run to make U-
> Boot send a
> +    router solicitation packet, receive a router advertisement
> message, and
> +    parse it.
> +    A router advertisement service needs to be running for this test
> to succeed.
> +    U-Boot receives the RA, processes it, and if successful, assigns
> the gateway
> +    IP and prefix length.
> +    The configuration is provided by the boardenv_* file; see the
> comment at
> +    the beginning of this file.
> +    """
> +
> +    router_on_net =
> u_boot_console.config.env.get('env__router_on_net', False)
> +    if not router_on_net:
> +        pytest.skip('No router on network')
> +
> +    fake_host_ip = 'fe80::215:5dff:fef6:2ec6'
> +    output = u_boot_console.run_command('ping6 ' + fake_host_ip)
> +    assert 'ROUTER SOLICITATION 1' in output
> +    assert 'Set gatewayip6:' in output
> +    assert '0000:0000:0000:0000:0000:0000:0000:0000' not in output
> +
>  @pytest.mark.buildconfigspec('cmd_net')
>  def test_net_tftpboot(u_boot_console):
>      """Test the tftpboot command.
> --
> 1.8.3.1
> 
> 

Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

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

* Re: [PATCH v4 3/3] test: eth: IPv6 network discovery unit test
  2023-04-22  0:08   ` [PATCH v4 3/3] test: eth: IPv6 network discovery unit test emohandesi
@ 2023-04-24  8:01     ` Vyacheslav V. Mitrofanov
  2023-05-06 14:53     ` Tom Rini
  1 sibling, 0 replies; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-04-24  8:01 UTC (permalink / raw)
  To: u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, mario.six, tobias, saproj,
	rfried.dev, ilias.apalodimas, pali, john, sjg, masahisa.kojima

On Fri, 2023-04-21 at 17:08 -0700, emohandesi@linux.microsoft.com
wrote:
> «Внимание! Данное письмо от внешнего адресата!»
> 
> From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> 
> Test router advertisement validation and processing functions.
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> ---
>  test/dm/eth.c | 88
> +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 88 insertions(+)
> 
> diff --git a/test/dm/eth.c b/test/dm/eth.c
> index ebf01d8..d05d2a9 100644
> --- a/test/dm/eth.c
> +++ b/test/dm/eth.c
> @@ -20,6 +20,7 @@
>  #include <dm/uclass-internal.h>
>  #include <test/test.h>
>  #include <test/ut.h>
> +#include <ndisc.h>
> 
>  #define DM_TEST_ETH_NUM                4
> 
> @@ -607,3 +608,90 @@ static int dm_test_eth_async_ping_reply(struct
> unit_test_state *uts)
>  }
> 
>  DM_TEST(dm_test_eth_async_ping_reply, UT_TESTF_SCAN_FDT);
> +
> +#if IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY)
> +
> +static u8 ip6_ra_buf[] = {0x60, 0xf, 0xc5, 0x4a, 0x0, 0x38, 0x3a,
> 0xff, 0xfe,
> +                         0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x6,
> 0x85, 0xe6,
> +                         0x29, 0x77, 0xcb, 0xc8, 0x53, 0xff, 0x2,
> 0x0, 0x0,
> +                         0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
> 0x0, 0x0, 0x0,
> +                         0x1, 0x86, 0x0, 0xdc, 0x90, 0x40, 0x80,
> 0x15, 0x18,
> +                         0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
> 0x3, 0x4,
> +                         0x40, 0xc0, 0x0, 0x0, 0x37, 0xdc, 0x0, 0x0,
> 0x37,
> +                         0x78, 0x0, 0x0, 0x0, 0x0, 0x20, 0x1, 0xca,
> 0xfe, 0xca,
> +                         0xfe, 0xca, 0xfe, 0x0, 0x0, 0x0, 0x0, 0x0,
> 0x0, 0x0,
> +                         0x0, 0x1, 0x1, 0x0, 0x15, 0x5d, 0xe2, 0x8a,
> 0x2};
> +
> +static int dm_test_validate_ra(struct unit_test_state *uts)
> +{
> +       struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +       __be16 temp = 0;
> +
> +       ut_assert(validate_ra(ip6) == true);
> +
> +       temp = ip6->payload_len;
> +       ip6->payload_len = 15;
> +       ut_assert(validate_ra(ip6) == false);
> +       ip6->payload_len = temp;
> +
> +       temp = ip6->saddr.s6_addr16[0];
> +       ip6->saddr.s6_addr16[0] = 0x2001;
> +       ut_assert(validate_ra(ip6) == false);
> +       ip6->saddr.s6_addr16[0] = temp;
> +
> +       temp = ip6->hop_limit;
> +       ip6->hop_limit = 15;
> +       ut_assert(validate_ra(ip6) == false);
> +       ip6->hop_limit = temp;
> +
> +       temp = icmp->icmp6_code;
> +       icmp->icmp6_code = 15;
> +       ut_assert(validate_ra(ip6) == false);
> +       icmp->icmp6_code = temp;
> +
> +       return 0;
> +}
> +
> +DM_TEST(dm_test_validate_ra, 0);
> +
> +static int dm_test_process_ra(struct unit_test_state *uts)
> +{
> +       int len = sizeof(ip6_ra_buf);
> +       struct ip6_hdr *ip6 = (struct ip6_hdr *)ip6_ra_buf;
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +       struct ra_msg *msg = (struct ra_msg *)icmp;
> +       unsigned char *option = msg->opt;
> +       struct icmp6_ra_prefix_info *prefix =
> +                                       (struct icmp6_ra_prefix_info
> *)option;
> +       __be16 temp = 0;
> +       unsigned char option_len = option[1];
> +
> +       ut_assert(process_ra(ip6, len) == 0);
> +
> +       temp = icmp->icmp6_rt_lifetime;
> +       icmp->icmp6_rt_lifetime = 0;
> +       ut_assert(process_ra(ip6, len) != 0);
> +       icmp->icmp6_rt_lifetime = temp;
> +
> +       ut_assert(process_ra(ip6, 0) != 0);
> +
> +       option[1] = 0;
> +       ut_assert(process_ra(ip6, len) != 0);
> +       option[1] = option_len;
> +
> +       prefix->on_link = false;
> +       ut_assert(process_ra(ip6, len) != 0);
> +       prefix->on_link = true;
> +
> +       temp = prefix->prefix.s6_addr16[0];
> +       prefix->prefix.s6_addr16[0] = 0x80fe;
> +       ut_assert(process_ra(ip6, len) != 0);
> +       prefix->prefix.s6_addr16[0] = temp;
> +
> +       return 0;
> +}
> +
> +DM_TEST(dm_test_process_ra, 0);
> +
> +#endif
> --
> 1.8.3.1
> 
> 
Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

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

* Re: [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
  2023-04-24  7:58     ` Vyacheslav V. Mitrofanov
@ 2023-04-24  8:03     ` Vyacheslav V. Mitrofanov
  2023-05-04 14:30     ` Sergei Antonov
                       ` (2 subsequent siblings)
  4 siblings, 0 replies; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-04-24  8:03 UTC (permalink / raw)
  To: u-boot, emohandesi
  Cc: joe.hershberger, xypron.glpk, mario.six, tobias, saproj,
	rfried.dev, ilias.apalodimas, pali, john, sjg, masahisa.kojima

On Fri, 2023-04-21 at 17:08 -0700, emohandesi@linux.microsoft.com
wrote:
> 
> From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> 
> In IPv6, the default gateway and prefix length are determined by
> receiving
> a router advertisement as defined in -
> https://www.rfc-editor.org/rfc/rfc4861.
> 
> Add support for sending router solicitation (RS) and processing
> router
> advertisements (RA).
> 
> If the RA has prefix info option and following conditions are met,
> then
> gatewayip6 and net_prefix_length of ip6addr env variables are
> initialized.
> These are later consumed by IPv6 code for non-local destination IP.
> 
> - "Router Lifetime" != 0
> - Prefix is NOT link-local prefix (0xfe80::/10)
> - L flag is 1
> - "Valid Lifetime" != 0
> 
> Timing Parameters:
> - MAX_RTR_SOLICITATION_DELAY (0-1s)
> - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
> - MAX_RTR_SOLICITATIONS (3 RS transmissions)
> 
> The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and
> invoked
> automatically from net_init_loop().
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> ---
>  cmd/Kconfig     |   6 ++
>  include/ndisc.h |  35 ++++++++
>  include/net.h   |   2 +-
>  include/net6.h  |  40 ++++++++++
>  net/ndisc.c     | 243
> +++++++++++++++++++++++++++++++++++++++++++++++++++++---
>  net/net.c       |  23 +++++-
>  net/net6.c      |   1 +
>  7 files changed, 338 insertions(+), 12 deletions(-)
> 
> diff --git a/cmd/Kconfig b/cmd/Kconfig
> index e45b884..6919d31 100644
> --- a/cmd/Kconfig
> +++ b/cmd/Kconfig
> @@ -1916,6 +1916,12 @@ config CMD_NCSI
>           Normally this happens automatically before other network
>           operations.
> 
> +config IPV6_ROUTER_DISCOVERY
> +       bool "Do IPv6 router discovery"
> +       depends on IPV6
> +       help
> +         Will automatically perform router solicitation on first
> IPv6
> +         network operation
>  endif
> 
>  config CMD_ETHSW
> diff --git a/include/ndisc.h b/include/ndisc.h
> index f6f8eb6..12fa9e7 100644
> --- a/include/ndisc.h
> +++ b/include/ndisc.h
> @@ -19,6 +19,20 @@ struct nd_msg {
>         __u8            opt[0];
>  };
> 
> +/* struct rs_msg - ICMPv6 Router Solicitation message format */
> +struct rs_msg {
> +       struct icmp6hdr icmph;
> +       __u8            opt[0];
> +};
> +
> +/* struct ra_msg - ICMPv6 Router Advertisement message format */
> +struct ra_msg {
> +       struct icmp6hdr icmph;
> +       __u32   reachable_time;
> +       __u32   retransmission_timer;
> +       __u8    opt[0];
> +};
> +
>  /* struct echo_msg - ICMPv6 echo request/reply message format */
>  struct echo_msg {
>         struct icmp6hdr icmph;
> @@ -57,6 +71,11 @@ extern int net_nd_try;
>   */
>  void ndisc_init(void);
> 
> +/*
> + * ip6_send_rs() - Send IPv6 Router Solicitation Message
> + */
> +void ip6_send_rs(void);
> +
>  /**
>   * ndisc_receive() - Handle ND packet
>   *
> @@ -78,6 +97,8 @@ void ndisc_request(void);
>   * Return: 0 if no timeout, -1 otherwise
>   */
>  int ndisc_timeout_check(void);
> +bool validate_ra(struct ip6_hdr *ip6);
> +int process_ra(struct ip6_hdr *ip6, int len);
>  #else
>  static inline void ndisc_init(void)
>  {
> @@ -97,6 +118,20 @@ static inline int ndisc_timeout_check(void)
>  {
>         return 0;
>  }
> +
> +void ip6_send_rs(void)
> +{
> +}
> +
> +static inline bool validate_ra(struct ip6_hdr *ip6)
> +{
> +       return true;
> +}
> +
> +static inline int process_ra(struct ip6_hdr *ip6, int len)
> +{
> +       return 0;
> +}
>  #endif
> 
>  #endif /* __NDISC_H__ */
> diff --git a/include/net.h b/include/net.h
> index 399af5e..25c43b3 100644
> --- a/include/net.h
> +++ b/include/net.h
> @@ -505,7 +505,7 @@ 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
> +       SNTP, TFTPSRV, TFTPPUT, LINKLOCAL, FASTBOOT, WOL, UDP, NCSI,
> WGET, RS
>  };
> 
>  extern char    net_boot_file_name[1024];/* Boot File name */
> diff --git a/include/net6.h b/include/net6.h
> index 2d7c5a0..beafc05 100644
> --- a/include/net6.h
> +++ b/include/net6.h
> @@ -81,8 +81,17 @@ struct udp_hdr {
>                           0x00, 0x00, 0x00, 0x00, \
>                           0x00, 0x00, 0x00, 0x00, \
>                           0x00, 0x00, 0x00, 0x00 } } }
> +/*
> + * All-routers multicast address is the link-local scope address to
> reach all
> + * routers.
> + */
> +#define ALL_ROUTERS_MULT_ADDR { { { 0xFF, 0x02, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x00, \
> +                                 0x00, 0x00, 0x00, 0x02 } } }
> 
>  #define IPV6_LINK_LOCAL_PREFIX 0xfe80
> +#define IPV6_LINK_LOCAL_MASK   0xffb0 /* The first 10-bit of address
> mask. */
> 
>  /* hop limit for neighbour discovery packets */
>  #define IPV6_NDISC_HOPLIMIT             255
> @@ -166,6 +175,37 @@ struct icmp6hdr {
>  #define icmp6_rt_lifetime      icmp6_dataun.u_nd_ra.rt_lifetime
>  } __packed;
> 
> +/*
> + * struct icmp6_ra_prefix_info - Prefix Information option of the
> ICMPv6 message
> + * The Prefix Information option provides hosts with on-link
> prefixes and
> + * prefixes for Address Autoconfiguration. Refer to RFC 4861 for
> more info.
> + */
> +struct icmp6_ra_prefix_info {
> +       u8      type;           /* Type is 3 for Prefix Information.
> */
> +       u8      len;            /* Len is 4 for Prefix Information.
> */
> +       /* The number of leading bits in the Prefix that are valid.
> */
> +       u8      prefix_len;
> +       u8      reserved1:6,    /* MUST be ignored by the receiver.
> */
> +               aac:1,          /* autonomous address-configuration
> flag */
> +       /* Indicates that this prefix can be used for on-link
> determination. */
> +               on_link:1;
> +       /*
> +        * The length of time in seconds that the prefix is valid for
> the
> +        * purpose of on-link determination.
> +        */
> +       __be32  valid_lifetime;
> +       /* The length of time addresses remain preferred. */
> +       __be32  preferred_lifetime;
> +       __be32  reserved2;      /* MUST be ignored by the receiver.
> */
> +       /*
> +        * Prefix is an IP address or a prefix of an IP address. The
> Prefix
> +        * Length field contains the number of valid leading bits in
> the prefix.
> +        * The bits in the prefix after the prefix length are
> reserved and MUST
> +        * be initialized to zero by the sender and ignored by the
> receiver.
> +        */
> +       struct in6_addr prefix;
> +};
> +
>  extern struct in6_addr const net_null_addr_ip6;        /* NULL IPv6
> address */
>  extern struct in6_addr net_gateway6;   /* Our gateways IPv6 address
> */
>  extern struct in6_addr net_ip6;        /* Our IPv6 addr (0 =
> unknown) */
> diff --git a/net/ndisc.c b/net/ndisc.c
> index 367dae7..0b27779 100644
> --- a/net/ndisc.c
> +++ b/net/ndisc.c
> @@ -13,6 +13,8 @@
>  #include <net.h>
>  #include <net6.h>
>  #include <ndisc.h>
> +#include <stdlib.h>
> +#include <linux/delay.h>
> 
>  /* IPv6 destination address of packet waiting for ND */
>  struct in6_addr net_nd_sol_packet_ip6 = ZERO_IPV6_ADDR;
> @@ -29,31 +31,37 @@ int net_nd_tx_packet_size;
>  ulong net_nd_timer_start;
>  /* the number of requests we have sent so far */
>  int net_nd_try;
> +struct in6_addr all_routers = ALL_ROUTERS_MULT_ADDR;
> +
> +#define MAX_RTR_SOLICITATIONS          3
> +/* The maximum time to delay sending the first router solicitation
> message. */
> +#define MAX_SOLICITATION_DELAY         1 // 1 second
> +/* The time to wait before sending the next router solicitation
> message. */
> +#define RTR_SOLICITATION_INTERVAL      4000 // 4 seconds
> 
>  #define IP6_NDISC_OPT_SPACE(len) (((len) + 2 + 7) & ~7)
> 
>  /**
>   * ndisc_insert_option() - Insert an option into a neighbor
> discovery packet
>   *
> - * @ndisc:     pointer to ND packet
> + * @opt:       pointer to the option element of the neighbor
> discovery packet
>   * @type:      option type to insert
>   * @data:      option data to insert
>   * @len:       data length
>   * Return: the number of bytes inserted (which may be >= len)
>   */
> -static int
> -ndisc_insert_option(struct nd_msg *ndisc, int type, u8 *data, int
> len)
> +static int ndisc_insert_option(__u8 *opt, int type, u8 *data, int
> len)
>  {
>         int space = IP6_NDISC_OPT_SPACE(len);
> 
> -       ndisc->opt[0] = type;
> -       ndisc->opt[1] = space >> 3;
> -       memcpy(&ndisc->opt[2], data, len);
> +       opt[0] = type;
> +       opt[1] = space >> 3;
> +       memcpy(&opt[2], data, len);
>         len += 2;
> 
>         /* fill the remainder with 0 */
>         if (space - len > 0)
> -               memset(&ndisc->opt[len], '\0', space - len);
> +               memset(&opt[len], '\0', space - len);
> 
>         return space;
>  }
> @@ -123,7 +131,7 @@ static void ip6_send_ns(struct in6_addr
> *neigh_addr)
> 
>         /* Set the target address and llsaddr option */
>         net_copy_ip6(&msg->target, neigh_addr);
> -       ndisc_insert_option(msg, ND_OPT_SOURCE_LL_ADDR, net_ethaddr,
> +       ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR,
> net_ethaddr,
>                             INETHADDRSZ);
> 
>         /* checksum */
> @@ -137,6 +145,76 @@ static void ip6_send_ns(struct in6_addr
> *neigh_addr)
>         net_send_packet(net_tx_packet, (pkt - net_tx_packet));
>  }
> 
> +/*
> + * ip6_send_rs() - Send IPv6 Router Solicitation Message.
> + *
> + * A router solicitation is sent to discover a router. RS message
> creation is
> + * based on RFC 4861 section 4.1. Router Solicitation Message
> Format.
> + */
> +void ip6_send_rs(void)
> +{
> +       unsigned char enetaddr[6];
> +       struct rs_msg *msg;
> +       __u16 icmp_len;
> +       uchar *pkt;
> +       unsigned short csum;
> +       unsigned int pcsum;
> +       static unsigned int retry_count;
> +
> +       if (!ip6_is_unspecified_addr(&net_gateway6) &&
> +           net_prefix_length != 0) {
> +               net_set_state(NETLOOP_SUCCESS);
> +               return;
> +       } else if (retry_count >= MAX_RTR_SOLICITATIONS) {
> +               net_set_state(NETLOOP_FAIL);
> +               net_set_timeout_handler(0, NULL);
> +               retry_count = 0;
> +               return;
> +       }
> +
> +       printf("ROUTER SOLICITATION %d\n", retry_count + 1);
> +
> +       ip6_make_mult_ethdstaddr(enetaddr, &all_routers);
> +       /*
> +        * ICMP length is the size of ICMP header (8) + one option
> (8) = 16.
> +        * The option is 2 bytes of type and length + 6 bytes for
> MAC.
> +        */
> +       icmp_len = sizeof(struct icmp6hdr) +
> IP6_NDISC_OPT_SPACE(INETHADDRSZ);
> +
> +       pkt = (uchar *)net_tx_packet;
> +       pkt += net_set_ether(pkt, enetaddr, PROT_IP6);
> +       pkt += ip6_add_hdr(pkt, &net_link_local_ip6, &all_routers,
> PROT_ICMPV6,
> +                          IPV6_NDISC_HOPLIMIT, icmp_len);
> +
> +       /* ICMPv6 - RS */
> +       msg = (struct rs_msg *)pkt;
> +       msg->icmph.icmp6_type = IPV6_NDISC_ROUTER_SOLICITATION;
> +       msg->icmph.icmp6_code = 0;
> +       memset(&msg->icmph.icmp6_cksum, 0, sizeof(__be16));
> +       memset(&msg->icmph.icmp6_unused, 0, sizeof(__be32));
> +
> +       /* Set the llsaddr option */
> +       ndisc_insert_option(msg->opt, ND_OPT_SOURCE_LL_ADDR,
> net_ethaddr,
> +                           INETHADDRSZ);
> +
> +       /* checksum */
> +       pcsum = csum_partial((__u8 *)msg, icmp_len, 0);
> +       csum = csum_ipv6_magic(&net_link_local_ip6, &all_routers,
> +                              icmp_len, PROT_ICMPV6, pcsum);
> +       msg->icmph.icmp6_cksum = csum;
> +       pkt += icmp_len;
> +
> +       /* Wait up to 1 second if it is the first try to get the RA
> */
> +       if (retry_count == 0)
> +               udelay(((unsigned int)rand() % 1000000) *
> MAX_SOLICITATION_DELAY);
> +
> +       /* send it! */
> +       net_send_packet(net_tx_packet, (pkt - net_tx_packet));
> +
> +       retry_count++;
> +       net_set_timeout_handler(RTR_SOLICITATION_INTERVAL,
> ip6_send_rs);
> +}
> +
>  static void
>  ip6_send_na(uchar *eth_dst_addr, struct in6_addr *neigh_addr,
>             struct in6_addr *target)
> @@ -167,7 +245,7 @@ ip6_send_na(uchar *eth_dst_addr, struct in6_addr
> *neigh_addr,
>         msg->icmph.icmp6_dataun.u_nd_advt.override = 1;
>         /* Set the target address and lltargetaddr option */
>         net_copy_ip6(&msg->target, target);
> -       ndisc_insert_option(msg, ND_OPT_TARGET_LL_ADDR, net_ethaddr,
> +       ndisc_insert_option(msg->opt, ND_OPT_TARGET_LL_ADDR,
> net_ethaddr,
>                             INETHADDRSZ);
> 
>         /* checksum */
> @@ -223,6 +301,10 @@ int ndisc_timeout_check(void)
>         return 1;
>  }
> 
> +/*
> + * ndisc_init() - Make initial steps for ND state machine.
> + * Usually move variables into initial state.
> + */
>  void ndisc_init(void)
>  {
>         net_nd_packet_mac = NULL;
> @@ -234,12 +316,125 @@ void ndisc_init(void)
>         net_nd_tx_packet -= (ulong)net_nd_tx_packet % PKTALIGN;
>  }
> 
> +/*
> + * validate_ra() - Validate the router advertisement message.
> + *
> + * @ip6: Pointer to the router advertisement packet
> + *
> + * Check if the router advertisement message is valid. Conditions
> are
> + * according to RFC 4861 section 6.1.2. Validation of Router
> Advertisement
> + * Messages.
> + *
> + * Return: true if the message is valid and false if it is invalid.
> + */
> +bool validate_ra(struct ip6_hdr *ip6)
> +{
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +
> +       /* ICMP length (derived from the IP length) should be 16 or
> more octets. */
> +       if (ip6->payload_len < 16)
> +               return false;
> +
> +       /* Source IP Address should be a valid link-local address. */
> +       if ((ntohs(ip6->saddr.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK)
> !=
> +           IPV6_LINK_LOCAL_PREFIX)
> +               return false;
> +
> +       /*
> +        * The IP Hop Limit field should have a value of 255, i.e.,
> the packet
> +        * could not possibly have been forwarded by a router.
> +        */
> +       if (ip6->hop_limit != 255)
> +               return false;
> +
> +       /* ICMP checksum has already been checked in net_ip6_handler.
> */
> +
> +       if (icmp->icmp6_code != 0)
> +               return false;
> +
> +       return true;
> +}
> +
> +/*
> + * process_ra() - Process the router advertisement packet.
> + *
> + * @ip6: Pointer to the router advertisement packet
> + * @len: Length of the router advertisement packet
> + *
> + * Process the received router advertisement message.
> + * Although RFC 4861 requires retaining at least two router
> addresses, we only
> + * keep one because of the U-Boot limitations and its goal of
> lightweight code.
> + *
> + * Return: 0 - RA is a default router and contains valid prefix
> information.
> + * Non-zero - RA options are invalid or do not indicate it is a
> default router
> + * or do not contain valid prefix information.
> + */
> +int process_ra(struct ip6_hdr *ip6, int len)
> +{
> +       /* Pointer to the ICMP section of the packet */
> +       struct icmp6hdr *icmp = (struct icmp6hdr *)(ip6 + 1);
> +       struct ra_msg *msg = (struct ra_msg *)icmp;
> +       int remaining_option_len = len - IP6_HDR_SIZE - sizeof(struct
> ra_msg);
> +       unsigned short int option_len;  /* Length of each option */
> +       /* Pointer to the ICMPv6 message options */
> +       unsigned char *option = NULL;
> +       /* 8-bit identifier of the type of ICMPv6 option */
> +       unsigned char type = 0;
> +       struct icmp6_ra_prefix_info *prefix = NULL;
> +
> +       /* Ignore the packet if router lifetime is 0. */
> +       if (!icmp->icmp6_rt_lifetime)
> +               return -EOPNOTSUPP;
> +
> +       /* Processing the options */
> +       option = msg->opt;
> +       while (remaining_option_len > 0) {
> +               /* The 2nd byte of the option is its length. */
> +               option_len = option[1];
> +               /* All included options should have a positive
> length. */
> +               if (option_len == 0)
> +                       return -EINVAL;
> +
> +               type = option[0];
> +               /* All option types except Prefix Information are
> ignored. */
> +               switch (type) {
> +               case ND_OPT_SOURCE_LL_ADDR:
> +               case ND_OPT_TARGET_LL_ADDR:
> +               case ND_OPT_REDIRECT_HDR:
> +               case ND_OPT_MTU:
> +                       break;
> +               case ND_OPT_PREFIX_INFO:
> +                       prefix = (struct icmp6_ra_prefix_info
> *)option;
> +                       /* The link-local prefix 0xfe80::/10 is
> ignored. */
> +                       if ((ntohs(prefix->prefix.s6_addr16[0]) &
> +                            IPV6_LINK_LOCAL_MASK) ==
> IPV6_LINK_LOCAL_PREFIX)
> +                               break;
> +                       if (prefix->on_link && ntohl(prefix-
> >valid_lifetime)) {
> +                               net_prefix_length = prefix-
> >prefix_len;
> +                               net_gateway6 = ip6->saddr;
> +                               return 0;
> +                       }
> +                       break;
> +               default:
> +                       debug("Unknown IPv6 Neighbor Discovery Option
> 0x%x\n",
> +                             type);
> +               }
> +
> +               option_len <<= 3; /* Option length is a multiple of
> 8. */
> +               remaining_option_len -= option_len;
> +               option += option_len;
> +       }
> +
> +       return -EADDRNOTAVAIL;
> +}
> +
>  int ndisc_receive(struct ethernet_hdr *et, struct ip6_hdr *ip6, int
> len)
>  {
>         struct icmp6hdr *icmp =
>             (struct icmp6hdr *)(((uchar *)ip6) + IP6_HDR_SIZE);
>         struct nd_msg *ndisc = (struct nd_msg *)icmp;
>         uchar neigh_eth_addr[6];
> +       int err = 0;    // The error code returned calling functions.
> 
>         switch (icmp->icmp6_type) {
>         case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
> @@ -280,6 +475,36 @@ int ndisc_receive(struct ethernet_hdr *et,
> struct ip6_hdr *ip6, int len)
>                         net_nd_packet_mac = NULL;
>                 }
>                 break;
> +       case IPV6_NDISC_ROUTER_SOLICITATION:
> +               break;
> +       case IPV6_NDISC_ROUTER_ADVERTISEMENT:
> +               debug("Received router advertisement for %pI6c from
> %pI6c\n",
> +                     &ip6->daddr, &ip6->saddr);
> +               /*
> +                * If gateway and prefix are set, the RA packet is
> ignored. The
> +                * reason is that the U-Boot code is supposed to be
> as compact
> +                * as possible and does not need to take care of
> multiple
> +                * routers. In addition to that, U-Boot does not want
> to handle
> +                * scenarios like a router setting its lifetime to
> zero to
> +                * indicate it is not routing anymore. U-Boot program
> has a
> +                * short life when the system boots up and does not
> need such
> +                * sophistication.
> +                */
> +               if (!ip6_is_unspecified_addr(&net_gateway6) &&
> +                   net_prefix_length != 0) {
> +                       break;
> +               }
> +               if (!validate_ra(ip6)) {
> +                       debug("Invalid router advertisement
> message.\n");
> +                       break;
> +               }
> +               err = process_ra(ip6, len);
> +               if (err)
> +                       debug("Ignored router advertisement. Error:
> %d\n", err);
> +               else
> +                       printf("Set gatewayip6: %pI6c, prefix_length:
> %d\n",
> +                              &net_gateway6, net_prefix_length);
> +               break;
>         default:
>                 debug("Unexpected ICMPv6 type 0x%x\n", icmp-
> >icmp6_type);
>                 return -1;
> diff --git a/net/net.c b/net/net.c
> index c9a749f..9d15329 100644
> --- a/net/net.c
> +++ b/net/net.c
> @@ -24,7 +24,7 @@
>   *                     - name of bootfile
>   *     Next step:      ARP
>   *
> - * LINK_LOCAL:
> + * LINKLOCAL:
>   *
>   *     Prerequisites:  - own ethernet address
>   *     We want:        - own IP address
> @@ -122,6 +122,7 @@
>  #endif
>  #include <net/tcp.h>
>  #include <net/wget.h>
> +#include "net_rand.h"
> 
>  /** BOOTP EXTENTIONS **/
> 
> @@ -346,6 +347,8 @@ void net_auto_load(void)
> 
>  static int net_init_loop(void)
>  {
> +       static bool first_call = true;
> +
>         if (eth_get_dev()) {
>                 memcpy(net_ethaddr, eth_get_ethaddr(), 6);
> 
> @@ -365,6 +368,12 @@ static int net_init_loop(void)
>                  */
>                 return -ENONET;
> 
> +       if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +               if (first_call && use_ip6) {
> +                       first_call = false;
> +                       srand_mac(); /* This is for rand used in
> ip6_send_rs. */
> +                       net_loop(RS);
> +               }
>         return 0;
>  }
> 
> @@ -574,6 +583,10 @@ restart:
>                         ncsi_probe_packages();
>                         break;
>  #endif
> +               case RS:
> +                       if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +                               ip6_send_rs();
> +                       break;
>                 default:
>                         break;
>                 }
> @@ -671,7 +684,13 @@ restart:
>                         x = time_handler;
>                         time_handler = (thand_f *)0;
>                         (*x)();
> -               }
> +               } else if (IS_ENABLED(CONFIG_IPV6_ROUTER_DISCOVERY))
> +                       if (time_handler && protocol == RS)
> +                               if
> (!ip6_is_unspecified_addr(&net_gateway6) &&
> +                                   net_prefix_length != 0) {
> +                                       net_set_state(NETLOOP_SUCCESS
> );
> +                                       net_set_timeout_handler(0,
> NULL);
> +                               }
> 
>                 if (net_state == NETLOOP_FAIL)
>                         ret = net_start_again();
> diff --git a/net/net6.c b/net/net6.c
> index 75577bc..2dd64c0 100644
> --- a/net/net6.c
> +++ b/net/net6.c
> @@ -413,6 +413,7 @@ int net_ip6_handler(struct ethernet_hdr *et,
> struct ip6_hdr *ip6, int len)
>                         break;
>                 case IPV6_NDISC_NEIGHBOUR_SOLICITATION:
>                 case IPV6_NDISC_NEIGHBOUR_ADVERTISEMENT:
> +               case IPV6_NDISC_ROUTER_ADVERTISEMENT:
>                         ndisc_receive(et, ip6, len);
>                         break;
>                 default:
> --
> 1.8.3.1
> 
> 


Tested on Si-five Hi-five Unmatched board (RISC-V)

Good. Thanks!

Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

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

* Re: [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
  2023-04-24  7:58     ` Vyacheslav V. Mitrofanov
  2023-04-24  8:03     ` Vyacheslav V. Mitrofanov
@ 2023-05-04 14:30     ` Sergei Antonov
  2023-05-04 14:52     ` Sergei Antonov
  2023-05-06 14:53     ` Tom Rini
  4 siblings, 0 replies; 35+ messages in thread
From: Sergei Antonov @ 2023-05-04 14:30 UTC (permalink / raw)
  To: emohandesi
  Cc: u-boot, sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov

On Sat, 22 Apr 2023 at 03:08, <emohandesi@linux.microsoft.com> wrote:
>
> From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>

Hello, Ehsan! Good patch, but one little change is needed.

> +struct icmp6_ra_prefix_info {
> +       u8      type;           /* Type is 3 for Prefix Information. */
> +       u8      len;            /* Len is 4 for Prefix Information. */
> +       /* The number of leading bits in the Prefix that are valid. */
> +       u8      prefix_len;
> +       u8      reserved1:6,    /* MUST be ignored by the receiver. */
> +               aac:1,          /* autonomous address-configuration flag */
> +       /* Indicates that this prefix can be used for on-link determination. */
> +               on_link:1;
> +       /*
> +        * The length of time in seconds that the prefix is valid for the
> +        * purpose of on-link determination.
> +        */
> +       __be32  valid_lifetime;
> +       /* The length of time addresses remain preferred. */
> +       __be32  preferred_lifetime;
> +       __be32  reserved2;      /* MUST be ignored by the receiver. */
> +       /*
> +        * Prefix is an IP address or a prefix of an IP address. The Prefix
> +        * Length field contains the number of valid leading bits in the prefix.
> +        * The bits in the prefix after the prefix length are reserved and MUST
> +        * be initialized to zero by the sender and ignored by the receiver.
> +        */
> +       struct in6_addr prefix;
> +};

Here it should end with:
} __packed;
Because this structure may be placed at a badly aligned offset within
a packet. For example, at offset 0x46 within Ethernet packet.

Other than that:
Tested-by: Sergei Antonov <saproj@gmail.com>
Reviewed-by: Sergei Antonov <saproj@gmail.com>

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

* Re: [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
                       ` (2 preceding siblings ...)
  2023-05-04 14:30     ` Sergei Antonov
@ 2023-05-04 14:52     ` Sergei Antonov
  2023-05-05 14:13       ` Ehsan Mohandesi
  2023-05-06 14:53     ` Tom Rini
  4 siblings, 1 reply; 35+ messages in thread
From: Sergei Antonov @ 2023-05-04 14:52 UTC (permalink / raw)
  To: emohandesi
  Cc: u-boot, sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov

On Sat, 22 Apr 2023 at 03:08, <emohandesi@linux.microsoft.com> wrote:

> +                       if (prefix->on_link && ntohl(prefix->valid_lifetime)) {
> +                               net_prefix_length = prefix->prefix_len;
> +                               net_gateway6 = ip6->saddr;
> +                               return 0;

Is it OK that prefix->prefix_len is used, but prefix->prefix is not?
Just curious.

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

* Re: [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-05-04 14:52     ` Sergei Antonov
@ 2023-05-05 14:13       ` Ehsan Mohandesi
  0 siblings, 0 replies; 35+ messages in thread
From: Ehsan Mohandesi @ 2023-05-05 14:13 UTC (permalink / raw)
  To: Sergei Antonov
  Cc: u-boot, sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov

On 5/4/2023 9:52 AM, Sergei Antonov wrote:
> On Sat, 22 Apr 2023 at 03:08,<emohandesi@linux.microsoft.com>  wrote:
>
>> +                       if (prefix->on_link && ntohl(prefix->valid_lifetime)) {
>> +                               net_prefix_length = prefix->prefix_len;
>> +                               net_gateway6 = ip6->saddr;
>> +                               return 0;
> Is it OK that prefix->prefix_len is used, but prefix->prefix is not?
> Just curious.

prefix->prefix is used just before this if statement to make sure it is 
not a link-local address as shown below.

if((ntohs(prefix->prefix.s6_addr16[0]) & IPV6_LINK_LOCAL_MASK) == 
IPV6_LINK_LOCAL_PREFIX) break; if(prefix->on_link && 
ntohl(prefix->valid_lifetime)) { net_prefix_length = prefix->prefix_len; 
net_gateway6 = ip6->saddr; return0; }

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

* Re: [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
                       ` (3 preceding siblings ...)
  2023-05-04 14:52     ` Sergei Antonov
@ 2023-05-06 14:53     ` Tom Rini
  2023-05-10 10:05       ` Sergei Antonov
  4 siblings, 1 reply; 35+ messages in thread
From: Tom Rini @ 2023-05-06 14:53 UTC (permalink / raw)
  To: emohandesi
  Cc: u-boot, sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov, saproj

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

On Fri, Apr 21, 2023 at 05:08:21PM -0700, emohandesi@linux.microsoft.com wrote:

> From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> 
> In IPv6, the default gateway and prefix length are determined by receiving
> a router advertisement as defined in -
> https://www.rfc-editor.org/rfc/rfc4861.
> 
> Add support for sending router solicitation (RS) and processing router
> advertisements (RA).
> 
> If the RA has prefix info option and following conditions are met, then
> gatewayip6 and net_prefix_length of ip6addr env variables are initialized.
> These are later consumed by IPv6 code for non-local destination IP.
> 
> - "Router Lifetime" != 0
> - Prefix is NOT link-local prefix (0xfe80::/10)
> - L flag is 1
> - "Valid Lifetime" != 0
> 
> Timing Parameters:
> - MAX_RTR_SOLICITATION_DELAY (0-1s)
> - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
> - MAX_RTR_SOLICITATIONS (3 RS transmissions)
> 
> The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and invoked
> automatically from net_init_loop().
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>Reviewed-by: 
> Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
> Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
> Tested-by: Sergei Antonov <saproj@gmail.com>
> Reviewed-by: Sergei Antonov <saproj@gmail.com>

Applied to u-boot/master, thanks!

-- 
Tom

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

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

* Re: [PATCH v4 2/3] test/py: IPv6 network discovery test
  2023-04-22  0:08   ` [PATCH v4 2/3] test/py: IPv6 network discovery test emohandesi
  2023-04-24  7:59     ` Vyacheslav V. Mitrofanov
@ 2023-05-06 14:53     ` Tom Rini
  1 sibling, 0 replies; 35+ messages in thread
From: Tom Rini @ 2023-05-06 14:53 UTC (permalink / raw)
  To: emohandesi
  Cc: u-boot, sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov, saproj

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

On Fri, Apr 21, 2023 at 05:08:22PM -0700, emohandesi@linux.microsoft.com wrote:

> From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> 
> Test the IPv6 network discovery feature if indicated by boardenv file.
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

Applied to u-boot/master, thanks!

-- 
Tom

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

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

* Re: [PATCH v4 3/3] test: eth: IPv6 network discovery unit test
  2023-04-22  0:08   ` [PATCH v4 3/3] test: eth: IPv6 network discovery unit test emohandesi
  2023-04-24  8:01     ` Vyacheslav V. Mitrofanov
@ 2023-05-06 14:53     ` Tom Rini
  1 sibling, 0 replies; 35+ messages in thread
From: Tom Rini @ 2023-05-06 14:53 UTC (permalink / raw)
  To: emohandesi
  Cc: u-boot, sjg, mario.six, joe.hershberger, rfried.dev, xypron.glpk,
	ilias.apalodimas, masahisa.kojima, pali, tobias, john,
	v.v.mitrofanov, saproj

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

On Fri, Apr 21, 2023 at 05:08:23PM -0700, emohandesi@linux.microsoft.com wrote:

> From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> 
> Test router advertisement validation and processing functions.
> 
> Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>

Applied to u-boot/master, thanks!

-- 
Tom

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

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

* Re: [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-05-06 14:53     ` Tom Rini
@ 2023-05-10 10:05       ` Sergei Antonov
  2023-05-10 11:16         ` Vyacheslav V. Mitrofanov
  0 siblings, 1 reply; 35+ messages in thread
From: Sergei Antonov @ 2023-05-10 10:05 UTC (permalink / raw)
  To: Tom Rini
  Cc: emohandesi, u-boot, sjg, mario.six, joe.hershberger, rfried.dev,
	xypron.glpk, ilias.apalodimas, masahisa.kojima, pali, tobias,
	john, v.v.mitrofanov

On Sat, 6 May 2023 at 17:53, Tom Rini <trini@konsulko.com> wrote:
>
> On Fri, Apr 21, 2023 at 05:08:21PM -0700, emohandesi@linux.microsoft.com wrote:
>
> > From: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> >
> > In IPv6, the default gateway and prefix length are determined by receiving
> > a router advertisement as defined in -
> > https://www.rfc-editor.org/rfc/rfc4861.
> >
> > Add support for sending router solicitation (RS) and processing router
> > advertisements (RA).
> >
> > If the RA has prefix info option and following conditions are met, then
> > gatewayip6 and net_prefix_length of ip6addr env variables are initialized.
> > These are later consumed by IPv6 code for non-local destination IP.
> >
> > - "Router Lifetime" != 0
> > - Prefix is NOT link-local prefix (0xfe80::/10)
> > - L flag is 1
> > - "Valid Lifetime" != 0
> >
> > Timing Parameters:
> > - MAX_RTR_SOLICITATION_DELAY (0-1s)
> > - RTR_SOLICITATION_INTERVAL (4s) (min retransmit delay)
> > - MAX_RTR_SOLICITATIONS (3 RS transmissions)
> >
> > The functionality is enabled by CONFIG_IPV6_ROUTER_DISCOVERY and invoked
> > automatically from net_init_loop().
> >
> > Signed-off-by: Ehsan Mohandesi <emohandesi@linux.microsoft.com>
> > Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>Reviewed-by:
> > Tested-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
> > Reviewed-by: Viacheslav Mitrofanov <v.v.mitrofanov@yadro.com>
> > Tested-by: Sergei Antonov <saproj@gmail.com>
> > Reviewed-by: Sergei Antonov <saproj@gmail.com>
>
> Applied to u-boot/master, thanks!

Hey! It was added without "__packed", see
https://lists.denx.de/pipermail/u-boot/2023-May/517370.html

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

* Re: [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery.
  2023-05-10 10:05       ` Sergei Antonov
@ 2023-05-10 11:16         ` Vyacheslav V. Mitrofanov
  0 siblings, 0 replies; 35+ messages in thread
From: Vyacheslav V. Mitrofanov @ 2023-05-10 11:16 UTC (permalink / raw)
  To: trini, saproj
  Cc: joe.hershberger, emohandesi, mario.six, tobias, xypron.glpk,
	rfried.dev, ilias.apalodimas, pali, john, sjg, u-boot,
	masahisa.kojima

On Wed, 2023-05-10 at 13:05 +0300, Sergei Antonov wrote:
> 
> Hey! It was added without "__packed", see
> https://lists.denx.de/pipermail/u-boot/2023-May/517370.html
> 
Hello, Sergei. 

I see Ehsan hadn't sent v5 before v4 was applied.
I would make a patch with __packed attribute and post it.
Thanks

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

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

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-03-02 16:58 [PATCH] net: ipv6: Add support for default gateway discovery emohandesi
2023-03-16  8:47 ` Vyacheslav V. Mitrofanov
2023-03-23 16:44   ` Ehsan Mohandesi
2023-03-23 16:59     ` Vyacheslav V. Mitrofanov
2023-04-01 19:02 ` Ramon Fried
2023-04-10 19:34 ` [PATCH v2 0/4] Add IPv6 Network Discovery emohandesi
2023-04-10 19:34   ` [PATCH v2 1/4] Revert "net: ipv6: Add support for default gateway discovery." emohandesi
2023-04-11  7:01     ` Vyacheslav V. Mitrofanov
2023-04-11 14:51       ` Ehsan Mohandesi
2023-04-10 19:34   ` [PATCH v2 2/4] net: ipv6: Add support for default gateway discovery emohandesi
2023-04-10 19:34   ` [PATCH v2 3/4] test/py: IPv6 network discovery test emohandesi
2023-04-10 19:34   ` [PATCH v2 4/4] test: eth: IPv6 network discovery unit test emohandesi
2023-04-12 16:10 ` [PATCH v3 0/3] Add IPv6 Network Discovery emohandesi
2023-04-12 16:10   ` [PATCH v3 1/3] net: ipv6: Add support for default gateway discovery emohandesi
2023-04-20 13:44     ` Vyacheslav V. Mitrofanov
2023-04-12 16:10   ` [PATCH v3 2/3] test/py: IPv6 network discovery test emohandesi
2023-04-19  1:46     ` Simon Glass
2023-04-12 16:10   ` [PATCH v3 3/3] test: eth: IPv6 network discovery unit test emohandesi
2023-04-19  1:46     ` Simon Glass
2023-04-22  0:08 ` [PATCH v4 0/3] Add IPv6 Network Discovery emohandesi
2023-04-22  0:08   ` [PATCH v4 1/3] net: ipv6: Add support for default gateway discovery emohandesi
2023-04-24  7:58     ` Vyacheslav V. Mitrofanov
2023-04-24  8:03     ` Vyacheslav V. Mitrofanov
2023-05-04 14:30     ` Sergei Antonov
2023-05-04 14:52     ` Sergei Antonov
2023-05-05 14:13       ` Ehsan Mohandesi
2023-05-06 14:53     ` Tom Rini
2023-05-10 10:05       ` Sergei Antonov
2023-05-10 11:16         ` Vyacheslav V. Mitrofanov
2023-04-22  0:08   ` [PATCH v4 2/3] test/py: IPv6 network discovery test emohandesi
2023-04-24  7:59     ` Vyacheslav V. Mitrofanov
2023-05-06 14:53     ` Tom Rini
2023-04-22  0:08   ` [PATCH v4 3/3] test: eth: IPv6 network discovery unit test emohandesi
2023-04-24  8:01     ` Vyacheslav V. Mitrofanov
2023-05-06 14:53     ` Tom Rini

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).