summaryrefslogtreecommitdiffstats
path: root/src/network/networkd-link.c
diff options
context:
space:
mode:
authorDan Streetman <ddstreet@canonical.com>2020-01-05 00:41:18 +0100
committerDan Streetman <ddstreet@canonical.com>2020-01-09 21:19:19 +0100
commit0917a2717810d3c6b5b91e9fb057b2520e3ad733 (patch)
treeb3c94075cb538742c7e36f61a1bacacd38bfb38c /src/network/networkd-link.c
parentnetwork: add link->setting_genmode flag (diff)
downloadsystemd-0917a2717810d3c6b5b91e9fb057b2520e3ad733.tar.xz
systemd-0917a2717810d3c6b5b91e9fb057b2520e3ad733.zip
network: if ipv6ll is disabled, enumerate tentative ipv6 addrs before dropping foreign addrs
The kernel will create an ipv6ll tentative address immediately when an interface is raised if addr_gen_mode is not disabled; and, the kernel does not notify netlink listeners about any tentative addresses. So it's possible for an interface to contain tentative ipv6 link-local address(es) that networkd doesn't know about when all foreign addresses are dropped. In this case, networkd is later notified about the new ipv6ll address(es) after they finish DAD and are no longer tentative; but since that's after networkd has already dropped foreign addresses, they are incorrectly left on the interface.
Diffstat (limited to 'src/network/networkd-link.c')
-rw-r--r--src/network/networkd-link.c50
1 files changed, 50 insertions, 0 deletions
diff --git a/src/network/networkd-link.c b/src/network/networkd-link.c
index 2913bf946d..fa6ac4b8f7 100644
--- a/src/network/networkd-link.c
+++ b/src/network/networkd-link.c
@@ -2506,6 +2506,48 @@ static bool link_address_is_dynamic(Link *link, Address *address) {
return false;
}
+static int link_enumerate_ipv6_tentative_addresses(Link *link) {
+ _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
+ sd_netlink_message *addr;
+ int r;
+
+ assert(link);
+ assert(link->manager);
+ assert(link->manager->rtnl);
+
+ r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_GETADDR, 0, AF_INET6);
+ if (r < 0)
+ return r;
+
+ r = sd_netlink_call(link->manager->rtnl, req, 0, &reply);
+ if (r < 0)
+ return r;
+
+ for (addr = reply; addr; addr = sd_netlink_message_next(addr)) {
+ unsigned char flags;
+ int ifindex;
+
+ r = sd_rtnl_message_addr_get_ifindex(addr, &ifindex);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: invalid ifindex, ignoring: %m");
+ continue;
+ } else if (link->ifindex != ifindex)
+ continue;
+
+ r = sd_rtnl_message_addr_get_flags(addr, &flags);
+ if (r < 0) {
+ log_link_warning_errno(link, r, "rtnl: received address message with invalid flags, ignoring: %m");
+ continue;
+ } else if (!(flags & IFA_F_TENTATIVE))
+ continue;
+
+ log_link_debug(link, "Found tentative ipv6 link-local address");
+ (void) manager_rtnl_process_address(link->manager->rtnl, addr, link->manager);
+ }
+
+ return 0;
+}
+
static int link_drop_foreign_config(Link *link) {
Address *address;
Neighbor *neighbor;
@@ -2513,6 +2555,14 @@ static int link_drop_foreign_config(Link *link) {
Iterator i;
int r;
+ /* The kernel doesn't notify us about tentative addresses;
+ * so if ipv6ll is disabled, we need to enumerate them now so we can drop them below */
+ if (!link_ipv6ll_enabled(link)) {
+ r = link_enumerate_ipv6_tentative_addresses(link);
+ if (r < 0)
+ return r;
+ }
+
SET_FOREACH(address, link->addresses_foreign, i) {
/* we consider IPv6LL addresses to be managed by the kernel */
if (address->family == AF_INET6 && in_addr_is_link_local(AF_INET6, &address->in_addr) == 1 && link_ipv6ll_enabled(link))