summary refs log tree commit diff
path: root/src/libnm-glib-aux
diff options
context:
space:
mode:
authorMichael Biebl <biebl@debian.org>2026-07-03 19:53:18 +0200
committerMichael Biebl <biebl@debian.org>2026-07-03 19:53:18 +0200
commit537bfce2bda471c92caabd388589230200891509 (patch)
treeaedeccfaf0ba52c238ecf51fc009c0db5d4b60f0 /src/libnm-glib-aux
parent869e9027026cdbb15d4e4a6327ff41d2697858eb (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.c86
-rw-r--r--src/libnm-glib-aux/nm-inet-utils.h4
-rw-r--r--src/libnm-glib-aux/tests/test-shared-general.c55
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);