diff mbox series

[5/6] icmp6,netconfig: Skip SLLAO if RS sent from optimistic address

Message ID 20220920133201.3303119-5-andrew.zaborowski@intel.com (mailing list archive)
State Accepted, archived
Headers show
Series [1/6] net: Allow padding in net_domain_list_parse | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success

Commit Message

Andrew Zaborowski Sept. 20, 2022, 1:32 p.m. UTC
Make sure we don't include de Source Link-Local Address Option in our
Router Solicitations until the source link-local address completes DAD.
Extand the recently added l_icmp6_client_set_link_local_address() with a
parameter to tell us whether the address is optimistic.  Don't change
the l_dhcp6_client_set_link_local_address signature to avoid breaking
existing users.
---
 ell/dhcp6.c     |  2 +-
 ell/icmp6.c     | 29 +++++++++++++++++++++--------
 ell/icmp6.h     |  3 ++-
 ell/netconfig.c | 30 ++++++++++++++++++++++++++----
 4 files changed, 50 insertions(+), 14 deletions(-)
diff mbox series

Patch

diff --git a/ell/dhcp6.c b/ell/dhcp6.c
index e234eb0..2792ff2 100644
--- a/ell/dhcp6.c
+++ b/ell/dhcp6.c
@@ -1623,7 +1623,7 @@  LIB_EXPORT bool l_dhcp6_client_set_link_local_address(
 		return false;
 
 	if (!client->nora)
-		l_icmp6_client_set_link_local_address(client->icmp6, ll);
+		l_icmp6_client_set_link_local_address(client->icmp6, ll, false);
 
 	return true;
 }
diff --git a/ell/icmp6.c b/ell/icmp6.c
index 5ddc494..f087ba3 100644
--- a/ell/icmp6.c
+++ b/ell/icmp6.c
@@ -206,7 +206,8 @@  static uint16_t icmp6_checksum(const struct iovec *iov, unsigned int iov_len)
 
 static int icmp6_send_router_solicitation(int s, int ifindex,
 					const uint8_t src_mac[static 6],
-					const struct in6_addr *src_ip)
+					const struct in6_addr *src_ip,
+					bool src_ip_optimistic)
 {
 	struct nd_router_solicit rs = {
 		.nd_rs_type = ND_ROUTER_SOLICIT,
@@ -247,11 +248,17 @@  static int icmp6_send_router_solicitation(int s, int ifindex,
 
 	memcpy(&ip_hdr.ip6_src, src_ip, 16);
 
-	if (l_memeqzero(src_ip, 16)) {
+	if (l_memeqzero(src_ip, 16) || src_ip_optimistic) {
 		/*
-		 * radvd will discard and warn about RSs from the unspecified
-		 * address with the SLLAO, omit that option by dropping the
-		 * last two iov buffers.
+		 * RFC 4429 Section 3.2: "A node MUST NOT send a Router
+		 * Solicitation with a SLLAO from an Optimistic Address.
+		 * Router Solicitations SHOULD be sent from a non-Optimistic
+		 * or the Unspecified Address; however, they MAY be sent from
+		 * an Optimistic Address as long as the SLLAO is not included."
+		 *
+		 * Additionally radvd will also discard and warn about RSs
+		 * from the unspecified address with the SLLAO.  Omit that
+		 * option by dropping the last two iov buffers.
 		 */
 		msg.msg_iovlen -= 2;
 		ip_hdr.ip6_plen = htons(ntohs(ip_hdr.ip6_plen) - rs_sllao_size);
@@ -347,6 +354,7 @@  struct l_icmp6_client {
 	uint64_t retransmit_time;
 	struct l_io *io;
 	struct in6_addr src_ip;
+	bool src_ip_optimistic;
 
 	struct l_icmp6_router *ra;
 	struct l_netlink *rtnl;
@@ -547,7 +555,8 @@  static void icmp6_client_timeout_send(struct l_timeout *timeout,
 
 	r = icmp6_send_router_solicitation(l_io_get_fd(client->io),
 						client->ifindex, client->mac,
-						&client->src_ip);
+						&client->src_ip,
+						client->src_ip_optimistic);
 	if (r < 0) {
 		CLIENT_DEBUG("Error sending Router Solicitation: %s",
 				strerror(-r));
@@ -766,7 +775,7 @@  LIB_EXPORT bool l_icmp6_client_set_route_priority(
 
 LIB_EXPORT bool l_icmp6_client_set_link_local_address(
 						struct l_icmp6_client *client,
-						const char *ll)
+						const char *ll, bool optimistic)
 {
 	if (unlikely(!client))
 		return false;
@@ -777,7 +786,11 @@  LIB_EXPORT bool l_icmp6_client_set_link_local_address(
 	 * is fine.  Once we have a confirmed link-local address we use that
 	 * as the source address.
 	 */
-	return inet_pton(AF_INET6, ll, &client->src_ip) == 1;
+	if (inet_pton(AF_INET6, ll, &client->src_ip) != 1)
+		return false;
+
+	client->src_ip_optimistic = optimistic;
+	return true;
 }
 
 struct l_icmp6_router *_icmp6_router_new()
diff --git a/ell/icmp6.h b/ell/icmp6.h
index ffbb8a8..ad3d661 100644
--- a/ell/icmp6.h
+++ b/ell/icmp6.h
@@ -66,7 +66,8 @@  bool l_icmp6_client_set_rtnl(struct l_icmp6_client *client,
 bool l_icmp6_client_set_route_priority(struct l_icmp6_client *client,
 						uint32_t priority);
 bool l_icmp6_client_set_link_local_address(struct l_icmp6_client *client,
-						const char *ll);
+						const char *ll,
+						bool optimistic);
 
 char *l_icmp6_router_get_address(const struct l_icmp6_router *r);
 bool l_icmp6_router_get_managed(const struct l_icmp6_router *r);
diff --git a/ell/netconfig.c b/ell/netconfig.c
index 765703b..c558cad 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -93,6 +93,7 @@  struct l_netconfig {
 	unsigned int orig_optimistic_dad;
 	uint8_t mac[ETH_ALEN];
 	struct l_timeout *ra_timeout;
+	bool have_lla;
 	enum {
 		NETCONFIG_V6_METHOD_UNSET,
 		NETCONFIG_V6_METHOD_DHCP,
@@ -702,7 +703,7 @@  static bool netconfig_check_start_dhcp6(struct l_netconfig *nc)
 		return true;
 
 	/* Don't start DHCPv6 while waiting for the link-local address */
-	if (l_queue_find(addr_wait_list, netconfig_match, nc))
+	if (!nc->have_lla)
 		return true;
 
 	return l_dhcp6_client_start(nc->dhcp6_client);
@@ -1844,6 +1845,7 @@  static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
 {
 	struct in6_addr in6;
 	_auto_(l_free) char *ip = NULL;
+	bool new_lla;
 
 	if ((ifa->ifa_flags & IFA_F_TENTATIVE) &&
 			!(ifa->ifa_flags & IFA_F_OPTIMISTIC))
@@ -1858,16 +1860,28 @@  static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
 	if (!IN6_IS_ADDR_LINKLOCAL(&in6))
 		return;
 
-	netconfig_addr_wait_unregister(nc, true);
+	new_lla = !nc->have_lla;
+	nc->have_lla = true;
+
+	if (!(ifa->ifa_flags & IFA_F_TENTATIVE))
+		netconfig_addr_wait_unregister(nc, true);
+	else if (nc->ifaddr6_dump_cmd_id) {
+		struct l_netlink *rtnl = l_rtnl_get();
+		unsigned int cmd_id = nc->ifaddr6_dump_cmd_id;
+
+		nc->ifaddr6_dump_cmd_id = 0;
+		l_netlink_cancel(rtnl, cmd_id);
+	}
 
 	l_dhcp6_client_set_link_local_address(nc->dhcp6_client, ip);
-	l_icmp6_client_set_link_local_address(nc->icmp6_client, ip);
+	l_icmp6_client_set_link_local_address(nc->icmp6_client, ip,
+					!!(ifa->ifa_flags & IFA_F_OPTIMISTIC));
 
 	/*
 	 * Only now that we have a link-local address see if we can start
 	 * actual DHCPv6 setup.
 	 */
-	if (netconfig_check_start_dhcp6(nc))
+	if (new_lla && netconfig_check_start_dhcp6(nc))
 		return;
 
 	netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED);
@@ -2022,6 +2036,13 @@  configure_ipv6:
 	 * before we start the dump, instead of after it ends, to eliminate
 	 * the possibility of missing an RTM_NEWADDR between the end of
 	 * the dump command and registering for the events.
+	 *
+	 * We stay on that list until we receive a non-tentative LL address.
+	 * Note that we may set .have_lla earlier, specifically when we
+	 * receive a tentative LL address that is also optimistic.  We will
+	 * however stay on addr_wait_list because we want to notify
+	 * l_icmp6_client again when the LL address completes DAD and becomes
+	 * non-tentative.
 	 */
 	if (!addr_wait_list) {
 		addr_wait_list = l_queue_new();
@@ -2041,6 +2062,7 @@  configure_ipv6:
 		goto unregister;
 
 	l_queue_push_tail(addr_wait_list, netconfig);
+	netconfig->have_lla = false;
 
 	if (!l_net_get_mac_address(netconfig->ifindex, netconfig->mac))
 		goto unregister;