mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-03-19 17:00:39 +01:00
merge: branch 'bg/clat-prefix-selection'
l3cfg: fix selection of the CLAT IPv6 prefix https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/2378
This commit is contained in:
commit
5580b982ac
4 changed files with 219 additions and 19 deletions
|
|
@ -4140,6 +4140,53 @@ update_routes:
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* _clat_prefix_is_better:
|
||||
* @best: current best candidate (or %NULL)
|
||||
* @candidate: the new candidate prefix
|
||||
* @nat64_pref: the NAT64 prefix
|
||||
*
|
||||
* Compare two SLAAC candidate prefixes to be used for CLAT,
|
||||
* as recommended by draft-ietf-v6ops-claton Section 7. Apply
|
||||
* rules 6 and 8 of the source address selection algorithm from
|
||||
* RFC 6724, Section 5.
|
||||
*
|
||||
* Returns %TRUE if @candidate is better than @best.
|
||||
*/
|
||||
static gboolean
|
||||
_clat_prefix_is_better(const NMPlatformIP6Address *best,
|
||||
const NMPlatformIP6Address *candidate,
|
||||
const struct in6_addr *nat64_pref)
|
||||
{
|
||||
guint nat64_pref_label;
|
||||
gboolean best_label_match;
|
||||
gboolean cand_label_match;
|
||||
guint best_prefix_len;
|
||||
guint cand_prefix_len;
|
||||
|
||||
if (!best)
|
||||
return TRUE;
|
||||
|
||||
/* Rule 6: prefer the address whose RFC 6724 label matches
|
||||
* the label of the NAT64 prefix. */
|
||||
nat64_pref_label = nm_ip6_addr_rfc6724_label(nat64_pref);
|
||||
best_label_match = nm_ip6_addr_rfc6724_label(&best->address) == nat64_pref_label;
|
||||
cand_label_match = nm_ip6_addr_rfc6724_label(&candidate->address) == nat64_pref_label;
|
||||
|
||||
if (cand_label_match && !best_label_match)
|
||||
return TRUE;
|
||||
else if (best_label_match && !cand_label_match)
|
||||
return FALSE;
|
||||
|
||||
/* Rule 8: longest matching prefix with the NAT64 prefix. */
|
||||
best_prefix_len = nm_ip6_addr_common_prefix_len(&best->address, nat64_pref);
|
||||
cand_prefix_len = nm_ip6_addr_common_prefix_len(&candidate->address, nat64_pref);
|
||||
if (cand_prefix_len != best_prefix_len)
|
||||
return cand_prefix_len > best_prefix_len;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
_l3cfg_update_clat_config(NML3Cfg *self,
|
||||
NML3ConfigData *l3cd,
|
||||
|
|
@ -4206,29 +4253,37 @@ _l3cfg_update_clat_config(NML3Cfg *self,
|
|||
network_id = nm_l3_config_data_get_network_id(l3cd);
|
||||
|
||||
if (!self->priv.p->clat_address_6_valid && network_id) {
|
||||
const NMPlatformIP6Address *best_prefix = NULL;
|
||||
|
||||
/* Select the best SLAAC prefix for the CLAT address per
|
||||
* draft-ietf-v6ops-claton-14 Section 7 */
|
||||
nm_l3_config_data_iter_ip6_address_for_each (&iter, l3cd, &ip6_entry) {
|
||||
if (ip6_entry->addr_source == NM_IP_CONFIG_SOURCE_NDISC && ip6_entry->plen == 64) {
|
||||
ip6 = ip6_entry->address;
|
||||
|
||||
nm_utils_ipv6_addr_set_stable_privacy(NM_UTILS_STABLE_TYPE_CLAT,
|
||||
&ip6,
|
||||
nm_l3cfg_get_ifname(self, TRUE),
|
||||
network_id,
|
||||
0);
|
||||
self->priv.p->clat_address_6 = (NMPlatformIP6Address) {
|
||||
.ifindex = self->priv.ifindex,
|
||||
.address = ip6,
|
||||
.peer_address = ip6,
|
||||
.addr_source = NM_IP_CONFIG_SOURCE_CLAT,
|
||||
.plen = ip6_entry->plen,
|
||||
};
|
||||
|
||||
_LOGT("clat: using IPv6 address %s", nm_inet6_ntop(&ip6, buf));
|
||||
|
||||
self->priv.p->clat_address_6_valid = TRUE;
|
||||
break;
|
||||
if (_clat_prefix_is_better(best_prefix, ip6_entry, &pref64))
|
||||
best_prefix = ip6_entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (best_prefix) {
|
||||
ip6 = best_prefix->address;
|
||||
|
||||
nm_utils_ipv6_addr_set_stable_privacy(NM_UTILS_STABLE_TYPE_CLAT,
|
||||
&ip6,
|
||||
nm_l3cfg_get_ifname(self, TRUE),
|
||||
network_id,
|
||||
0);
|
||||
self->priv.p->clat_address_6 = (NMPlatformIP6Address) {
|
||||
.ifindex = self->priv.ifindex,
|
||||
.address = ip6,
|
||||
.peer_address = ip6,
|
||||
.addr_source = NM_IP_CONFIG_SOURCE_CLAT,
|
||||
.plen = best_prefix->plen,
|
||||
};
|
||||
|
||||
_LOGT("clat: using IPv6 address %s", nm_inet6_ntop(&ip6, buf));
|
||||
|
||||
self->priv.p->clat_address_6_valid = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't get a v4 address if we have no v6 address (otherwise, we could
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue