diff --git a/src/libnm-glib-aux/nm-inet-utils.c b/src/libnm-glib-aux/nm-inet-utils.c index 2ee73ad5e7..f4f418cf18 100644 --- a/src/libnm-glib-aux/nm-inet-utils.c +++ b/src/libnm-glib-aux/nm-inet-utils.c @@ -238,6 +238,42 @@ nm_ip6_addr_clear_host_address(struct in6_addr *dst, const struct in6_addr *src, return dst; } +/** + * nm_ip6_addr_get_subnet_id: + * @addr: the IPv6 address + * @plen: the prefix length + * + * Given an IPv6 address and a prefix length, returns the subnet ID, + * defined as follows: + * + * | plen bits | (64 - plen) bits | 64 bits | + * +-----------------+------------------+-------------------------------+ + * | prefix | subnet ID | interface ID | + * +-----------------+------------------+-------------------------------+ + * + * The prefix length must be a value between 0 and 64. + */ +guint64 +nm_ip6_addr_get_subnet_id(struct in6_addr *addr, guint8 plen) +{ + int nbits = 64 - plen; + guint64 res = 0; + guint64 tmp; + guint64 i; + + g_return_val_if_fail(addr, 0); + g_return_val_if_fail(plen <= 64, 0); + + for (i = 0; i < 2 && nbits > 0; i++, nbits -= 32) { + tmp = htonl(addr->s6_addr32[1 - i]); + tmp = tmp & ((1ULL << NM_MIN(32U, (guint) nbits)) - 1); + tmp = tmp << (32 * i); + res += tmp; + } + + return res; +} + int nm_ip6_addr_same_prefix_cmp(const struct in6_addr *addr_a, const struct in6_addr *addr_b, diff --git a/src/libnm-glib-aux/nm-inet-utils.h b/src/libnm-glib-aux/nm-inet-utils.h index 489d21dde2..999519f890 100644 --- a/src/libnm-glib-aux/nm-inet-utils.h +++ b/src/libnm-glib-aux/nm-inet-utils.h @@ -218,6 +218,8 @@ nm_ip4_addr_clear_host_address(in_addr_t addr, guint32 plen) const struct in6_addr * nm_ip6_addr_clear_host_address(struct in6_addr *dst, const struct in6_addr *src, guint32 plen); +guint64 nm_ip6_addr_get_subnet_id(struct in6_addr *addr, guint8 plen); + /*****************************************************************************/ static inline int diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 2f09a5490d..931c25c667 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -397,6 +397,53 @@ test_nm_ip4_addr_netmask_from_prefix(void) /*****************************************************************************/ +static void +test_nm_ip6_addr_get_subnet_id(void) +{ +#define check_subnet_id(_addrstr, _plen, _subnet_id) \ + { \ + struct in6_addr addr; \ + \ + addr = nmtst_inet6_from_string(_addrstr); \ + g_assert_cmpint(nm_ip6_addr_get_subnet_id(&addr, _plen), ==, _subnet_id); \ + } + + check_subnet_id("aaaa:bbbb:cccc:ffff::", 64, 0); + check_subnet_id("aaaa:bbbb:cccc:fffe::", 63, 0); + check_subnet_id("aaaa:bbbb:cccc:ffff::", 63, 1); + check_subnet_id("aaaa:bbbb:cccc:fffc::", 62, 0); + check_subnet_id("aaaa:bbbb:cccc:fffd::", 62, 1); + check_subnet_id("aaaa:bbbb:cccc:fffe::", 62, 2); + check_subnet_id("aaaa:bbbb:cccc:ffff::", 62, 3); + check_subnet_id("aaaa:bbbb:cccc:fff8::", 61, 0); + check_subnet_id("aaaa:bbbb:cccc:ffff::", 61, 7); + check_subnet_id("aaaa:bbbb:cccc:fff0::", 60, 0); + check_subnet_id("aaaa:bbbb:cccc:fff2::", 60, 2); + check_subnet_id("aaaa:bbbb:cccc:ffff::", 60, 15); + check_subnet_id("aaaa:bbbb:cccc:0000::", 48, 0); + check_subnet_id("aaaa:bbbb:cccc:f000::", 48, 61440); + check_subnet_id("aaaa:bbbb:cccc:ffff::", 48, 65535); + check_subnet_id("aaaa:bbbb:cccc:0000::", 46, 0); + check_subnet_id("aaaa:bbbb:cccc:0001::", 46, 1); + check_subnet_id("aaaa:bbbb:ccce:5555::", 46, 152917); + check_subnet_id("aaaa:bbbb:0000:0000::", 32, 0); + check_subnet_id("aaaa:bbbb:0000:0001::", 32, 1); + check_subnet_id("aaaa:bbbb:0001:0001::", 32, 65537); + check_subnet_id("aaaa:bbbb:ffff:ffff::", 32, 0xffffffff); + check_subnet_id("aaaa:bbb8:0000:0000::", 30, 0); + check_subnet_id("aaaa:bbb8:0000:0001::", 30, 1); + check_subnet_id("aaaa:bbb8:0001:0001::", 30, 65537); + check_subnet_id("aaaa:bbbb:0000:0000::", 30, 0x300000000ULL); + check_subnet_id("aaaa:bbbb:0001:0001::", 30, 0x300010001ULL); + check_subnet_id("aaaa:bbbb:0001:0001::", 8, 0xAABBBB00010001ULL); + check_subnet_id("abaa:bbbb:0001:0001::", 7, 0x1AABBBB00010001ULL); + check_subnet_id("abaa:bbbb:0001:0001::", 0, 0xABAABBBB00010001ULL); + check_subnet_id("abaa:bbbb:0001:0001:5555:5555:5555:5555", 0, 0xABAABBBB00010001ULL); + check_subnet_id("::", 0, 0); +} + +/*****************************************************************************/ + static void test_unaligned(void) { @@ -2864,6 +2911,7 @@ main(int argc, char **argv) g_test_add_func("/general/test_nm_ip4_addr_is_loopback", test_nm_ip4_addr_is_loopback); g_test_add_func("/general/test_nm_ip4_addr_netmask_from_prefix", test_nm_ip4_addr_netmask_from_prefix); + g_test_add_func("/general/test_nm_ip6_addr_get_subnet_id", test_nm_ip6_addr_get_subnet_id); g_test_add_func("/general/test_unaligned", test_unaligned); g_test_add_func("/general/test_strv_cmp", test_strv_cmp); g_test_add_func("/general/test_strstrip_avoid_copy", test_strstrip_avoid_copy);