]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: introduces link_drop_ipv6ll_addresses()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 23 May 2021 14:47:19 +0000 (23:47 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 7 Jun 2021 21:33:27 +0000 (06:33 +0900)
It is not necessary to parse whole message and store the address in
Link::addresses_foreign, as the address will be removed soon later.

src/network/networkd-address.c
src/network/networkd-address.h

index 06c5c39f1ce6169d3a06ec259a6c2b572a891a3f..dac799ee51090309271ce4362cfba9df07623df5 100644 (file)
@@ -784,7 +784,7 @@ bool link_address_is_dynamic(const Link *link, const Address *address) {
         return false;
 }
 
-static int link_enumerate_ipv6_tentative_addresses(Link *link) {
+int link_drop_ipv6ll_addresses(Link *link) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
         int r;
 
@@ -792,6 +792,12 @@ static int link_enumerate_ipv6_tentative_addresses(Link *link) {
         assert(link->manager);
         assert(link->manager->rtnl);
 
+        /* IPv6LL address may be in the tentative state, and in that case networkd has not received it.
+         * So, we need to dump all IPv6 addresses. */
+
+        if (link_ipv6ll_enabled(link))
+                return 0;
+
         r = sd_rtnl_message_new_addr(link->manager->rtnl, &req, RTM_GETADDR, link->ifindex, AF_INET6);
         if (r < 0)
                 return r;
@@ -805,27 +811,57 @@ static int link_enumerate_ipv6_tentative_addresses(Link *link) {
                 return r;
 
         for (sd_netlink_message *addr = reply; addr; addr = sd_netlink_message_next(addr)) {
-                unsigned char flags;
+                _cleanup_(address_freep) Address *a = NULL;
+                unsigned char flags, prefixlen;
+                struct in6_addr address;
                 int ifindex;
 
                 /* NETLINK_GET_STRICT_CHK socket option is supported since kernel 4.20. To support
                  * older kernels, we need to check ifindex here. */
                 r = sd_rtnl_message_addr_get_ifindex(addr, &ifindex);
                 if (r < 0) {
-                        log_link_warning_errno(link, r, "rtnl: received address message without valid ifindex, ignoring: %m");
+                        log_link_debug_errno(link, r, "rtnl: received address message without valid 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 without valid flags, ignoring: %m");
+                        log_link_debug_errno(link, r, "rtnl: received address message without valid flags, ignoring: %m");
                         continue;
-                } else if (!(flags & IFA_F_TENTATIVE))
+                }
+
+                r = sd_rtnl_message_addr_get_prefixlen(addr, &prefixlen);
+                if (r < 0) {
+                        log_link_debug_errno(link, r, "rtnl: received address message without prefixlen, ignoring: %m");
                         continue;
+                }
+
+                if (sd_netlink_message_read_in6_addr(addr, IFA_LOCAL, NULL) >= 0)
+                        /* address with peer, ignoring. */
+                        continue;
+
+                r = sd_netlink_message_read_in6_addr(addr, IFA_ADDRESS, &address);
+                if (r < 0) {
+                        log_link_debug_errno(link, r, "rtnl: received address message without valid address, ignoring: %m");
+                        continue;
+                }
+
+                if (!in6_addr_is_link_local(&address))
+                         continue;
 
-                log_link_debug(link, "Found tentative ipv6 link-local address");
-                (void) manager_rtnl_process_address(link->manager->rtnl, addr, link->manager);
+                r = address_new(&a);
+                if (r < 0)
+                        return -ENOMEM;
+
+                a->family = AF_INET6;
+                a->in_addr.in6 = address;
+                a->prefixlen = prefixlen;
+                a->flags = flags;
+
+                r = address_remove(a, link);
+                if (r < 0)
+                        return r;
         }
 
         return 0;
@@ -837,17 +873,13 @@ int link_drop_foreign_addresses(Link *link) {
 
         assert(link);
 
-        /* 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;
-        }
+        r = link_drop_ipv6ll_addresses(link);
+        if (r < 0)
+                return log_link_warning_errno(link, r, "Failed to drop IPv6LL address: %m");
 
         SET_FOREACH(address, link->addresses_foreign) {
-                /* we consider IPv6LL addresses to be managed by the kernel */
-                if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6) == 1 && link_ipv6ll_enabled(link))
+                /* We consider IPv6LL addresses to be managed by the kernel, or dropped in link_drop_ipv6ll_addresses() */
+                if (address->family == AF_INET6 && in6_addr_is_link_local(&address->in_addr.in6) == 1)
                         continue;
 
                 if (link_address_is_dynamic(link, address)) {
index af9f05c1e90bd41bab923eba6d5524b1aceca1eb..56251089d1ae8a6ca61abe1ff7f84d8ef78d0ad0 100644 (file)
@@ -64,6 +64,7 @@ DEFINE_NETWORK_SECTION_FUNCTIONS(Address, address_free);
 
 int link_drop_addresses(Link *link);
 int link_drop_foreign_addresses(Link *link);
+int link_drop_ipv6ll_addresses(Link *link);
 bool link_address_is_dynamic(const Link *link, const Address *address);
 int link_get_ipv6_address(Link *link, const struct in6_addr *address, Address **ret);
 int link_get_ipv4_address(Link *link, const struct in_addr *address, unsigned char prefixlen, Address **ret);