All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andrew Zaborowski <andrew.zaborowski at intel.com>
To: ell at lists.01.org
Subject: [PATCH 11/17] netconfig: Wait for link-local address before DHCPv6
Date: Sat, 14 May 2022 00:47:07 +0200	[thread overview]
Message-ID: <20220513224713.1447773-11-andrew.zaborowski@intel.com> (raw)
In-Reply-To: 20220513224713.1447773-1-andrew.zaborowski@intel.com

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

Keep one global rtnl instance instead of one per interface so that we
can have just one RTM_NEWADDR listener.  On l_netconfig_start dump
existing IPv6 addresses and wait until a link-local address is added so
it can be used as the source address for our DHCPv6 frames.
---
 ell/netconfig.c | 178 ++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 163 insertions(+), 15 deletions(-)

diff --git a/ell/netconfig.c b/ell/netconfig.c
index 60962ce..c6a5c8e 100644
--- a/ell/netconfig.c
+++ b/ell/netconfig.c
@@ -64,7 +64,7 @@ struct l_netconfig {
 	char **v6_dns_override;
 	char **v6_domain_names_override;
 
-	struct l_netlink *rtnl;
+	unsigned int ifaddr6_dump_cmd_id;
 	bool started;
 	struct l_idle *do_static_work;
 	bool v4_configured;
@@ -115,6 +115,11 @@ union netconfig_addr {
 	struct in6_addr v6;
 };
 
+static struct l_netlink *rtnl;
+static unsigned int rtnl_use_count;
+static struct l_queue *addr_wait_list;
+static unsigned int rtnl_id;
+
 static void netconfig_update_cleanup(struct l_netconfig *nc)
 {
 	l_queue_clear(nc->addresses.added, NULL);
@@ -848,13 +853,17 @@ static void netconfig_icmp6_event_handler(struct l_icmp6_client *client,
 LIB_EXPORT struct l_netconfig *l_netconfig_new(uint32_t ifindex)
 {
 	struct l_netconfig *nc;
-	struct l_netlink *rtnl = l_netlink_new(NETLINK_ROUTE);
 
-	if (!rtnl)
-		return NULL;
+	if (!rtnl) {
+		rtnl = l_netlink_new(NETLINK_ROUTE);
+
+		if (!rtnl)
+			return NULL;
+	}
+
+	rtnl_use_count++;
 
 	nc = l_new(struct l_netconfig, 1);
-	nc->rtnl = rtnl;
 	nc->ifindex = ifindex;
 	nc->v4_enabled = true;
 
@@ -912,8 +921,12 @@ LIB_EXPORT void l_netconfig_destroy(struct l_netconfig *netconfig)
 	l_queue_destroy(netconfig->routes.added, NULL);
 	l_queue_destroy(netconfig->routes.updated, NULL);
 	l_queue_destroy(netconfig->routes.removed, NULL);
-	l_netlink_destroy(netconfig->rtnl);
 	l_free(netconfig);
+
+	rtnl_use_count--;
+
+	if (!rtnl_use_count)
+		l_netlink_destroy(l_steal_ptr(rtnl));
 }
 
 /*
@@ -1208,6 +1221,115 @@ static void netconfig_do_static_config(struct l_idle *idle, void *user_data)
 	}
 }
 
+static void netconfig_rtnl_unregister(void *user_data)
+{
+	if (!addr_wait_list || !l_queue_isempty(addr_wait_list))
+		return;
+
+	l_queue_destroy(l_steal_ptr(addr_wait_list), NULL);
+	l_netlink_unregister(rtnl, rtnl_id);
+	rtnl_id = 0;
+}
+
+static void netconfig_addr_wait_unregister(struct l_netconfig *nc,
+						bool in_notify)
+{
+	if (nc->ifaddr6_dump_cmd_id) {
+		unsigned int cmd_id = nc->ifaddr6_dump_cmd_id;
+
+		nc->ifaddr6_dump_cmd_id = 0;
+		l_netlink_cancel(rtnl, cmd_id);
+	}
+
+	if (!l_queue_remove(addr_wait_list, nc))
+		return;
+
+	if (!l_queue_isempty(addr_wait_list))
+		return;
+
+	/* We can't do l_netlink_unregister() inside a notification */
+	if (in_notify)
+		l_idle_oneshot(netconfig_rtnl_unregister, NULL, NULL);
+	else
+		netconfig_rtnl_unregister(NULL);
+}
+
+static void netconfig_ifaddr_ipv6_added(struct l_netconfig *nc,
+					const struct ifaddrmsg *ifa,
+					uint32_t len)
+{
+	struct in6_addr in6;
+	_auto_(l_free) char *ip = NULL;
+
+	if (ifa->ifa_flags & IFA_F_TENTATIVE)
+		return;
+
+	if (!nc->started)
+		return;
+
+	l_rtnl_ifaddr6_extract(ifa, len, &ip);
+	inet_pton(AF_INET6, ip, &in6);
+
+	if (!IN6_IS_ADDR_LINKLOCAL(&in6))
+		return;
+
+	netconfig_addr_wait_unregister(nc, true);
+
+	l_dhcp6_client_set_link_local_address(nc->dhcp6_client, ip);
+
+	/*
+	 * Only now that we have a link-local address start actual DHCPv6
+	 * setup.
+	 */
+	if (l_dhcp6_client_start(nc->dhcp6_client))
+		return;
+
+	netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED);
+}
+
+static void netconfig_ifaddr_ipv6_notify(uint16_t type, const void *data,
+						uint32_t len, void *user_data)
+{
+	const struct ifaddrmsg *ifa = data;
+	uint32_t bytes = len - NLMSG_ALIGN(sizeof(struct ifaddrmsg));
+	const struct l_queue_entry *entry, *next;
+
+	switch (type) {
+	case RTM_NEWADDR:
+		/* Iterate safely since elements may be removed */
+		for (entry = l_queue_get_entries(addr_wait_list); entry;
+				entry = next) {
+			struct l_netconfig *nc = entry->data;
+
+			next = entry->next;
+			netconfig_ifaddr_ipv6_added(nc, ifa, bytes);
+		}
+
+		break;
+	}
+}
+
+static void netconfig_ifaddr_ipv6_dump_cb(int error, uint16_t type,
+						const void *data, uint32_t len,
+						void *user_data)
+{
+	struct l_netconfig *nc = user_data;
+
+	if (!nc->ifaddr6_dump_cmd_id || !nc->started)
+		return;
+
+	if (error) {
+		netconfig_addr_wait_unregister(nc, false);
+		netconfig_emit_event(nc, AF_INET6, L_NETCONFIG_EVENT_FAILED);
+		return;
+	}
+
+	if (type != RTM_NEWADDR)
+		return;
+
+	netconfig_ifaddr_ipv6_notify(type, data, len, user_data);
+}
+
 LIB_EXPORT bool l_netconfig_start(struct l_netconfig *netconfig)
 {
 	if (unlikely(!netconfig || netconfig->started))
@@ -1250,14 +1372,38 @@ configure_ipv6:
 		goto done;
 	}
 
-	if (!l_dhcp6_client_start(netconfig->dhcp6_client))
-		goto stop_ipv4;
+	/*
+	 * 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
+	 * 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.
+	 */
+	if (!addr_wait_list) {
+		addr_wait_list = l_queue_new();
+
+		rtnl_id = l_netlink_register(rtnl, RTNLGRP_IPV6_IFADDR,
+						netconfig_ifaddr_ipv6_notify,
+						netconfig, NULL);
+		if (!rtnl_id)
+			goto unregister;
+	}
+
+	netconfig->ifaddr6_dump_cmd_id = l_rtnl_ifaddr6_dump(rtnl,
+						netconfig_ifaddr_ipv6_dump_cb,
+						netconfig, NULL);
+	if (!netconfig->ifaddr6_dump_cmd_id)
+		goto unregister;
+
+	l_queue_push_tail(addr_wait_list, netconfig);
 
 done:
 	netconfig->started = true;
 	return true;
 
-stop_ipv4:
+unregister:
+	netconfig_addr_wait_unregister(netconfig, false);
+
 	if (netconfig->v4_enabled) {
 		if (netconfig->v4_static_addr)
 			l_idle_remove(l_steal_ptr(netconfig->do_static_work));
@@ -1281,6 +1427,8 @@ 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));
 
+	netconfig_addr_wait_unregister(netconfig, false);
+
 	netconfig_update_cleanup(netconfig);
 	l_queue_clear(netconfig->routes.current,
 			(l_queue_destroy_func_t) l_rtnl_route_free);
@@ -1344,34 +1492,34 @@ LIB_EXPORT void l_netconfig_apply_rtnl(struct l_netconfig *netconfig)
 
 	for (entry = l_queue_get_entries(netconfig->addresses.removed); entry;
 			entry = entry->next)
-		l_rtnl_ifaddr_delete(netconfig->rtnl, netconfig->ifindex,
+		l_rtnl_ifaddr_delete(rtnl, netconfig->ifindex,
 					entry->data, NULL, NULL, NULL);
 
 	for (entry = l_queue_get_entries(netconfig->addresses.added); entry;
 			entry = entry->next)
-		l_rtnl_ifaddr_add(netconfig->rtnl, netconfig->ifindex,
+		l_rtnl_ifaddr_add(rtnl, netconfig->ifindex,
 					entry->data, NULL, NULL, NULL);
 
 	/* We can use l_rtnl_ifaddr_add here since that uses NLM_F_REPLACE */
 	for (entry = l_queue_get_entries(netconfig->addresses.updated); entry;
 			entry = entry->next)
-		l_rtnl_ifaddr_add(netconfig->rtnl, netconfig->ifindex,
+		l_rtnl_ifaddr_add(rtnl, netconfig->ifindex,
 					entry->data, NULL, NULL, NULL);
 
 	for (entry = l_queue_get_entries(netconfig->routes.removed); entry;
 			entry = entry->next)
-		l_rtnl_route_delete(netconfig->rtnl, netconfig->ifindex,
+		l_rtnl_route_delete(rtnl, netconfig->ifindex,
 					entry->data, NULL, NULL, NULL);
 
 	for (entry = l_queue_get_entries(netconfig->routes.added); entry;
 			entry = entry->next)
-		l_rtnl_route_add(netconfig->rtnl, netconfig->ifindex,
+		l_rtnl_route_add(rtnl, netconfig->ifindex,
 					entry->data, NULL, NULL, NULL);
 
 	/* We can use l_rtnl_route_add here since that uses NLM_F_REPLACE */
 	for (entry = l_queue_get_entries(netconfig->routes.updated); entry;
 			entry = entry->next)
-		l_rtnl_route_add(netconfig->rtnl, netconfig->ifindex,
+		l_rtnl_route_add(rtnl, netconfig->ifindex,
 					entry->data, NULL, NULL, NULL);
 }
 
-- 
2.32.0

             reply	other threads:[~2022-05-13 22:47 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-05-13 22:47 Andrew Zaborowski [this message]
  -- strict thread matches above, loose matches on Subject: below --
2022-05-13 14:55 [PATCH 11/17] netconfig: Wait for link-local address before DHCPv6 Andrew Zaborowski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220513224713.1447773-11-andrew.zaborowski@intel.com \
    --to=unknown@example.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.