diff options
| author | Michael Biebl <biebl@debian.org> | 2026-07-03 19:53:18 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2026-07-03 19:53:18 +0200 |
| commit | 537bfce2bda471c92caabd388589230200891509 (patch) | |
| tree | aedeccfaf0ba52c238ecf51fc009c0db5d4b60f0 /src/libnm-glib-aux | |
| parent | 869e9027026cdbb15d4e4a6327ff41d2697858eb (diff) | |
New upstream version 1.58~rc1 upstream/1.58_rc1
Diffstat (limited to 'src/libnm-glib-aux')
| -rw-r--r-- | src/libnm-glib-aux/nm-inet-utils.c | 86 | ||||
| -rw-r--r-- | src/libnm-glib-aux/nm-inet-utils.h | 4 | ||||
| -rw-r--r-- | src/libnm-glib-aux/tests/test-shared-general.c | 55 |
3 files changed, 145 insertions, 0 deletions
diff --git a/src/libnm-glib-aux/nm-inet-utils.c b/src/libnm-glib-aux/nm-inet-utils.c index f4f418cf..ead060bc 100644 --- a/src/libnm-glib-aux/nm-inet-utils.c +++ b/src/libnm-glib-aux/nm-inet-utils.c @@ -167,6 +167,92 @@ nm_ip6_addr_is_ula(const struct in6_addr *address) return (address->s6_addr32[0] & htonl(0xfe000000u)) == htonl(0xfc000000u); } +/** + * nm_ip6_addr_common_prefix_len: + * @a: first IPv6 address + * @b: second IPv6 address + * + * Returns: the number of leading bits that @a and @b have in common, + * from 0 to 128. + */ +guint +nm_ip6_addr_common_prefix_len(const struct in6_addr *a, const struct in6_addr *b) +{ + guint i; + + for (i = 0; i < 16; i++) { + guint8 diff = a->s6_addr[i] ^ b->s6_addr[i]; + + if (diff != 0) + return i * 8u + __builtin_clz((guint) diff) - 24u; + } + return 128; +} + +/** + * nm_ip6_addr_rfc6724_label: + * @addr: an IPv6 address + * + * Returns the label for @addr from the default policy table defined + * in RFC 6724, Section 2.1: + * + * Prefix Precedence Label + * ::1/128 50 0 + * ::/0 40 1 + * ::ffff:0:0/96 35 4 + * 2002::/16 30 2 + * 2001::/32 5 5 + * fc00::/7 3 13 + * ::/96 1 3 + * fec0::/10 1 11 + * 3ffe::/16 1 12 + * + * Returns: the label value (0-13). It can be used in the Source + * Address Selection algorithm to prefer a source whose label + * matches with the label of the destination. + */ +guint +nm_ip6_addr_rfc6724_label(const struct in6_addr *addr) +{ + /* Checked from most-specific to least-specific prefix length. */ + + /* ::1/128 (loopback) */ + if (IN6_IS_ADDR_LOOPBACK(addr)) + return 0; + + /* ::ffff:0:0/96 (IPv4-mapped) */ + if (IN6_IS_ADDR_V4MAPPED(addr)) + return 4; + + /* ::/96 (IPv4-compatible, deprecated) */ + if (addr->s6_addr32[0] == 0 && addr->s6_addr32[1] == 0 && addr->s6_addr32[2] == 0 + && !IN6_IS_ADDR_UNSPECIFIED(addr)) + return 3; + + /* 2001::/32 (Teredo) */ + if (addr->s6_addr32[0] == htonl(0x20010000u)) + return 5; + + /* 2002::/16 (6to4) */ + if ((addr->s6_addr32[0] & htonl(0xFFFF0000u)) == htonl(0x20020000u)) + return 2; + + /* 3ffe::/16 (deprecated 6bone) */ + if ((addr->s6_addr32[0] & htonl(0xFFFF0000u)) == htonl(0x3FFE0000u)) + return 12; + + /* fec0::/10 (deprecated site-local) */ + if ((addr->s6_addr32[0] & htonl(0xFFC00000u)) == htonl(0xFEC00000u)) + return 11; + + /* fc00::/7 (ULA) */ + if (nm_ip6_addr_is_ula(addr)) + return 13; + + /* ::/0 (default) */ + return 1; +} + /*****************************************************************************/ gconstpointer diff --git a/src/libnm-glib-aux/nm-inet-utils.h b/src/libnm-glib-aux/nm-inet-utils.h index 999519f8..ab4361e6 100644 --- a/src/libnm-glib-aux/nm-inet-utils.h +++ b/src/libnm-glib-aux/nm-inet-utils.h @@ -280,6 +280,10 @@ nm_ip_addr_same_prefix(int addr_family, gconstpointer addr_a, gconstpointer addr gboolean nm_ip_addr_is_site_local(int addr_family, const void *address); gboolean nm_ip6_addr_is_ula(const struct in6_addr *address); +guint nm_ip6_addr_common_prefix_len(const struct in6_addr *a, const struct in6_addr *b); + +guint nm_ip6_addr_rfc6724_label(const struct in6_addr *addr); + /*****************************************************************************/ #define NM_IPV4LL_NETWORK ((in_addr_t) htonl(0xA9FE0000lu)) /* 169.254.0.0 */ diff --git a/src/libnm-glib-aux/tests/test-shared-general.c b/src/libnm-glib-aux/tests/test-shared-general.c index 931c25c6..95255c7e 100644 --- a/src/libnm-glib-aux/tests/test-shared-general.c +++ b/src/libnm-glib-aux/tests/test-shared-general.c @@ -2380,6 +2380,60 @@ test_inet_utils(void) /*****************************************************************************/ +static void +test_ip6_addr_common_prefix_len(void) +{ + struct in6_addr a; + struct in6_addr b; + + /* identical addresses -> 128 */ + a = nmtst_inet6_from_string("2001:db8::1"); + b = nmtst_inet6_from_string("2001:db8::1"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 128); + + /* completely different -> 0 */ + a = nmtst_inet6_from_string("8000::"); + b = nmtst_inet6_from_string("::"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 0); + + /* first 64 bits in common, differ at bit 65 */ + a = nmtst_inet6_from_string("2001:db8:abcd:1234:8000::"); + b = nmtst_inet6_from_string("2001:db8:abcd:1234::"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 64); + + /* same /48 prefix */ + a = nmtst_inet6_from_string("2001:db8:abcd::"); + b = nmtst_inet6_from_string("2001:db8:abcd:ffff::"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 48); + + /* differ in 5th bit -> 4 common bits */ + a = nmtst_inet6_from_string("f800::"); + b = nmtst_inet6_from_string("f000::"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 4); + + /* differ in 2nd bit -> 1 common bit */ + a = nmtst_inet6_from_string("c000::"); + b = nmtst_inet6_from_string("8000::"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 1); + + /* both zero -> 128 */ + a = nmtst_inet6_from_string("::"); + b = nmtst_inet6_from_string("::"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 128); + + /* first 120 bits in common, differ at MSB of last byte */ + a = nmtst_inet6_from_string("2001:db8::80"); + b = nmtst_inet6_from_string("2001:db8::"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 120); + + /* first 127 bits in common, differ only in last bit */ + a = nmtst_inet6_from_string("2001:db8::fe"); + b = nmtst_inet6_from_string("2001:db8::ff"); + g_assert_cmpuint(nm_ip6_addr_common_prefix_len(&a, &b), ==, 127); +} + +/*****************************************************************************/ + static gboolean _inet_parse(int addr_family, const char *str, gboolean accept_legacy, gpointer out_addr) { @@ -2940,6 +2994,7 @@ main(int argc, char **argv) g_test_add_func("/general/test_path_simplify", test_path_simplify); g_test_add_func("/general/test_hostname_is_valid", test_hostname_is_valid); g_test_add_func("/general/test_inet_utils", test_inet_utils); + g_test_add_func("/general/test_ip6_addr_common_prefix_len", test_ip6_addr_common_prefix_len); g_test_add_func("/general/test_inet_parse_ip4_legacy", test_inet_parse_ip4_legacy); g_test_add_func("/general/test_garray", test_garray); g_test_add_func("/general/test_nm_prioq", test_nm_prioq); |