chiark / gitweb /
networkd: IP address equality
authorTom Gundersen <teg@jklm.no>
Thu, 15 May 2014 22:27:56 +0000 (00:27 +0200)
committerTom Gundersen <teg@jklm.no>
Sat, 17 May 2014 18:46:11 +0000 (20:46 +0200)
src/network/networkd-address.c
src/network/networkd.h
src/network/test-network.c

index 6977fe7..34863b2 100644 (file)
@@ -508,3 +508,46 @@ int config_parse_label(const char *unit,
 
         return 0;
 }
+
+bool address_equal(Address *a1, Address *a2) {
+        /* same object */
+        if (a1 == a2)
+                return true;
+
+        /* one, but not both, is NULL */
+        if (!a1 || !a2)
+                return false;
+
+        if (a1->family != a2->family)
+                return false;
+
+        switch (a1->family) {
+        /* use the same notion of equality as the kernel does */
+        case AF_UNSPEC:
+                return true;
+
+        case AF_INET:
+                if (a1->prefixlen != a2->prefixlen)
+                        return false;
+                else {
+                        uint32_t b1, b2;
+
+                        b1 = be32toh(a1->in_addr.in.s_addr);
+                        b2 = be32toh(a2->in_addr.in.s_addr);
+
+                        return (b1 >> (32 - a1->prefixlen)) == (b2 >> (32 - a1->prefixlen));
+                }
+
+        case AF_INET6:
+        {
+                uint64_t *b1, *b2;
+
+                b1 = (uint64_t*)&a1->in_addr.in6;
+                b2 = (uint64_t*)&a2->in_addr.in6;
+
+                return (((b1[0] ^ b2[0]) | (b1[1] ^ b2[1])) == 0UL);
+        }
+        default:
+                assert_not_reached("Invalid address family");
+        }
+}
index eaaf259..d5154aa 100644 (file)
@@ -386,6 +386,7 @@ void address_free(Address *address);
 int address_configure(Address *address, Link *link, sd_rtnl_message_handler_t callback);
 int address_update(Address *address, Link *link, sd_rtnl_message_handler_t callback);
 int address_drop(Address *address, Link *link, sd_rtnl_message_handler_t callback);
+bool address_equal(Address *a1, Address *a2);
 
 DEFINE_TRIVIAL_CLEANUP_FUNC(Address*, address_free);
 #define _cleanup_address_free_ _cleanup_(address_freep)
index 38d57cc..a0e04f8 100644 (file)
@@ -42,11 +42,58 @@ static void test_network_get(Manager *manager, struct udev_device *loopback) {
         assert_se(!network);
 }
 
+static void test_address_equality(void) {
+        Address *a1, *a2;
+
+        assert_se(address_new_dynamic(&a1) >= 0);
+        assert_se(address_new_dynamic(&a2) >= 0);
+
+        assert_se(address_equal(NULL, NULL));
+        assert_se(!address_equal(a1, NULL));
+        assert_se(!address_equal(NULL, a2));
+        assert_se(address_equal(a1, a2));
+
+        a1->family = AF_INET;
+        assert_se(!address_equal(a1, a2));
+
+        a2->family = AF_INET;
+        assert_se(address_equal(a1, a2));
+
+        assert_se(inet_pton(AF_INET, "192.168.3.9", &a1->in_addr.in));
+        assert_se(!address_equal(a1, a2));
+        assert_se(inet_pton(AF_INET, "192.168.3.9", &a2->in_addr.in));
+        assert_se(address_equal(a1, a2));
+
+        a1->prefixlen = 10;
+        assert_se(!address_equal(a1, a2));
+        a2->prefixlen = 10;
+        assert_se(address_equal(a1, a2));
+
+        assert_se(inet_pton(AF_INET, "192.168.3.10", &a2->in_addr.in));
+        assert_se(address_equal(a1, a2));
+
+        a1->family = AF_INET6;
+        assert_se(!address_equal(a1, a2));
+
+        a2->family = AF_INET6;
+        assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::2", &a1->in_addr.in6));
+        assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::2", &a2->in_addr.in6));
+        assert_se(address_equal(a1, a2));
+
+        a2->prefixlen = 8;
+        assert_se(address_equal(a1, a2));
+
+        assert_se(inet_pton(AF_INET6, "2001:4ca0:4f01::1", &a2->in_addr.in6));
+        assert_se(!address_equal(a1, a2));
+}
+
 int main(void) {
         _cleanup_manager_free_ Manager *manager = NULL;
         struct udev *udev;
         struct udev_device *loopback;
 
+        test_address_equality();
+
         assert_se(manager_new(&manager) >= 0);
 
         test_load_config(manager);