mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-03-01 15:01:48 +01:00
ndisc: track multiple PREF64 options
Previously the NMNDisc instance always used the last received NAT64 prefix. If a network advertises multiple NAT64 prefixes, NetworkManager would constantly flip between them. Change this and keep a list of valid PREF64. Most importantly, stick with the same PREF64 unless a new one appears from a router with higher priority, or the current PREF64 expires.
This commit is contained in:
parent
2499e896ee
commit
7c7528edaf
4 changed files with 123 additions and 30 deletions
|
|
@ -162,7 +162,6 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
|
|||
int offset;
|
||||
int hop_limit;
|
||||
guint32 val;
|
||||
gboolean pref64_found = FALSE;
|
||||
|
||||
/* Router discovery is subject to the following RFC documents:
|
||||
*
|
||||
|
|
@ -404,25 +403,22 @@ receive_ra(struct ndp *ndp, struct ndp_msg *msg, gpointer user_data)
|
|||
|
||||
/* PREF64 */
|
||||
ndp_msg_opt_for_each_offset (offset, msg, NDP_MSG_OPT_PREF64) {
|
||||
struct in6_addr pref64_prefix = *ndp_msg_opt_pref64_prefix(msg, offset);
|
||||
guint8 pref64_length = ndp_msg_opt_pref64_prefix_length(msg, offset);
|
||||
gint64 expiry_msec =
|
||||
_nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_pref64_lifetime(msg, offset));
|
||||
NMNDiscPref64 pref64;
|
||||
|
||||
pref64 = (NMNDiscPref64) {
|
||||
.prefix = *ndp_msg_opt_pref64_prefix(msg, offset),
|
||||
.plen = ndp_msg_opt_pref64_prefix_length(msg, offset),
|
||||
.gateway = gateway.address,
|
||||
.gateway_preference = gateway.preference,
|
||||
.expiry_msec =
|
||||
_nm_ndisc_lifetime_to_expiry(now_msec, ndp_msg_opt_pref64_lifetime(msg, offset)),
|
||||
.gateway_expiry_msec = gateway.expiry_msec,
|
||||
};
|
||||
|
||||
/* libndp should only return lengths defined in RFC 8781 */
|
||||
nm_assert(NM_IN_SET(pref64_length, 96, 64, 56, 48, 40, 32));
|
||||
nm_assert(NM_IN_SET(pref64.plen, 96, 64, 56, 48, 40, 32));
|
||||
|
||||
/* Newer RA has more up to date information, prefer it: */
|
||||
if (!pref64_found) {
|
||||
pref64_found = TRUE;
|
||||
rdata->public.pref64.expiry_msec = 0;
|
||||
}
|
||||
|
||||
if (expiry_msec >= rdata->public.pref64.expiry_msec) {
|
||||
rdata->public.pref64.network = pref64_prefix;
|
||||
rdata->public.pref64.expiry_msec = expiry_msec;
|
||||
rdata->public.pref64.plen = pref64_length;
|
||||
rdata->public.pref64.valid = TRUE;
|
||||
if (nm_ndisc_add_pref64(ndisc, &pref64, now_msec)) {
|
||||
changed |= NM_NDISC_CONFIG_PREF64;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct _NMNDiscDataInternal {
|
|||
NMNDiscData public;
|
||||
GArray *gateways;
|
||||
GArray *addresses;
|
||||
GArray *clat_addresses;
|
||||
GArray *pref64;
|
||||
GArray *routes;
|
||||
GArray *dns_servers;
|
||||
GArray *dns_domains;
|
||||
|
|
@ -29,6 +29,7 @@ gboolean nm_ndisc_add_gateway(NMNDisc *ndisc, const NMNDiscGateway *new_item, gi
|
|||
gboolean
|
||||
nm_ndisc_complete_and_add_address(NMNDisc *ndisc, const NMNDiscAddress *new_item, gint64 now_msec);
|
||||
gboolean nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec);
|
||||
gboolean nm_ndisc_add_pref64(NMNDisc *ndisc, const NMNDiscPref64 *new_item, gint64 now_msec);
|
||||
gboolean nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 now_msec);
|
||||
gboolean nm_ndisc_add_dns_domain(NMNDisc *ndisc, const NMNDiscDNSDomain *new_item, gint64 now_msec);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#define _SIZE_MAX_ROUTES 1000u
|
||||
#define _SIZE_MAX_DNS_SERVERS 64u
|
||||
#define _SIZE_MAX_DNS_DOMAINS 64u
|
||||
#define _SIZE_MAX_PREF64 8u
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
|
@ -212,8 +213,8 @@ nm_ndisc_data_to_l3cd(NMDedupMultiIndex *multi_idx,
|
|||
for (i = 0; i < rdata->dns_domains_n; i++)
|
||||
nm_l3_config_data_add_search(l3cd, AF_INET6, rdata->dns_domains[i].domain);
|
||||
|
||||
if (rdata->pref64.valid) {
|
||||
nm_l3_config_data_set_pref64(l3cd, rdata->pref64.network, rdata->pref64.plen);
|
||||
if (rdata->pref64_n > 0) {
|
||||
nm_l3_config_data_set_pref64(l3cd, rdata->pref64[0].prefix, rdata->pref64[0].plen);
|
||||
} else {
|
||||
nm_l3_config_data_set_pref64_valid(l3cd, FALSE);
|
||||
}
|
||||
|
|
@ -425,6 +426,7 @@ _data_complete(NMNDiscDataInternal *data)
|
|||
_SET(data, gateways);
|
||||
_SET(data, addresses);
|
||||
_SET(data, routes);
|
||||
_SET(data, pref64);
|
||||
_SET(data, dns_servers);
|
||||
_SET(data, dns_domains);
|
||||
#undef _SET
|
||||
|
|
@ -770,6 +772,59 @@ nm_ndisc_add_route(NMNDisc *ndisc, const NMNDiscRoute *new_item, gint64 now_msec
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_ndisc_add_pref64(NMNDisc *ndisc, const NMNDiscPref64 *new_item, gint64 now_msec)
|
||||
{
|
||||
NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
|
||||
guint i;
|
||||
guint insert_idx = G_MAXUINT;
|
||||
|
||||
for (i = 0; i < rdata->pref64->len;) {
|
||||
NMNDiscPref64 *item = &nm_g_array_index(rdata->pref64, NMNDiscPref64, i);
|
||||
|
||||
if (item->plen == new_item->plen && IN6_ARE_ADDR_EQUAL(&item->prefix, &new_item->prefix)
|
||||
&& IN6_ARE_ADDR_EQUAL(&item->gateway, &new_item->gateway)) {
|
||||
if (new_item->expiry_msec <= now_msec) {
|
||||
g_array_remove_index(rdata->pref64, i);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (item->gateway_preference != new_item->gateway_preference) {
|
||||
g_array_remove_index(rdata->pref64, i);
|
||||
continue;
|
||||
}
|
||||
|
||||
item->gateway_expiry_msec = new_item->gateway_expiry_msec;
|
||||
|
||||
if (item->expiry_msec == new_item->expiry_msec)
|
||||
return FALSE;
|
||||
|
||||
item->expiry_msec = new_item->expiry_msec;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Put before less preferable gateways. */
|
||||
if (_preference_to_priority(item->gateway_preference)
|
||||
< _preference_to_priority(new_item->gateway_preference)
|
||||
&& insert_idx == G_MAXUINT)
|
||||
insert_idx = i;
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
if (rdata->pref64->len >= _SIZE_MAX_PREF64)
|
||||
return FALSE;
|
||||
|
||||
if (new_item->expiry_msec <= now_msec)
|
||||
return FALSE;
|
||||
|
||||
g_array_insert_val(rdata->pref64,
|
||||
insert_idx == G_MAXUINT ? rdata->pref64->len : insert_idx,
|
||||
*new_item);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
nm_ndisc_add_dns_server(NMNDisc *ndisc, const NMNDiscDNSServer *new_item, gint64 now_msec)
|
||||
{
|
||||
|
|
@ -1410,6 +1465,17 @@ _config_changed_log(NMNDisc *ndisc, NMNDiscConfigMap changed)
|
|||
nm_icmpv6_router_pref_to_string(route->preference, str_pref, sizeof(str_pref)),
|
||||
get_exp(str_exp, now_msec, route));
|
||||
}
|
||||
for (i = 0; i < rdata->pref64->len; i++) {
|
||||
const NMNDiscPref64 *pref64 = &nm_g_array_index(rdata->pref64, NMNDiscPref64, i);
|
||||
char addrstr2[NM_INET_ADDRSTRLEN];
|
||||
|
||||
_LOGD(" pref64 %s/%u via %s exp %s",
|
||||
nm_inet6_ntop(&pref64->prefix, addrstr),
|
||||
pref64->plen,
|
||||
nm_inet6_ntop(&pref64->gateway, addrstr2),
|
||||
get_exp(str_exp, now_msec, pref64));
|
||||
}
|
||||
|
||||
for (i = 0; i < rdata->dns_servers->len; i++) {
|
||||
const NMNDiscDNSServer *dns_server =
|
||||
&nm_g_array_index(rdata->dns_servers, NMNDiscDNSServer, i);
|
||||
|
|
@ -1538,13 +1604,39 @@ static void
|
|||
clean_pref64(NMNDisc *ndisc, gint64 now_msec, NMNDiscConfigMap *changed, gint64 *next_msec)
|
||||
{
|
||||
NMNDiscDataInternal *rdata = &NM_NDISC_GET_PRIVATE(ndisc)->rdata;
|
||||
NMNDiscPref64 *arr;
|
||||
guint i;
|
||||
guint j;
|
||||
|
||||
if (!rdata->public.pref64.valid)
|
||||
if (rdata->pref64->len == 0)
|
||||
return;
|
||||
if (!expiry_next(now_msec, rdata->public.pref64.expiry_msec, next_msec)) {
|
||||
rdata->public.pref64.valid = FALSE;
|
||||
*changed |= NM_NDISC_CONFIG_PREF64;
|
||||
|
||||
arr = &nm_g_array_first(rdata->pref64, NMNDiscPref64);
|
||||
|
||||
for (i = 0, j = 0; i < rdata->pref64->len; i++) {
|
||||
if (!expiry_next(now_msec, arr[i].expiry_msec, next_msec)
|
||||
|| !expiry_next(now_msec,
|
||||
arr[i].gateway_expiry_msec,
|
||||
next_msec)) { /* no gateway no party */
|
||||
if (i == 0) {
|
||||
/* Emit the changed signal only when the first PREF64 expires,
|
||||
* because only the first item is exported into the l3cd. Changes
|
||||
* in other PREF64s are not relevant. */
|
||||
*changed |= NM_NDISC_CONFIG_PREF64;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i != j)
|
||||
arr[j] = arr[i];
|
||||
j++;
|
||||
}
|
||||
|
||||
if (i != j) {
|
||||
g_array_set_size(rdata->pref64, j);
|
||||
}
|
||||
|
||||
_array_set_size_max(rdata->pref64, _SIZE_MAX_PREF64);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -1943,6 +2035,7 @@ nm_ndisc_init(NMNDisc *ndisc)
|
|||
rdata->gateways = g_array_new(FALSE, FALSE, sizeof(NMNDiscGateway));
|
||||
rdata->addresses = g_array_new(FALSE, FALSE, sizeof(NMNDiscAddress));
|
||||
rdata->routes = g_array_new(FALSE, FALSE, sizeof(NMNDiscRoute));
|
||||
rdata->pref64 = g_array_new(FALSE, FALSE, sizeof(NMNDiscPref64));
|
||||
rdata->dns_servers = g_array_new(FALSE, FALSE, sizeof(NMNDiscDNSServer));
|
||||
rdata->dns_domains = g_array_new(FALSE, FALSE, sizeof(NMNDiscDNSDomain));
|
||||
g_array_set_clear_func(rdata->dns_domains, dns_domain_free);
|
||||
|
|
@ -1975,6 +2068,7 @@ finalize(GObject *object)
|
|||
g_array_unref(rdata->gateways);
|
||||
g_array_unref(rdata->addresses);
|
||||
g_array_unref(rdata->routes);
|
||||
g_array_unref(rdata->pref64);
|
||||
g_array_unref(rdata->dns_servers);
|
||||
g_array_unref(rdata->dns_domains);
|
||||
|
||||
|
|
|
|||
|
|
@ -120,10 +120,12 @@ typedef struct _NMNDiscRoute {
|
|||
} NMNDiscRoute;
|
||||
|
||||
typedef struct _NMNDiscPref64 {
|
||||
struct in6_addr network;
|
||||
gint64 expiry_msec;
|
||||
guint8 plen;
|
||||
bool valid : 1;
|
||||
struct in6_addr prefix;
|
||||
struct in6_addr gateway;
|
||||
gint64 expiry_msec;
|
||||
gint64 gateway_expiry_msec;
|
||||
NMIcmpv6RouterPref gateway_preference;
|
||||
guint8 plen;
|
||||
} NMNDiscPref64;
|
||||
|
||||
typedef struct {
|
||||
|
|
@ -196,16 +198,16 @@ typedef struct {
|
|||
guint gateways_n;
|
||||
guint addresses_n;
|
||||
guint routes_n;
|
||||
guint pref64_n;
|
||||
guint dns_servers_n;
|
||||
guint dns_domains_n;
|
||||
|
||||
const NMNDiscGateway *gateways;
|
||||
const NMNDiscAddress *addresses;
|
||||
const NMNDiscRoute *routes;
|
||||
const NMNDiscPref64 *pref64;
|
||||
const NMNDiscDNSServer *dns_servers;
|
||||
const NMNDiscDNSDomain *dns_domains;
|
||||
|
||||
NMNDiscPref64 pref64;
|
||||
} NMNDiscData;
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue