diff mbox series

[1/7] icmp6: Save SLAAC prefixes from RAs

Message ID 20220919133105.3129080-1-andrew.zaborowski@intel.com (mailing list archive)
State Accepted, archived
Headers show
Series [1/7] icmp6: Save SLAAC prefixes from RAs | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
prestwoj/iwd-ci-makedistcheck success Make Distcheck
prestwoj/iwd-ci-build success Build - Configure
prestwoj/iwd-ci-clang success clang PASS
prestwoj/iwd-ci-makecheckvalgrind success Make Check w/Valgrind
prestwoj/iwd-ci-makecheck success Make Check
prestwoj/iwd-ci-testrunner fail test-runner - FAIL:

Commit Message

Andrew Zaborowski Sept. 19, 2022, 1:30 p.m. UTC
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 mbox series

Patch

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)