summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTom Gundersen <teg@jklm.no>2014-05-16 00:27:56 +0200
committerTom Gundersen <teg@jklm.no>2014-05-17 20:46:11 +0200
commit9505d3c6deda0452c22ab2ed47bca74b98d87a17 (patch)
tree36abb35e666d895aca033dad602f699aa1658e29 /src
parentac976532063da637a70af3e39e0e1876267018ed (diff)
networkd: IP address equality
Diffstat (limited to 'src')
-rw-r--r--src/network/networkd-address.c43
-rw-r--r--src/network/networkd.h1
-rw-r--r--src/network/test-network.c47
3 files changed, 91 insertions, 0 deletions
diff --git a/src/network/networkd-address.c b/src/network/networkd-address.c
index 6977fe7947..34863b2f69 100644
--- a/src/network/networkd-address.c
+++ b/src/network/networkd-address.c
@@ -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");
+ }
+}
diff --git a/src/network/networkd.h b/src/network/networkd.h
index eaaf2593f9..d5154aa64e 100644
--- a/src/network/networkd.h
+++ b/src/network/networkd.h
@@ -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)
diff --git a/src/network/test-network.c b/src/network/test-network.c
index 38d57cc6a6..a0e04f8fd2 100644
--- a/src/network/test-network.c
+++ b/src/network/test-network.c
@@ -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);