ell.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs
@ 2022-09-19 13:30 Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 2/7] icmp6: Parse RDNSS and DNSSL options Andrew Zaborowski
                   ` (5 more replies)
  0 siblings, 6 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2022-09-19 13:30 UTC (permalink / raw)
  To: ell

In struct l_icmp6_router save the lists of prefixes for autoconfiguration
separately from routes.  The two sets of prefixes may overlap completely,
partially or not at all.  Drop the preferred_lifetime information from
route_info since this is strictly to be used as the preferred lifetime
for addresses generated through SLAAC and has nothing to do with routes.
---
 ell/icmp6-private.h |  7 ++++
 ell/icmp6.c         | 98 +++++++++++++++++++++++++++++++--------------
 ell/netconfig.c     |  7 +---
 3 files changed, 77 insertions(+), 35 deletions(-)

diff --git a/ell/icmp6-private.h b/ell/icmp6-private.h
index 77df0b8..a26639d 100644
--- a/ell/icmp6-private.h
+++ b/ell/icmp6-private.h
@@ -25,6 +25,11 @@ struct route_info {
 	bool onlink : 1;
 	uint8_t prefix_len;
 	uint8_t preference;
+	uint32_t valid_lifetime;
+};
+
+struct autoconf_prefix_info {
+	uint8_t prefix[8];
 	uint32_t preferred_lifetime;
 	uint32_t valid_lifetime;
 };
@@ -40,6 +45,8 @@ struct l_icmp6_router {
 	uint32_t max_rtr_adv_interval_ms;
 	uint32_t n_routes;
 	struct route_info *routes;
+	uint32_t n_ac_prefixes;
+	struct autoconf_prefix_info *ac_prefixes;
 };
 
 struct l_icmp6_router *_icmp6_router_new();
diff --git a/ell/icmp6.c b/ell/icmp6.c
index 7319903..b71a98f 100644
--- a/ell/icmp6.c
+++ b/ell/icmp6.c
@@ -693,9 +693,58 @@ struct l_icmp6_router *_icmp6_router_new()
 void _icmp6_router_free(struct l_icmp6_router *r)
 {
 	l_free(r->routes);
+	l_free(r->ac_prefixes);
 	l_free(r);
 }
 
+/* Note: the following two write to @out even when they return false */
+static bool icmp6_prefix_parse_rt_info(const uint8_t *data,
+					struct route_info *out)
+{
+	out->prefix_len = data[2];
+	out->onlink = true;
+	out->preference = 0;
+	out->valid_lifetime = l_get_be32(data + 4);
+
+	/*
+	 * Only the initial Prefix Length bits of the prefix are valid.
+	 * The remaining bits "MUST" be ignored by the receiver.
+	 */
+	memcpy(out->address, net_prefix_from_ipv6(data + 16, out->prefix_len),
+		16);
+
+	if (out->prefix_len >= 10 && IN6_IS_ADDR_LINKLOCAL(out->address))
+		return false;
+
+	return true;
+}
+
+static bool icmp6_prefix_parse_ac_info(const uint8_t *data,
+					struct autoconf_prefix_info *out)
+{
+	/*
+	 * Per RFC4862 we need to silently ignore prefixes with a
+	 * preferred lifetime longer than valid lifetime, those with
+	 * 0 valid lifetime and those with link-local prefixes.
+	 * Prefix Length must be 8 bytes (IPv6 address - Interface ID).
+	 */
+	if (data[2] != 64)
+		return false;
+
+	if (IN6_IS_ADDR_LINKLOCAL(data + 16))
+		return false;
+
+	out->valid_lifetime = l_get_be32(data + 4);
+	out->preferred_lifetime = l_get_be32(data + 8);
+
+	if (out->valid_lifetime == 0 ||
+			out->preferred_lifetime > out->valid_lifetime)
+		return false;
+
+	memcpy(out->prefix, data + 16, 8);
+	return true;
+}
+
 struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 						size_t len,
 						const uint8_t src[static 16],
@@ -705,6 +754,7 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 	const uint8_t *opts;
 	uint32_t opts_len;
 	uint32_t n_routes = 0;
+	uint32_t n_ac_prefixes = 0;
 
 	if (ra->nd_ra_type != ND_ROUTER_ADVERT)
 		return NULL;
@@ -742,6 +792,10 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 
 			if (opts[3] & ND_OPT_PI_FLAG_ONLINK)
 				n_routes += 1;
+
+			if (opts[3] & ND_OPT_PI_FLAG_AUTO)
+				n_ac_prefixes += 1;
+
 			break;
 		case ND_OPT_ROUTE_INFORMATION:
 			if (l < 8)
@@ -781,6 +835,8 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 	memcpy(r->address, src, sizeof(r->address));
 	r->routes = l_new(struct route_info, n_routes);
 	r->n_routes = n_routes;
+	r->ac_prefixes = l_new(struct autoconf_prefix_info, n_ac_prefixes);
+	r->n_ac_prefixes = n_ac_prefixes;
 
 	if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
 		r->managed = true;
@@ -798,6 +854,7 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 	opts = (uint8_t *) (ra + 1);
 	opts_len = len - sizeof(struct nd_router_advert);
 	n_routes = 0;
+	n_ac_prefixes = 0;
 
 	while (opts_len) {
 		uint8_t t = opts[0];
@@ -814,41 +871,22 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 
 			break;
 		case ND_OPT_PREFIX_INFORMATION:
-		{
-			struct route_info *i = &r->routes[n_routes];
-
-			if (!(opts[3] & ND_OPT_PI_FLAG_ONLINK))
-				break;
+			if (opts[3] & ND_OPT_PI_FLAG_ONLINK) {
+				struct route_info *i = &r->routes[n_routes];
 
-			i->prefix_len = opts[2];
-			i->onlink = true;
-			i->valid_lifetime = l_get_be32(opts + 4);
-			i->preferred_lifetime = l_get_be32(opts + 8);
-
-			/*
-			 * Only the initial Prefix Length bits of the prefix
-			 * are valid.  The remaining bits "MUST" be ignored
-			 * by the receiver.
-			 */
-			memcpy(i->address, net_prefix_from_ipv6(opts + 16,
-							i->prefix_len), 16);
+				if (icmp6_prefix_parse_rt_info(opts, i))
+					n_routes++;
+			}
 
-			/*
-			 * For SLAAC (RFC4862) we need to "silently ignore"
-			 * routes with a preferred lifetime longer than valid
-			 * lifetime, and those with the link-local prefix.
-			 * Since it makes sense, do it regardless of SLAAC.
-			 */
-			if (i->preferred_lifetime > i->valid_lifetime)
-				break;
+			if (opts[3] & ND_OPT_PI_FLAG_AUTO) {
+				struct autoconf_prefix_info *i =
+					&r->ac_prefixes[n_ac_prefixes];
 
-			if (i->prefix_len >= 10 &&
-					IN6_IS_ADDR_LINKLOCAL(i->address))
-				break;
+				if (icmp6_prefix_parse_ac_info(opts, i))
+					n_ac_prefixes++;
+			}
 
-			n_routes += 1;
 			break;
-		}
 		case ND_OPT_RTR_ADV_INTERVAL:
 			if (l < 8)
 				break;
diff --git a/ell/netconfig.c b/ell/netconfig.c
index 29e95b7..bbe1dec 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -799,7 +799,6 @@ static bool netconfig_check_route_need_update(
 static void netconfig_set_icmp6_route_data(struct l_netconfig *nc,
 						struct netconfig_route_data *rd,
 						const struct l_icmp6_router *ra,
-						uint32_t preferred_lifetime,
 						uint32_t valid_lifetime,
 						uint32_t mtu, bool updated)
 {
@@ -916,10 +915,10 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
 		 * netconfig_set_icmp6_route_data.
 		 */
 		netconfig_set_icmp6_route_data(nc, default_rd, r, r->lifetime,
-						r->lifetime, r->mtu, false);
+						r->mtu, false);
 	} else if (default_rd && r->lifetime)
 		netconfig_set_icmp6_route_data(nc, default_rd, r, r->lifetime,
-						r->lifetime, r->mtu, true);
+						r->mtu, true);
 	else if (default_rd && !r->lifetime)
 		netconfig_remove_icmp6_route(nc, default_rd);
 
@@ -941,12 +940,10 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
 				continue;
 
 			netconfig_set_icmp6_route_data(nc, rd, r,
-						info->preferred_lifetime,
 						info->valid_lifetime,
 						gateway ? r->mtu : 0, false);
 		} else if (rd && info->valid_lifetime)
 			netconfig_set_icmp6_route_data(nc, rd, r,
-						info->preferred_lifetime,
 						info->valid_lifetime,
 						gateway ? r->mtu : 0, true);
 		else if (rd && !info->valid_lifetime)
-- 
2.34.1


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

* [PATCH 2/7] icmp6: Parse RDNSS and DNSSL options
  2022-09-19 13:30 [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs Andrew Zaborowski
@ 2022-09-19 13:31 ` Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 3/7] icmp6: Switch socket from AF_INET6 to AF_PACKET Andrew Zaborowski
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2022-09-19 13:31 UTC (permalink / raw)
  To: ell

Save the list of DNS server addresses and the local domain search list
from Router Advertisements in the l_icmp6_router structure.
---
 ell/icmp6-private.h | 14 +++++++
 ell/icmp6.c         | 94 ++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 106 insertions(+), 2 deletions(-)

diff --git a/ell/icmp6-private.h b/ell/icmp6-private.h
index a26639d..f207164 100644
--- a/ell/icmp6-private.h
+++ b/ell/icmp6-private.h
@@ -34,6 +34,16 @@ struct autoconf_prefix_info {
 	uint32_t valid_lifetime;
 };
 
+struct dns_info {
+	uint8_t address[16];
+	uint32_t lifetime;
+};
+
+struct domain_info {
+	char *domain;
+	uint32_t lifetime;
+};
+
 struct l_icmp6_router {
 	uint8_t address[16];
 	bool managed : 1;
@@ -47,6 +57,10 @@ struct l_icmp6_router {
 	struct route_info *routes;
 	uint32_t n_ac_prefixes;
 	struct autoconf_prefix_info *ac_prefixes;
+	uint32_t n_dns;
+	struct dns_info *dns_list;
+	uint32_t n_domains;
+	struct domain_info *domains;
 };
 
 struct l_icmp6_router *_icmp6_router_new();
diff --git a/ell/icmp6.c b/ell/icmp6.c
index b71a98f..c38df17 100644
--- a/ell/icmp6.c
+++ b/ell/icmp6.c
@@ -50,6 +50,7 @@
 #include "netlink.h"
 #include "rtnl.h"
 #include "missing.h"
+#include "utf8.h"
 #include "icmp6.h"
 #include "icmp6-private.h"
 
@@ -58,6 +59,14 @@
 #define ND_OPT_ROUTE_INFORMATION	24
 #endif
 
+/* RFC8106 */
+#ifndef ND_OPT_RECURSIVE_DNS_SERVER
+#define ND_OPT_RECURSIVE_DNS_SERVER	25
+#endif
+#ifndef ND_OPT_DNS_SEARCH_LIST
+#define ND_OPT_DNS_SEARCH_LIST		31
+#endif
+
 #define CLIENT_DEBUG(fmt, args...)					\
 	l_util_debug(client->debug_handler, client->debug_data,		\
 			"%s:%i " fmt, __func__, __LINE__, ## args)
@@ -755,6 +764,8 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 	uint32_t opts_len;
 	uint32_t n_routes = 0;
 	uint32_t n_ac_prefixes = 0;
+	uint32_t n_dns = 0;
+	uint32_t n_domains = 0;
 
 	if (ra->nd_ra_type != ND_ROUTER_ADVERT)
 		return NULL;
@@ -824,8 +835,48 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 				break;
 
 			n_routes += 1;
+			break;
+		case ND_OPT_RECURSIVE_DNS_SERVER:
+			if (l < 24 || (l & 15) != 8)
+				return NULL;
+
+			n_dns += (l - 8) / 16;
+			break;
+		case ND_OPT_DNS_SEARCH_LIST:
+		{
+			unsigned int n_labels;
+			unsigned int pos = 8;
+
+			if (l < 16)
+				return NULL;
+
+			/* Count domains according to RFC1035 Section 3.1 */
+			do {
+				unsigned int label_len;
+
+				n_labels = 0;
+
+				do {
+					label_len = opts[pos];
+					pos += 1 + label_len;
+					n_labels += label_len ? 1 : 0;
+				} while (label_len && pos < l);
+
+				/*
+				 * Check if the root label was missing, or
+				 * a label didn't fit in the option bytes, or
+				 * the first domain had 0 labels, i.e. there
+				 * were no domains.
+				 */
+				if (label_len || pos > l || pos == 9)
+					return NULL;
+
+				n_domains += n_labels ? 1 : 0;
+			} while (n_labels && pos < l);
+
 			break;
 		}
+		}
 
 		opts += l;
 		opts_len -= l;
@@ -834,9 +885,9 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 	r = _icmp6_router_new();
 	memcpy(r->address, src, sizeof(r->address));
 	r->routes = l_new(struct route_info, n_routes);
-	r->n_routes = n_routes;
 	r->ac_prefixes = l_new(struct autoconf_prefix_info, n_ac_prefixes);
-	r->n_ac_prefixes = n_ac_prefixes;
+	r->dns_list = l_new(struct dns_info, n_dns);
+	r->domains = l_new(struct domain_info, n_domains);
 
 	if (ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED)
 		r->managed = true;
@@ -855,6 +906,8 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 	opts_len = len - sizeof(struct nd_router_advert);
 	n_routes = 0;
 	n_ac_prefixes = 0;
+	n_dns = 0;
+	n_domains = 0;
 
 	while (opts_len) {
 		uint8_t t = opts[0];
@@ -946,12 +999,49 @@ struct l_icmp6_router *_icmp6_router_parse(const struct nd_router_advert *ra,
 			n_routes += 1;
 			break;
 		}
+		case ND_OPT_RECURSIVE_DNS_SERVER:
+		{
+			unsigned int pos;
+
+			for (pos = 8; pos < l; pos += 16) {
+				struct dns_info *i = &r->dns_list[n_dns++];
+
+				i->lifetime = l_get_be32(opts + 4);
+				memcpy(i->address, opts + pos, 16);
+			}
+
+			break;
+		}
+		case ND_OPT_DNS_SEARCH_LIST:
+		{
+			struct domain_info *info = &r->domains[n_domains];
+			_auto_(l_free) char **domain_list =
+				net_domain_list_parse(opts + 8, l - 8);
+			char **i;
+
+			/* Ignore invalid option */
+			if (!domain_list)
+				break;
+
+			for (i = domain_list; *i; i++) {
+				info->lifetime = l_get_be32(opts + 4);
+				info->domain = *i;
+				info++;
+				n_domains++;
+			}
+
+			break;
+		}
 		}
 
 		opts += l;
 		opts_len -= l;
 	}
 
+	r->n_routes = n_routes;
+	r->n_ac_prefixes = n_ac_prefixes;
+	r->n_dns = n_dns;
+	r->n_domains = n_domains;
 	return r;
 }
 
-- 
2.34.1


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

* [PATCH 3/7] icmp6: Switch socket from AF_INET6 to AF_PACKET
  2022-09-19 13:30 [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 2/7] icmp6: Parse RDNSS and DNSSL options Andrew Zaborowski
@ 2022-09-19 13:31 ` Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 4/7] rtnl: Add l_rtnl_address_get_in_addr Andrew Zaborowski
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2022-09-19 13:31 UTC (permalink / raw)
  To: ell

In order to be able to send our Router Solicitations before the
interface has a confirmed link-local address we have to be able to
supply our own IPv6 header and skip source address selection and routing
in the kernel.  It is at least tricky to do with an AF_INET6 raw socket
as some socket features, like SO_DONTROUTE/MSG_DONTROUTE, are not
supported and others don't suppress the source address selection/checks.
Additionally since we need to use the IPv6 unspecified address, which is
represented as all 0 bytes, some syscalls interpret it as no value
supplied.

Build/parse the IPv6 header on our own and send/receive using an
AF_PACKET socket.

Once we have the link-local address, we switch to sending from that
address.  Add l_icmp6_client_set_link_local_address for the user to give
it to us as they're likely to need it for DHCPv6.
---
 ell/dhcp6.c     |   3 +
 ell/ell.sym     |   1 +
 ell/icmp6.c     | 324 ++++++++++++++++++++++++++++++------------------
 ell/icmp6.h     |   2 +
 ell/netconfig.c |   1 +
 5 files changed, 213 insertions(+), 118 deletions(-)

diff --git a/ell/dhcp6.c b/ell/dhcp6.c
index 01d7eb3..15b451d 100644
--- a/ell/dhcp6.c
+++ b/ell/dhcp6.c
@@ -1619,6 +1619,9 @@ LIB_EXPORT bool l_dhcp6_client_set_link_local_address(
 	if (inet_pton(AF_INET6, ll, &client->ll_address) != 1)
 		return false;
 
+	if (!client->nora)
+		l_icmp6_client_set_link_local_address(client->icmp6, ll);
+
 	return true;
 }
 
diff --git a/ell/ell.sym b/ell/ell.sym
index ed66352..a28eb55 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -703,6 +703,7 @@ global:
 	l_icmp6_client_set_nodelay;
 	l_icmp6_client_set_rtnl;
 	l_icmp6_client_set_route_priority;
+	l_icmp6_client_set_link_local_address;
 	l_icmp6_router_get_address;
 	l_icmp6_router_get_managed;
 	l_icmp6_router_get_other;
diff --git a/ell/icmp6.c b/ell/icmp6.c
index c38df17..a2765ea 100644
--- a/ell/icmp6.c
+++ b/ell/icmp6.c
@@ -24,6 +24,7 @@
 #include <config.h>
 #endif
 
+#define _GNU_SOURCE
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
@@ -31,12 +32,16 @@
 #include <netinet/icmp6.h>
 #include <linux/ipv6.h>
 #include <linux/rtnetlink.h>
+#include <linux/if_packet.h>
+#include <linux/filter.h>
 #include <sys/types.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
+#include <net/ethernet.h>
 #include <unistd.h>
 #include <errno.h>
 #include <sys/time.h>
+#include <stddef.h>
 
 #include "private.h"
 #include "useful.h"
@@ -75,135 +80,186 @@
 			{ { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1 } } }
 #define IN6ADDR_LINKLOCAL_ALLROUTERS_INIT \
 			{ { { 0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2 } } }
+#define LLADDR_LINKLOCAL_ALLNODES_INIT	\
+			{ 0x33,0x33,0,0,0,1 }
+#define LLADDR_LINKLOCAL_ALLROUTERS_INIT \
+			{ 0x33,0x33,0,0,0,2 }
 
-static const struct in6_addr in6addr_linklocal_allnodes_init =
-					IN6ADDR_LINKLOCAL_ALLNODES_INIT;
-
-static int add_mreq(int s, int ifindex, const struct in6_addr *mc_addr)
-{
-	struct ipv6_mreq mreq = {
-		.ipv6mr_interface = ifindex,
-		.ipv6mr_multiaddr = *mc_addr,
-	};
-
-	return setsockopt(s, IPPROTO_IPV6,
-				IPV6_JOIN_GROUP, &mreq, sizeof(mreq));
-}
-
-static int icmp6_open_router_common(const struct icmp6_filter *filter,
-					int ifindex)
+static int icmp6_open_router_solicitation(int ifindex)
 {
 	int s;
-	int r;
-	int yes = 1;
-	int no = 0;
-	int nhops = 255;
+	struct sockaddr_ll addr;
+	struct sock_filter filter[] = {
+		/* A <- packet length */
+		BPF_STMT(BPF_LD | BPF_W | BPF_LEN, 0),
+		/* A >= sizeof(nd_router_advert) ? */
+		BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K, sizeof(struct ip6_hdr) +
+				sizeof(struct nd_router_advert), 1, 0),
+		/* ignore */
+		BPF_STMT(BPF_RET | BPF_K, 0),
+		/* A <- IP version + Traffic class */
+		BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 0),
+		/* A <- A & 0xf0 (Mask off version) */
+		BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xf0),
+		/* A == IPv6 ? */
+		BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 6 << 4, 1, 0),
+		/* ignore */
+		BPF_STMT(BPF_RET | BPF_K, 0),
+		/* A <- Next Header */
+		BPF_STMT(BPF_LD | BPF_B | BPF_ABS,
+				offsetof(struct ip6_hdr, ip6_nxt)),
+		/* A == ICMPv6 ? */
+		BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, IPPROTO_ICMPV6, 1, 0),
+		/* ignore */
+		BPF_STMT(BPF_RET | BPF_K, 0),
+		/* A <- ICMPv6 Type */
+		BPF_STMT(BPF_LD | BPF_B | BPF_ABS, sizeof(struct ip6_hdr) +
+				offsetof(struct icmp6_hdr, icmp6_type)),
+		/* A == Router Advertisement ? */
+		BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, ND_ROUTER_ADVERT, 1, 0),
+		/* ignore */
+		BPF_STMT(BPF_RET | BPF_K, 0),
+		/* A <- Payload Length */
+		BPF_STMT(BPF_LD | BPF_H | BPF_ABS,
+				offsetof(struct ip6_hdr, ip6_plen)),
+		/* A >= sizeof(nd_router_advert) ? */
+		BPF_JUMP(BPF_JMP | BPF_JGE | BPF_K,
+				sizeof(struct nd_router_advert), 1, 0),
+		/* ignore */
+		BPF_STMT(BPF_RET | BPF_K, 0),
+		/* return all */
+		BPF_STMT(BPF_RET | BPF_K, 65535),
+	};
+	const struct sock_fprog fprog = {
+		.len = L_ARRAY_SIZE(filter),
+		.filter = filter
+	};
+	int one = 1;
 
-	s = socket(AF_INET6, SOCK_RAW | SOCK_CLOEXEC, IPPROTO_ICMPV6);
+	s = socket(AF_PACKET, SOCK_DGRAM | SOCK_CLOEXEC, htons(ETH_P_IPV6));
 	if (s < 0)
 		return -errno;
 
-	r = setsockopt(s, IPPROTO_ICMPV6,
-			ICMP6_FILTER, filter, sizeof(struct icmp6_filter));
-	if (r < 0)
-		goto fail;
-
-	r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &no, sizeof(no));
-	if (r < 0)
-		goto fail;
-
-	r = setsockopt(s, IPPROTO_IPV6,
-				IPV6_MULTICAST_IF, &ifindex, sizeof(ifindex));
-	if (r < 0)
-		goto fail;
+	if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
+						&fprog, sizeof(fprog)) < 0)
+		goto error;
 
-	r = setsockopt(s, IPPROTO_IPV6,
-				IPV6_RECVHOPLIMIT, &yes, sizeof(yes));
-	if (r < 0)
-		goto fail;
+	if (setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &one, sizeof(one)) < 0)
+		goto error;
 
-	r = setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
-							&nhops, sizeof(nhops));
-	if (r < 0)
-		goto fail;
+	memset(&addr, 0, sizeof(addr));
+	addr.sll_family = AF_PACKET;
+	addr.sll_protocol = htons(ETH_P_IPV6);
+	addr.sll_ifindex = ifindex;
 
-	r = setsockopt(s, SOL_SOCKET, SO_BINDTOIFINDEX,
-						&ifindex, sizeof(ifindex));
-	if (r < 0 && errno == ENOPROTOOPT) {
-		struct ifreq ifr = {
-			.ifr_ifindex = ifindex,
-		};
-
-		r = ioctl(s, SIOCGIFNAME, &ifr);
-		if (r < 0)
-			goto fail;
-
-		r = setsockopt(s, SOL_SOCKET, SO_BINDTODEVICE,
-				ifr.ifr_name, strlen(ifr.ifr_name) + 1);
-	}
-
-	if (r < 0)
-		goto fail;
-
-	r = setsockopt(s, SOL_SOCKET, SO_TIMESTAMP, &yes, sizeof(yes));
-	if (r < 0)
-		goto fail;
+	if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+		goto error;
 
 	return s;
 
-fail:
-	close(s);
+error:
+	L_TFR(close(s));
 	return -errno;
 }
 
-static int icmp6_open_router_solicitation(int ifindex)
+static uint16_t icmp6_checksum(const struct iovec *iov, unsigned int iov_len)
 {
-	struct icmp6_filter filter;
-	int s;
-	int r;
+	const struct ip6_hdr *ip_hdr = iov[0].iov_base;
+	uint32_t sum = 0;
+	const uint16_t *ptr;
+	const uint16_t *buf_end;
+	/* Skip the real IPv6 header */
+	unsigned int buf_offset = sizeof(struct ip6_hdr);
 
-	ICMP6_FILTER_SETBLOCKALL(&filter);
-	ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filter);
+	/*
+	 * ICMPv6 checksum according to RFC 4443 Section 2.3, this includes
+	 * the IPv6 payload + the IPv6 pseudo-header according to RFC 2460
+	 * Section 8.1, i.e. the two IPv6 addresses + the payload length +
+	 * the header type.  The caller must ensure that the IPv6 header is
+	 * all in one buffer and that all buffer starts and lengths are
+	 * 16-bit-aligned.
+	 *
+	 * We can skip all zero words such as the upper 16 bits of the
+	 * payload length.  No need to byteswap as the carry bits from
+	 * either byte (high or low) accumulate in the other byte in
+	 * exactly the same way.
+	 */
+	buf_end = (void *) &ip_hdr->ip6_src + 32;
+	for (ptr = (void *) &ip_hdr->ip6_src; ptr < buf_end; )
+		sum += *ptr++;
 
-	s = icmp6_open_router_common(&filter, ifindex);
-	if (s < 0)
-		return s;
+	sum += ip_hdr->ip6_plen + htons(ip_hdr->ip6_nxt);
 
-	r = add_mreq(s, ifindex, &in6addr_linklocal_allnodes_init);
-	if (r < 0) {
-		close(s);
-		return -errno;
+	for (; iov_len; iov++, iov_len--) {
+		buf_end = iov->iov_base + iov->iov_len;
+		for (ptr = iov->iov_base + buf_offset; ptr < buf_end; )
+			sum += *ptr++;
+
+		buf_offset = 0;
 	}
 
-	return s;
+	while (sum >> 16)
+		sum = (sum & 0xffff) + (sum >> 16);
+
+	return ~sum;
 }
 
-static int icmp6_send_router_solicitation(int s, const uint8_t mac[static 6])
+static int icmp6_send_router_solicitation(int s, int ifindex,
+					const uint8_t src_mac[static 6],
+					const struct in6_addr *src_ip)
 {
-	struct sockaddr_in6 dst = {
-		.sin6_family = AF_INET6,
-		.sin6_addr = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
-	};
 	struct nd_router_solicit rs = {
 		.nd_rs_type = ND_ROUTER_SOLICIT,
+		.nd_rs_code = 0,
 	};
-	struct nd_opt_hdr rs_opt = {
+	struct nd_opt_hdr rs_sllao = {
 		.nd_opt_type = ND_OPT_SOURCE_LINKADDR,
 		.nd_opt_len = 1,
 	};
-	struct iovec iov[3] = {
+	const size_t rs_sllao_size = sizeof(rs_sllao) + 6;
+	struct ip6_hdr ip_hdr = {
+		.ip6_flow = htonl(6 << 28),
+		.ip6_hops = 255,
+		.ip6_nxt = IPPROTO_ICMPV6,
+		.ip6_plen = htons(sizeof(rs) + rs_sllao_size),
+		.ip6_dst = IN6ADDR_LINKLOCAL_ALLROUTERS_INIT,
+	};
+	struct iovec iov[4] = {
+		{ .iov_base = &ip_hdr, .iov_len = sizeof(ip_hdr) },
 		{ .iov_base = &rs, .iov_len = sizeof(rs) },
-		{ .iov_base = &rs_opt, .iov_len = sizeof(rs_opt) },
-		{ .iov_base = (void *) mac, .iov_len = 6 } };
-
+		{ .iov_base = &rs_sllao, .iov_len = sizeof(rs_sllao) },
+		{ .iov_base = (void *) src_mac, .iov_len = 6 } };
+
+	struct sockaddr_ll dst = {
+		.sll_family = AF_PACKET,
+		.sll_protocol = htons(ETH_P_IPV6),
+		.sll_ifindex = ifindex,
+		.sll_addr = LLADDR_LINKLOCAL_ALLROUTERS_INIT,
+		.sll_halen = 6,
+	};
 	struct msghdr msg = {
 		.msg_name = &dst,
 		.msg_namelen = sizeof(dst),
 		.msg_iov = iov,
-		.msg_iovlen = 3,
+		.msg_iovlen = L_ARRAY_SIZE(iov),
 	};
 	int r;
 
+	memcpy(&ip_hdr.ip6_src, src_ip, 16);
+
+	if (l_memeqzero(src_ip, 16)) {
+		/*
+		 * radvd will 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);
+	}
+
+	/* Don't byteswap the checksum */
+	rs.nd_rs_cksum = icmp6_checksum(msg.msg_iov, msg.msg_iovlen);
+
 	r = sendmsg(s, &msg, 0);
 	if (r < 0)
 		return -errno;
@@ -211,22 +267,23 @@ static int icmp6_send_router_solicitation(int s, const uint8_t mac[static 6])
 	return 0;
 }
 
-static int icmp6_receive(int s, void *buf, size_t buf_len, struct in6_addr *src,
-				uint64_t *out_timestamp)
+static int icmp6_receive(int s, void *buf, ssize_t *buf_len,
+				struct in6_addr *src, uint64_t *out_timestamp)
 {
 	char c_msg_buf[CMSG_SPACE(sizeof(int)) +
 			CMSG_SPACE(sizeof(struct timeval))];
-	struct iovec iov = {
-		.iov_base = buf,
-		.iov_len = buf_len,
+	struct ip6_hdr ip_hdr;
+	struct iovec iov[2] = {
+		{ .iov_base = &ip_hdr, .iov_len = sizeof(ip_hdr) },
+		{ .iov_base = buf, .iov_len = *buf_len - sizeof(ip_hdr) },
 	};
-	struct sockaddr_in6 saddr;
+	struct sockaddr_ll saddr;
 	struct msghdr msg = {
 		.msg_name = (void *)&saddr,
-		.msg_namelen = sizeof(struct sockaddr_in6),
+		.msg_namelen = sizeof(struct sockaddr_ll),
 		.msg_flags = 0,
-		.msg_iov = &iov,
-		.msg_iovlen = 1,
+		.msg_iov = iov,
+		.msg_iovlen = L_ARRAY_SIZE(iov),
 		.msg_control = c_msg_buf,
 		.msg_controllen = sizeof(c_msg_buf),
 	};
@@ -238,22 +295,30 @@ static int icmp6_receive(int s, void *buf, size_t buf_len, struct in6_addr *src,
 	if (l < 0)
 		return -errno;
 
-	if ((size_t) l != buf_len)
+	if (l != *buf_len)
 		return -EINVAL;
 
-	if (msg.msg_namelen != sizeof(struct sockaddr_in6) ||
-			saddr.sin6_family != AF_INET6)
-		return -EPFNOSUPPORT;
+	if (ntohs(ip_hdr.ip6_plen) > iov[1].iov_len)
+		return -EMSGSIZE;
+
+	iov[1].iov_len = ntohs(ip_hdr.ip6_plen);
+
+	/*
+	 * Unlikely but align length for icmp6_checksum().  We know we have
+	 * at least sizeof(struct ip6_hdr) extra bytes in buf so we can
+	 * append this 0 byte no problem.
+	 */
+	if (iov[1].iov_len & 1)
+		((uint8_t *) buf)[iov[1].iov_len++] = 0x00;
+
+	if (icmp6_checksum(iov, L_ARRAY_SIZE(iov)))
+		return -EBADMSG;
+
+	if (ip_hdr.ip6_hops != 255)
+		return -EMULTIHOP;
 
 	for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) {
-		if (cmsg->cmsg_level == SOL_IPV6 &&
-				cmsg->cmsg_type == IPV6_HOPLIMIT &&
-				cmsg->cmsg_len == CMSG_LEN(sizeof(int))) {
-			int hops = l_get_u32(CMSG_DATA(cmsg));
-
-			if (hops != 255)
-				return -EMULTIHOP;
-		} else if (cmsg->cmsg_level == SOL_SOCKET &&
+		if (cmsg->cmsg_level == SOL_SOCKET &&
 				cmsg->cmsg_type == SCM_TIMESTAMP &&
 				cmsg->cmsg_len ==
 				CMSG_LEN(sizeof(struct timeval))) {
@@ -263,7 +328,8 @@ static int icmp6_receive(int s, void *buf, size_t buf_len, struct in6_addr *src,
 		}
 	}
 
-	memcpy(src, &saddr.sin6_addr, sizeof(saddr.sin6_addr));
+	*buf_len = ntohs(ip_hdr.ip6_plen);
+	memcpy(src, &ip_hdr.ip6_src, 16);
 	*out_timestamp = timestamp ?: l_time_now();
 	return 0;
 }
@@ -280,6 +346,7 @@ struct l_icmp6_client {
 	struct l_timeout *timeout_send;
 	uint64_t retransmit_time;
 	struct l_io *io;
+	struct in6_addr src_ip;
 
 	struct l_icmp6_router *ra;
 	struct l_netlink *rtnl;
@@ -432,12 +499,16 @@ static bool icmp6_client_read_handler(struct l_io *io, void *userdata)
 		return false;
 	}
 
-	ra = l_malloc(l);
-	if (icmp6_receive(s, ra, l, &src, &timestamp) < 0)
-		goto done;
+	if ((size_t) l < sizeof(struct ip6_hdr) +
+			sizeof(struct nd_router_advert)) {
+		CLIENT_DEBUG("Message too small - ignore");
+		return true;
+	}
 
-	if ((size_t) l < sizeof(struct nd_router_advert)) {
-		CLIENT_DEBUG("Message to small - ignore");
+	ra = l_malloc(l);
+	r = icmp6_receive(s, ra, &l, &src, &timestamp);
+	if (r < 0) {
+		CLIENT_DEBUG("icmp6_receive(): %s (%i)", strerror(-r), -r);
 		goto done;
 	}
 
@@ -475,7 +546,8 @@ static void icmp6_client_timeout_send(struct l_timeout *timeout,
 						SOLICITATION_INTERVAL);
 
 	r = icmp6_send_router_solicitation(l_io_get_fd(client->io),
-								client->mac);
+						client->ifindex, client->mac,
+						&client->src_ip);
 	if (r < 0) {
 		CLIENT_DEBUG("Error sending Router Solicitation: %s",
 				strerror(-r));
@@ -692,6 +764,22 @@ LIB_EXPORT bool l_icmp6_client_set_route_priority(
 	return true;
 }
 
+LIB_EXPORT bool l_icmp6_client_set_link_local_address(
+						struct l_icmp6_client *client,
+						const char *ll)
+{
+	if (unlikely(!client))
+		return false;
+
+	/*
+	 * client->src_ip is all 0s initially which results in our Router
+	 * Solicitations being sent from the IPv6 Unspecified Address, which
+	 * 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;
+}
+
 struct l_icmp6_router *_icmp6_router_new()
 {
 	struct l_icmp6_router *r = l_new(struct l_icmp6_router, 1);
diff --git a/ell/icmp6.h b/ell/icmp6.h
index 615dba4..ffbb8a8 100644
--- a/ell/icmp6.h
+++ b/ell/icmp6.h
@@ -65,6 +65,8 @@ bool l_icmp6_client_set_rtnl(struct l_icmp6_client *client,
 						struct l_netlink *rtnl);
 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);
 
 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 bbe1dec..72c67a9 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -1532,6 +1532,7 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
 	netconfig_addr_wait_unregister(nc, true);
 
 	l_dhcp6_client_set_link_local_address(nc->dhcp6_client, ip);
+	l_icmp6_client_set_link_local_address(nc->icmp6_client, ip);
 
 	/*
 	 * Only now that we have a link-local address start actual DHCPv6
-- 
2.34.1


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

* [PATCH 4/7] rtnl: Add l_rtnl_address_get_in_addr
  2022-09-19 13:30 [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 2/7] icmp6: Parse RDNSS and DNSSL options Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 3/7] icmp6: Switch socket from AF_INET6 to AF_PACKET Andrew Zaborowski
@ 2022-09-19 13:31 ` Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 5/7] netconfig: Decouple icmp6 start from dhcp6 start Andrew Zaborowski
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2022-09-19 13:31 UTC (permalink / raw)
  To: ell

Similar to l_rtnl_route_get_dst_in_addr add a getter for the raw address
value, avoiding extra string conversions.
---
 ell/ell.sym | 1 +
 ell/rtnl.c  | 9 +++++++++
 ell/rtnl.h  | 1 +
 3 files changed, 11 insertions(+)

diff --git a/ell/ell.sym b/ell/ell.sym
index a28eb55..d76b2ea 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -626,6 +626,7 @@ global:
 	l_rtnl_address_clone;
 	l_rtnl_address_free;
 	l_rtnl_address_get_address;
+	l_rtnl_address_get_in_addr;
 	l_rtnl_address_get_family;
 	l_rtnl_address_get_prefix_length;
 	l_rtnl_address_get_broadcast;
diff --git a/ell/rtnl.c b/ell/rtnl.c
index 2483e82..5909c4b 100644
--- a/ell/rtnl.c
+++ b/ell/rtnl.c
@@ -180,6 +180,15 @@ LIB_EXPORT bool l_rtnl_address_get_address(const struct l_rtnl_address *addr,
 						out_buf);
 }
 
+LIB_EXPORT const void *l_rtnl_address_get_in_addr(
+					const struct l_rtnl_address *addr)
+{
+	if (unlikely(!addr))
+		return NULL;
+
+	return addr->family == AF_INET ? (void *) &addr->in_addr : &addr->in6_addr;
+}
+
 LIB_EXPORT uint8_t l_rtnl_address_get_family(const struct l_rtnl_address *addr)
 {
 	if (unlikely(!addr))
diff --git a/ell/rtnl.h b/ell/rtnl.h
index ffd1d73..1e6b1fa 100644
--- a/ell/rtnl.h
+++ b/ell/rtnl.h
@@ -44,6 +44,7 @@ void l_rtnl_address_free(struct l_rtnl_address *addr);
 DEFINE_CLEANUP_FUNC(l_rtnl_address_free);
 bool l_rtnl_address_get_address(const struct l_rtnl_address *addr,
 				char *out_buf);
+const void *l_rtnl_address_get_in_addr(const struct l_rtnl_address *addr);
 uint8_t l_rtnl_address_get_family(const struct l_rtnl_address *addr);
 uint8_t l_rtnl_address_get_prefix_length(const struct l_rtnl_address *addr);
 bool l_rtnl_address_get_broadcast(const struct l_rtnl_address *addr,
-- 
2.34.1


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

* [PATCH 5/7] netconfig: Decouple icmp6 start from dhcp6 start
  2022-09-19 13:30 [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs Andrew Zaborowski
                   ` (2 preceding siblings ...)
  2022-09-19 13:31 ` [PATCH 4/7] rtnl: Add l_rtnl_address_get_in_addr Andrew Zaborowski
@ 2022-09-19 13:31 ` Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 6/7] netconfig: Create SLAAC address Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 7/7] netconfig: Control optimistic DAD Andrew Zaborowski
  5 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2022-09-19 13:31 UTC (permalink / raw)
  To: ell

In preparation for supporting generating SLAAC addresses, start the
l_icmp6_client directly from the netconfig state machine.
l_dhcp6_client still takes care of creating and destroying
l_icmp6_client because it needs it for its own public API, but netconfig
starts/stops/handles events locally.  This also allows a slight
optimization described in RFC4862 where if we're lucky, we will
send the Router Solicitation simultaneously with the Neighbor
Solicitation for the link-local addresses so as to avoid waiting the sum
of the RA response time + DAD timeout and instead wait the longer of the
two periods.
---
 ell/dhcp6.c     |   3 ++
 ell/netconfig.c | 102 ++++++++++++++++++++++++++++++++++++++++++------
 2 files changed, 93 insertions(+), 12 deletions(-)

diff --git a/ell/dhcp6.c b/ell/dhcp6.c
index 15b451d..e234eb0 100644
--- a/ell/dhcp6.c
+++ b/ell/dhcp6.c
@@ -1473,6 +1473,9 @@ static void dhcp6_client_icmp6_event(struct l_icmp6_client *icmp6,
 {
 	struct l_dhcp6_client *client = user_data;
 
+	if (client->nora)
+		return;
+
 	switch (event) {
 	case L_ICMP6_CLIENT_EVENT_ROUTER_FOUND:
 	{
diff --git a/ell/netconfig.c b/ell/netconfig.c
index 72c67a9..3ac0319 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -24,12 +24,13 @@
 #include <config.h>
 #endif
 
+#include <net/if.h>
 #include <linux/types.h>
 #include <linux/if_ether.h>
+#include <linux/if_arp.h>
 #include <netinet/ip.h>
 #include <arpa/inet.h>
 #include <netinet/icmp6.h>
-#include <net/if.h>
 #include <errno.h>
 #include <sys/types.h>
 #include <sys/stat.h>
@@ -56,6 +57,7 @@
 #include "net.h"
 #include "net-private.h"
 #include "acd.h"
+#include "timeout.h"
 #include "netconfig.h"
 
 struct l_netconfig {
@@ -87,6 +89,8 @@ struct l_netconfig {
 	struct l_queue *icmp_route_data;
 	struct l_acd *acd;
 	unsigned int orig_disable_ipv6;
+	uint8_t mac[ETH_ALEN];
+	struct l_timeout *ra_timeout;
 
 	/* These objects, if not NULL, are owned by @addresses and @routes */
 	struct l_rtnl_address *v4_address;
@@ -665,6 +669,35 @@ static void netconfig_dhcp6_event_handler(struct l_dhcp6_client *client,
 	}
 }
 
+static bool netconfig_match(const void *a, const void *b)
+{
+	return a == b;
+}
+
+static bool netconfig_check_start_dhcp6(struct l_netconfig *nc)
+{
+	/* Don't start DHCPv6 until we get an RA with the managed bit set */
+	if (nc->ra_timeout)
+		return true;
+
+	/* Don't start DHCPv6 while waiting for the link-local address */
+	if (l_queue_find(addr_wait_list, netconfig_match, nc))
+		return true;
+
+	return l_dhcp6_client_start(nc->dhcp6_client);
+}
+
+static void netconfig_ra_timeout_cb(struct l_timeout *timeout, void *user_data)
+{
+	struct l_netconfig *nc = user_data;
+
+	l_timeout_remove(l_steal_ptr(nc->ra_timeout));
+
+	/* No Router Advertisements received, assume no DHCPv6 or SLAAC */
+	l_icmp6_client_stop(nc->icmp6_client);
+	netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED);
+}
+
 static uint64_t now;
 
 static bool netconfig_check_route_expired(void *data, void *user_data)
@@ -883,23 +916,23 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
 	const struct l_icmp6_router *r;
 	struct netconfig_route_data *default_rd;
 	unsigned int i;
+	bool first_ra = false;
 
 	if (event != L_ICMP6_CLIENT_EVENT_ROUTER_FOUND)
 		return;
 
 	r = event_data;
 
-	/*
-	 * Note: If this is the first RA received, the l_dhcp6_client
-	 * will have received the event before us and will be acting
-	 * on it by now.
-	 */
-
-	if (nc->v6_gateway_override)
-		return;
+	if (nc->ra_timeout) {
+		first_ra = true;
+		l_timeout_remove(l_steal_ptr(nc->ra_timeout));
+	}
 
 	netconfig_expire_routes(nc);
 
+	if (nc->v6_gateway_override)
+		goto process_nondefault_routes;
+
 	/* Process the default gateway information */
 	default_rd = netconfig_find_icmp6_route(nc, r->address, NULL);
 
@@ -922,6 +955,7 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
 	else if (default_rd && !r->lifetime)
 		netconfig_remove_icmp6_route(nc, default_rd);
 
+process_nondefault_routes:
 	/*
 	 * Process the onlink and offlink routes, from the Router
 	 * Advertisement's Prefix Information options and Route
@@ -950,6 +984,16 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
 			netconfig_remove_icmp6_route(nc, rd);
 	}
 
+	/* See if we should start DHCPv6 now */
+	if (first_ra) {
+		if (!l_icmp6_router_get_managed(r) ||
+				!netconfig_check_start_dhcp6(nc)) {
+			netconfig_emit_event(nc, AF_INET6,
+						L_NETCONFIG_EVENT_FAILED);
+			return;
+		}
+	}
+
 	/*
 	 * Note: we may be emitting this before L_NETCONFIG_EVENT_CONFIGURE.
 	 * We should probably instead save the affected routes in separate
@@ -1046,6 +1090,7 @@ LIB_EXPORT struct l_netconfig *l_netconfig_new(uint32_t ifindex)
 					nc, NULL);
 
 	nc->dhcp6_client = l_dhcp6_client_new(ifindex);
+	l_dhcp6_client_set_nora(nc->dhcp6_client, true);
 	l_dhcp6_client_set_event_handler(nc->dhcp6_client,
 					netconfig_dhcp6_event_handler,
 					nc, NULL);
@@ -1535,10 +1580,10 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
 	l_icmp6_client_set_link_local_address(nc->icmp6_client, ip);
 
 	/*
-	 * Only now that we have a link-local address start actual DHCPv6
-	 * setup.
+	 * Only now that we have a link-local address see if we can start
+	 * actual DHCPv6 setup.
 	 */
-	if (l_dhcp6_client_start(nc->dhcp6_client))
+	if (netconfig_check_start_dhcp6(nc))
 		return;
 
 	netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED);
@@ -1687,6 +1732,35 @@ configure_ipv6:
 
 	l_queue_push_tail(addr_wait_list, netconfig);
 
+	if (!l_net_get_mac_address(netconfig->ifindex, netconfig->mac))
+		goto unregister;
+
+	l_dhcp6_client_set_address(netconfig->dhcp6_client, ARPHRD_ETHER,
+					netconfig->mac, ETH_ALEN);
+	l_icmp6_client_set_address(netconfig->icmp6_client, netconfig->mac);
+
+	/*
+	 * RFC4862 Section 4: "To speed the autoconfiguration process, a host
+	 * may generate its link-local address (and verify its uniqueness) in
+	 * parallel with waiting for a Router Advertisement.  Because a router
+	 * may delay responding to a Router Solicitation for a few seconds,
+	 * the total time needed to complete autoconfiguration can be
+	 * significantly longer if the two steps are done serially."
+	 *
+	 * We don't know whether we have the LL address yet.  The interface
+	 * may have been just brought up and DAD may still running or the LL
+	 * address may have been deleted and won't be added until
+	 * netconfig_ifaddr_ipv6_dump_done_cb() writes the /proc settings.
+	 * In any case the Router Solicitation doesn't depend on having the
+	 * LL address so send it now.  We won't start DHCPv6 however until we
+	 * have both the LL address and the Router Advertisement.
+	 */
+	if (!l_icmp6_client_start(netconfig->icmp6_client))
+		goto unregister;
+
+	netconfig->ra_timeout = l_timeout_create(10, netconfig_ra_timeout_cb,
+							netconfig, NULL);
+
 done:
 	netconfig->started = true;
 	return true;
@@ -1717,6 +1791,9 @@ LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig)
 	if (netconfig->signal_expired_work)
 		l_idle_remove(l_steal_ptr(netconfig->signal_expired_work));
 
+	if (netconfig->ra_timeout)
+		l_timeout_remove(l_steal_ptr(netconfig->ra_timeout));
+
 	netconfig_addr_wait_unregister(netconfig, false);
 
 	netconfig_update_cleanup(netconfig);
@@ -1734,6 +1811,7 @@ LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig)
 
 	l_dhcp_client_stop(netconfig->dhcp_client);
 	l_dhcp6_client_stop(netconfig->dhcp6_client);
+	l_icmp6_client_stop(netconfig->icmp6_client);
 
 	l_acd_destroy(l_steal_ptr(netconfig->acd));
 
-- 
2.34.1


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

* [PATCH 6/7] netconfig: Create SLAAC address
  2022-09-19 13:30 [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs Andrew Zaborowski
                   ` (3 preceding siblings ...)
  2022-09-19 13:31 ` [PATCH 5/7] netconfig: Decouple icmp6 start from dhcp6 start Andrew Zaborowski
@ 2022-09-19 13:31 ` Andrew Zaborowski
  2022-09-19 13:31 ` [PATCH 7/7] netconfig: Control optimistic DAD Andrew Zaborowski
  5 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2022-09-19 13:31 UTC (permalink / raw)
  To: ell

If the Router Advertisement doesn't indicate DHCPv6 is available and
includes prefixes for address auto-configuration, fall back to using
those to generate a single IPv6 address.  Other settings such as DNS
are not supported in this mode yet and there's no renewal timer for when
the address lifetime is finite.
---
 ell/netconfig.c | 184 +++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 174 insertions(+), 10 deletions(-)

diff --git a/ell/netconfig.c b/ell/netconfig.c
index 3ac0319..3336b30 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -91,6 +91,11 @@ struct l_netconfig {
 	unsigned int orig_disable_ipv6;
 	uint8_t mac[ETH_ALEN];
 	struct l_timeout *ra_timeout;
+	enum {
+		NETCONFIG_V6_METHOD_UNSET,
+		NETCONFIG_V6_METHOD_DHCP,
+		NETCONFIG_V6_METHOD_SLAAC,
+	} v6_auto_method;
 
 	/* These objects, if not NULL, are owned by @addresses and @routes */
 	struct l_rtnl_address *v4_address;
@@ -677,7 +682,7 @@ static bool netconfig_match(const void *a, const void *b)
 static bool netconfig_check_start_dhcp6(struct l_netconfig *nc)
 {
 	/* Don't start DHCPv6 until we get an RA with the managed bit set */
-	if (nc->ra_timeout)
+	if (nc->ra_timeout || nc->v6_auto_method != NETCONFIG_V6_METHOD_DHCP)
 		return true;
 
 	/* Don't start DHCPv6 while waiting for the link-local address */
@@ -698,6 +703,109 @@ static void netconfig_ra_timeout_cb(struct l_timeout *timeout, void *user_data)
 	netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED);
 }
 
+static void netconfig_add_slaac_address(struct l_netconfig *nc,
+					const struct l_icmp6_router *r)
+{
+	unsigned int i;
+	const struct autoconf_prefix_info *longest = &r->ac_prefixes[0];
+	uint8_t addr[16];
+	char addr_str[INET6_ADDRSTRLEN];
+	uint32_t p, v;
+
+	/* Find the autoconfiguration prefix that offers the longest lifetime */
+	for (i = 1; i < r->n_ac_prefixes; i++)
+		if (r->ac_prefixes[i].preferred_lifetime >
+				longest->preferred_lifetime)
+			longest = &r->ac_prefixes[i];
+
+	memcpy(addr, longest->prefix, 8);
+	/* EUI-64-based Interface Identifier (RFC2464 Section 4) */
+	addr[ 8] = nc->mac[0] ^ 0x02;
+	addr[ 9] = nc->mac[1];
+	addr[10] = nc->mac[2];
+	addr[11] = 0xff;
+	addr[12] = 0xfe;
+	addr[13] = nc->mac[3];
+	addr[14] = nc->mac[4];
+	addr[15] = nc->mac[5];
+	inet_ntop(AF_INET6, addr, addr_str, sizeof(addr_str));
+	p = longest->preferred_lifetime;
+	v = longest->valid_lifetime;
+
+	nc->v6_address = l_rtnl_address_new(addr_str, 128);
+	l_rtnl_address_set_noprefixroute(nc->v6_address, true);
+
+	if (p != 0xffffffff || v != 0xffffffff) {
+		l_rtnl_address_set_lifetimes(nc->v6_address,
+					p != 0xffffffff ? p : 0,
+					v != 0xffffffff ? v : 0);
+		l_rtnl_address_set_expiry(nc->v6_address,
+					p != 0xffffffff ?
+					r->start_time + p * L_USEC_PER_SEC : 0,
+					v != 0xffffffff ?
+					r->start_time + v * L_USEC_PER_SEC : 0);
+	}
+
+	l_queue_push_tail(nc->addresses.current, nc->v6_address);
+	l_queue_push_tail(nc->addresses.added, nc->v6_address);
+	netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_CONFIGURE);
+
+	/* TODO: set a renew timeout */
+}
+
+static void netconfig_set_slaac_address_lifetimes(struct l_netconfig *nc,
+						const struct l_icmp6_router *r)
+{
+	const uint8_t *addr = l_rtnl_address_get_in_addr(nc->v6_address);
+	bool updated = false;
+	uint64_t p_expiry;
+	uint64_t v_expiry;
+	uint32_t remaining = 0xffffffff;
+	unsigned int i;
+
+	if (L_WARN_ON(!addr))
+		return;
+
+	l_rtnl_address_get_expiry(nc->v6_address, &p_expiry, &v_expiry);
+
+	if (v_expiry)
+		remaining = (v_expiry - r->start_time) / L_USEC_PER_SEC;
+
+	for (i = 0; i < r->n_ac_prefixes; i++) {
+		const struct autoconf_prefix_info *prefix = &r->ac_prefixes[i];
+		uint32_t p = prefix->preferred_lifetime;
+		uint32_t v = prefix->valid_lifetime;
+
+		if (memcmp(prefix->prefix, addr, 8))
+			continue;
+
+		/* RFC4862 Section 5.5.3 e) */
+		if (v < 120 * 60 && v < remaining)
+			v = 120 * 60; /* 2 hours */
+
+		l_rtnl_address_set_lifetimes(nc->v6_address,
+						p != 0xffffffff ? p : 0,
+						v != 0xffffffff ? v : 0);
+		p_expiry = p != 0xffffffff ? r->start_time + p * L_USEC_PER_SEC : 0;
+		v_expiry = v != 0xffffffff ? r->start_time + v * L_USEC_PER_SEC : 0;
+		l_rtnl_address_set_expiry(nc->v6_address, p_expiry, v_expiry);
+		updated = true;
+
+		/*
+		 * TODO: modify the renew timeout.
+		 *
+		 * Also we probably want to apply a mechanism similar to that
+		 * in netconfig_check_route_need_update() to avoid generating
+		 * and UPDATED event for every RA that covers this prefix
+		 * with constant lifetime values.
+		 */
+	}
+
+	if (updated && !l_queue_find(nc->addresses.added, netconfig_match,
+					nc->v6_address))
+		l_queue_push_tail(nc->addresses.updated, nc->v6_address);
+}
+
 static uint64_t now;
 
 static bool netconfig_check_route_expired(void *data, void *user_data)
@@ -916,17 +1024,14 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
 	const struct l_icmp6_router *r;
 	struct netconfig_route_data *default_rd;
 	unsigned int i;
-	bool first_ra = false;
 
 	if (event != L_ICMP6_CLIENT_EVENT_ROUTER_FOUND)
 		return;
 
 	r = event_data;
 
-	if (nc->ra_timeout) {
-		first_ra = true;
+	if (nc->ra_timeout)
 		l_timeout_remove(l_steal_ptr(nc->ra_timeout));
-	}
 
 	netconfig_expire_routes(nc);
 
@@ -984,16 +1089,72 @@ process_nondefault_routes:
 			netconfig_remove_icmp6_route(nc, rd);
 	}
 
-	/* See if we should start DHCPv6 now */
-	if (first_ra) {
-		if (!l_icmp6_router_get_managed(r) ||
-				!netconfig_check_start_dhcp6(nc)) {
+	/*
+	 * For lack of a better policy, select between DHCPv6 and SLAAC based
+	 * on the first RA received.  Prefer DHCPv6.
+	 *
+	 * Just like we currently only request one address in l_dhcp6_client,
+	 * we only set up one address using SLAAC regardless of how many
+	 * prefixes are available.  Generate the address in the prefix that
+	 * offers the longest preferred_lifetime.
+	 */
+	if (nc->v6_auto_method == NETCONFIG_V6_METHOD_UNSET &&
+			l_icmp6_router_get_managed(r)) {
+		nc->v6_auto_method = NETCONFIG_V6_METHOD_DHCP;
+
+		if (!netconfig_check_start_dhcp6(nc)) {
 			netconfig_emit_event(nc, AF_INET6,
 						L_NETCONFIG_EVENT_FAILED);
 			return;
 		}
+
+		goto emit_event;
+	}
+
+	/*
+	 * DHCP not available according to this router, check if any of the
+	 * prefixes allow SLAAC.
+	 */
+	if (nc->v6_auto_method == NETCONFIG_V6_METHOD_UNSET &&
+			r->n_ac_prefixes) {
+		nc->v6_auto_method = NETCONFIG_V6_METHOD_SLAAC;
+
+		/*
+		 * The DAD for the link-local address may be still running
+		 * but again we can generate the global address already and
+		 * commit it to start in-kernel DAD for it.
+		 *
+		 * The global address alone should work for most uses.  On
+		 * the other hand since both the link-local address and the
+		 * global address are based on the same MAC, there's some
+		 * correlation between one failing DAD and the other
+		 * failing DAD due to another host using the same address.
+		 * As RFC4862 Section 5.4 notes we can't rely on that to
+		 * skip DAD for one of the addresses.
+		 */
+
+		netconfig_add_slaac_address(nc, r);
+		return;
 	}
 
+	/* Neither method seems available, fail */
+	if (nc->v6_auto_method == NETCONFIG_V6_METHOD_UNSET) {
+		netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED);
+		return;
+	}
+
+	/* DHCP already started or waiting for the LL address, nothing to do */
+	if (nc->v6_auto_method == NETCONFIG_V6_METHOD_DHCP)
+		goto emit_event;
+
+	/*
+	 * Otherwise we already have a SLAAC address, just check if any of the
+	 * auto-configuration prefixes in this RA covers our existing address
+	 * and allows us to extend its lifetime.
+	 */
+	netconfig_set_slaac_address_lifetimes(nc, r);
+
+emit_event:
 	/*
 	 * Note: we may be emitting this before L_NETCONFIG_EVENT_CONFIGURE.
 	 * We should probably instead save the affected routes in separate
@@ -1003,7 +1164,8 @@ process_nondefault_routes:
 	if (!l_queue_isempty(nc->routes.added) ||
 			!l_queue_isempty(nc->routes.updated) ||
 			!l_queue_isempty(nc->routes.removed) ||
-			!l_queue_isempty(nc->routes.expired))
+			!l_queue_isempty(nc->routes.expired) ||
+			!l_queue_isempty(nc->addresses.updated))
 		netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_UPDATE);
 }
 
@@ -1706,6 +1868,8 @@ configure_ipv6:
 		goto done;
 	}
 
+	netconfig->v6_auto_method = NETCONFIG_V6_METHOD_UNSET;
+
 	/*
 	 * We only care about being on addr_wait_list if we're waiting for
 	 * the link-local address for DHCP6.  Add ourself to the list here
-- 
2.34.1


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

* [PATCH 7/7] netconfig: Control optimistic DAD
  2022-09-19 13:30 [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs Andrew Zaborowski
                   ` (4 preceding siblings ...)
  2022-09-19 13:31 ` [PATCH 6/7] netconfig: Create SLAAC address Andrew Zaborowski
@ 2022-09-19 13:31 ` Andrew Zaborowski
  2022-09-19 18:43   ` Denis Kenzior
  5 siblings, 1 reply; 9+ messages in thread
From: Andrew Zaborowski @ 2022-09-19 13:31 UTC (permalink / raw)
  To: ell

Enable or disable optimistic DAD for the interface using /proc.  Add
l_netconfig_set_optimistic_dad_enabled() for the user to request that
RFC 4429 optimistic DAD be enabled.  As recommended in the RFC, we'll
only actually enable optimistic DAD for the automatic address generation
methods and disable it if a static IPv6 address is configured.  The
default if l_netconfig_set_optimistic_dad_enabled() isn't used is to
disable optimistic DAD always.

Optimistic DAD can shorten practical IPv6 setup time by an amount on
the order of a second by allowing the link-local address and the global
address to be used immediately after being generated.  For the
link-local address this means that DHCPv6 may start sooner and
for the global address, which is added to the kernel by the
L_NETCONFIG_EVENT_CONFIGURE event handler, the user can start
establishing connections sooner.  With DHCPv6 and some luck the two
savings may compound, with SLAAC only the global address's DAD time
should matter.
---
 ell/ell.sym     |  1 +
 ell/netconfig.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
 ell/netconfig.h |  2 ++
 3 files changed, 51 insertions(+), 1 deletion(-)

diff --git a/ell/ell.sym b/ell/ell.sym
index d76b2ea..6df9024 100644
--- a/ell/ell.sym
+++ b/ell/ell.sym
@@ -751,6 +751,7 @@ global:
 	l_netconfig_set_dns_override;
 	l_netconfig_set_domain_names_override;
 	l_netconfig_set_acd_enabled;
+	l_netconfig_set_optimistic_dad_enabled;
 	l_netconfig_check_config;
 	l_netconfig_reset_config;
 	l_netconfig_start;
diff --git a/ell/netconfig.c b/ell/netconfig.c
index 3336b30..7a9ee89 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -76,6 +76,7 @@ struct l_netconfig {
 	char *v6_gateway_override;
 	char **v6_dns_override;
 	char **v6_domain_names_override;
+	bool optimistic_dad_enabled;
 
 	bool started;
 	struct l_idle *do_static_work;
@@ -89,6 +90,7 @@ struct l_netconfig {
 	struct l_queue *icmp_route_data;
 	struct l_acd *acd;
 	unsigned int orig_disable_ipv6;
+	unsigned int orig_optimistic_dad;
 	uint8_t mac[ETH_ALEN];
 	struct l_timeout *ra_timeout;
 	enum {
@@ -1490,6 +1492,17 @@ LIB_EXPORT bool l_netconfig_set_acd_enabled(struct l_netconfig *netconfig,
 	return true;
 }
 
+LIB_EXPORT bool l_netconfig_set_optimistic_dad_enabled(
+						struct l_netconfig *netconfig,
+						bool enabled)
+{
+	if (unlikely(!netconfig || netconfig->started))
+		return false;
+
+	netconfig->optimistic_dad_enabled = enabled;
+	return true;
+}
+
 static bool netconfig_check_family_config(struct l_netconfig *nc,
 						uint8_t family)
 {
@@ -1724,7 +1737,8 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
 	struct in6_addr in6;
 	_auto_(l_free) char *ip = NULL;
 
-	if (ifa->ifa_flags & IFA_F_TENTATIVE)
+	if ((ifa->ifa_flags & IFA_F_TENTATIVE) &&
+			!(ifa->ifa_flags & IFA_F_OPTIMISTIC))
 		return;
 
 	if (!nc->started)
@@ -1828,6 +1842,8 @@ static void netconfig_ifaddr_ipv6_dump_done_cb(void *user_data)
 
 LIB_EXPORT bool l_netconfig_start(struct l_netconfig *netconfig)
 {
+	bool optimistic_dad;
+
 	if (unlikely(!netconfig || netconfig->started))
 		return false;
 
@@ -1855,6 +1871,28 @@ configure_ipv6:
 	if (!netconfig->v6_enabled)
 		goto done;
 
+	/*
+	 * Enable optimistic DAD if the user has requested it *and* it is
+	 * recommended by RFC 4429 Section 3.1 for the address generation
+	 * method in use:
+	 *   * mac-based Interface ID such as EUI-64
+	 *   * random
+	 *   * well-distributed hash function
+	 *   * DHCPv6
+	 * i.e. all autoconfiguration methods.  In any other case disable
+	 * it.
+	 */
+	optimistic_dad = netconfig->optimistic_dad_enabled &&
+		!netconfig->v6_static_addr;
+	netconfig->orig_optimistic_dad =
+		netconfig_proc_read_ipv6_uint_setting(netconfig,
+							"optimistic_dad");
+
+	if (!!netconfig->orig_optimistic_dad != optimistic_dad)
+		netconfig_proc_write_ipv6_uint_setting(netconfig,
+							"optimistic_dad",
+							optimistic_dad ? 1 : 0);
+
 	if (netconfig->v6_static_addr) {
 		/*
 		 * We're basically ready to configure the interface
@@ -1944,6 +1982,8 @@ unregister:
 
 LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig)
 {
+	bool optimistic_dad;
+
 	if (unlikely(!netconfig || !netconfig->started))
 		return;
 
@@ -1985,6 +2025,13 @@ LIB_EXPORT void l_netconfig_stop(struct l_netconfig *netconfig)
 						netconfig->orig_disable_ipv6);
 		netconfig->orig_disable_ipv6 = 0;
 	}
+
+	optimistic_dad = netconfig->optimistic_dad_enabled &&
+		!netconfig->v6_static_addr;
+	if (!!netconfig->orig_optimistic_dad != optimistic_dad)
+		netconfig_proc_write_ipv6_uint_setting(netconfig,
+						"optimistic_dad",
+						netconfig->orig_optimistic_dad);
 }
 
 /*
diff --git a/ell/netconfig.h b/ell/netconfig.h
index fb3c536..3c024fe 100644
--- a/ell/netconfig.h
+++ b/ell/netconfig.h
@@ -69,6 +69,8 @@ bool l_netconfig_set_dns_override(struct l_netconfig *netconfig, uint8_t family,
 bool l_netconfig_set_domain_names_override(struct l_netconfig *netconfig,
 						uint8_t family, char **names);
 bool l_netconfig_set_acd_enabled(struct l_netconfig *netconfig, bool enabled);
+bool l_netconfig_set_optimistic_dad_enabled(struct l_netconfig *netconfig,
+						bool enabled);
 bool l_netconfig_check_config(struct l_netconfig *netconfig);
 bool l_netconfig_reset_config(struct l_netconfig *netconfig);
 
-- 
2.34.1


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

* Re: [PATCH 7/7] netconfig: Control optimistic DAD
  2022-09-19 13:31 ` [PATCH 7/7] netconfig: Control optimistic DAD Andrew Zaborowski
@ 2022-09-19 18:43   ` Denis Kenzior
  2022-09-19 23:56     ` Andrew Zaborowski
  0 siblings, 1 reply; 9+ messages in thread
From: Denis Kenzior @ 2022-09-19 18:43 UTC (permalink / raw)
  To: Andrew Zaborowski, ell

Hi Andrew,

On 9/19/22 08:31, Andrew Zaborowski wrote:
> Enable or disable optimistic DAD for the interface using /proc.  Add
> l_netconfig_set_optimistic_dad_enabled() for the user to request that
> RFC 4429 optimistic DAD be enabled.  As recommended in the RFC, we'll
> only actually enable optimistic DAD for the automatic address generation
> methods and disable it if a static IPv6 address is configured.  The
> default if l_netconfig_set_optimistic_dad_enabled() isn't used is to
> disable optimistic DAD always.
> 
> Optimistic DAD can shorten practical IPv6 setup time by an amount on
> the order of a second by allowing the link-local address and the global
> address to be used immediately after being generated.  For the
> link-local address this means that DHCPv6 may start sooner and
> for the global address, which is added to the kernel by the
> L_NETCONFIG_EVENT_CONFIGURE event handler, the user can start
> establishing connections sooner.  With DHCPv6 and some luck the two
> savings may compound, with SLAAC only the global address's DAD time
> should matter.
> ---
>   ell/ell.sym     |  1 +
>   ell/netconfig.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++-
>   ell/netconfig.h |  2 ++
>   3 files changed, 51 insertions(+), 1 deletion(-)
> 

I went ahead and applied all patches in this series.  Question:

> @@ -1724,7 +1737,8 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
>   	struct in6_addr in6;
>   	_auto_(l_free) char *ip = NULL;
>   
> -	if (ifa->ifa_flags & IFA_F_TENTATIVE)
> +	if ((ifa->ifa_flags & IFA_F_TENTATIVE) &&
> +			!(ifa->ifa_flags & IFA_F_OPTIMISTIC))
>   		return;

This implies that we set the link local address even if it is still OPTIMISTIC. 
  Do we now need to update icmp6_send_router_solicitation() logic to make sure 
we do not include SLLAO from optimistic addresses?

>   
>   	if (!nc->started)

Regards,
-Denis

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

* Re: [PATCH 7/7] netconfig: Control optimistic DAD
  2022-09-19 18:43   ` Denis Kenzior
@ 2022-09-19 23:56     ` Andrew Zaborowski
  0 siblings, 0 replies; 9+ messages in thread
From: Andrew Zaborowski @ 2022-09-19 23:56 UTC (permalink / raw)
  To: Denis Kenzior; +Cc: ell

Hi Denis,

On Mon, 19 Sept 2022 at 20:59, Denis Kenzior <denkenz@gmail.com> wrote:
> > @@ -1724,7 +1737,8 @@ static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
> >       struct in6_addr in6;
> >       _auto_(l_free) char *ip = NULL;
> >
> > -     if (ifa->ifa_flags & IFA_F_TENTATIVE)
> > +     if ((ifa->ifa_flags & IFA_F_TENTATIVE) &&
> > +                     !(ifa->ifa_flags & IFA_F_OPTIMISTIC))
> >               return;
>
> This implies that we set the link local address even if it is still OPTIMISTIC.
>   Do we now need to update icmp6_send_router_solicitation() logic to make sure
> we do not include SLLAO from optimistic addresses?

Good point.  I'll add an extra l_icmp6_client_set_link_local_address()
parameter to indicate whether the address is optimistic and make sure
we call it again after DAD finishes.

Best regards

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

end of thread, other threads:[~2022-09-19 23:57 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-19 13:30 [PATCH 1/7] icmp6: Save SLAAC prefixes from RAs Andrew Zaborowski
2022-09-19 13:31 ` [PATCH 2/7] icmp6: Parse RDNSS and DNSSL options Andrew Zaborowski
2022-09-19 13:31 ` [PATCH 3/7] icmp6: Switch socket from AF_INET6 to AF_PACKET Andrew Zaborowski
2022-09-19 13:31 ` [PATCH 4/7] rtnl: Add l_rtnl_address_get_in_addr Andrew Zaborowski
2022-09-19 13:31 ` [PATCH 5/7] netconfig: Decouple icmp6 start from dhcp6 start Andrew Zaborowski
2022-09-19 13:31 ` [PATCH 6/7] netconfig: Create SLAAC address Andrew Zaborowski
2022-09-19 13:31 ` [PATCH 7/7] netconfig: Control optimistic DAD Andrew Zaborowski
2022-09-19 18:43   ` Denis Kenzior
2022-09-19 23:56     ` Andrew Zaborowski

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).