]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
network: also hash address label and broadcast address
authorYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 23 Feb 2022 22:24:19 +0000 (07:24 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Wed, 23 Feb 2022 22:28:04 +0000 (07:28 +0900)
Otherwise, even if the address label and/or broadcast address are changed
in a .network file, the change will not be applied on reconfigure.

src/network/networkd-address.c

index ee1f0fc21a6f4db0c716efc697b0ad7b61cf5778..d577926a7c539fbbb4fdbaf5f4944d153a085304 100644 (file)
@@ -328,6 +328,15 @@ void address_hash_func(const Address *a, struct siphash *state) {
         siphash24_compress(&a->prefixlen, sizeof(a->prefixlen), state);
         siphash24_compress(&a->in_addr, FAMILY_ADDRESS_SIZE(a->family), state);
         siphash24_compress(&a->in_addr_peer, FAMILY_ADDRESS_SIZE(a->family), state);
+
+        if (a->family == AF_INET) {
+                /* On update, the kernel ignores the address label and broadcast address, hence we need
+                 * to distinguish addresses with different labels or broadcast addresses. Otherwise,
+                 * the label or broadcast address change will not be applied when we reconfigure the
+                 * interface. */
+                siphash24_compress_string(a->label, state);
+                siphash24_compress(&a->broadcast, sizeof(a->broadcast), state);
+        }
 }
 
 int address_compare_func(const Address *a1, const Address *a2) {
@@ -352,6 +361,16 @@ int address_compare_func(const Address *a1, const Address *a2) {
         if (r != 0)
                 return r;
 
+        if (a1->family == AF_INET) {
+                r = strcmp_ptr(a1->label, a2->label);
+                if (r != 0)
+                        return r;
+
+                r = CMP(a1->broadcast.s_addr, a2->broadcast.s_addr);
+                if (r != 0)
+                        return r;
+        }
+
         return 0;
 }
 
@@ -525,6 +544,7 @@ int address_get(Link *link, const Address *in, Address **ret) {
 }
 
 int link_get_address(Link *link, int family, const union in_addr_union *address, unsigned char prefixlen, Address **ret) {
+        Address *a;
         int r;
 
         assert(link);
@@ -536,40 +556,50 @@ int link_get_address(Link *link, int family, const union in_addr_union *address,
          * arbitrary prefixlen will be returned. */
 
         if (prefixlen != 0) {
-                _cleanup_(address_freep) Address *a = NULL;
+                _cleanup_(address_freep) Address *tmp = NULL;
 
                 /* If prefixlen is set, then we can use address_get(). */
 
-                r = address_new(&a);
+                r = address_new(&tmp);
                 if (r < 0)
                         return r;
 
-                a->family = family;
-                a->in_addr = *address;
-                a->prefixlen = prefixlen;
-
-                return address_get(link, a, ret);
-        } else {
-                Address *a;
-
-                SET_FOREACH(a, link->addresses) {
-                        if (a->family != family)
-                                continue;
-
-                        if (!in_addr_equal(family, &a->in_addr, address))
-                                continue;
-
-                        if (in_addr_is_set(family, &a->in_addr_peer))
-                                continue;
+                tmp->family = family;
+                tmp->in_addr = *address;
+                tmp->prefixlen = prefixlen;
+                address_set_broadcast(tmp, link);
 
+                if (address_get(link, tmp, &a) >= 0) {
                         if (ret)
                                 *ret = a;
 
                         return 0;
                 }
 
-                return -ENOENT;
+                if (family == AF_INET6)
+                        return -ENOENT;
+
+                /* IPv4 addresses may have label and/or non-default broadcast address.
+                 * Hence, we need to always fallback below. */
+        }
+
+        SET_FOREACH(a, link->addresses) {
+                if (a->family != family)
+                        continue;
+
+                if (!in_addr_equal(family, &a->in_addr, address))
+                        continue;
+
+                if (in_addr_is_set(family, &a->in_addr_peer))
+                        continue;
+
+                if (ret)
+                        *ret = a;
+
+                return 0;
         }
+
+        return -ENOENT;
 }
 
 int manager_has_address(Manager *manager, int family, const union in_addr_union *address, bool check_ready) {