]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: if ipv6ll is disabled, enumerate tentative ipv6 addrs before dropping foreig...
authorDan Streetman <ddstreet@canonical.com>
Sat, 4 Jan 2020 23:41:18 +0000 (18:41 -0500)
committerDan Streetman <ddstreet@canonical.com>
Thu, 9 Jan 2020 20:19:19 +0000 (15:19 -0500)
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.

src/network/networkd-link.c

index 2913bf946d43c0db68d48cde5df569421f00b638..fa6ac4b8f79d9520ce201afec1735fc66dafed38 100644 (file)
@@ -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))